@babylonjs-toolkit/next 9.34.10 → 9.35.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 (109) hide show
  1. package/lib/index.d.ts +80 -14
  2. package/lib/index.d.ts.map +1 -1
  3. package/lib/index.js +80 -14
  4. package/lib/leaderboardaroundpage.d.ts +5 -0
  5. package/lib/leaderboardaroundpage.d.ts.map +1 -0
  6. package/lib/leaderboardaroundpage.js +1 -0
  7. package/lib/leaderboardcallresult.d.ts +13 -0
  8. package/lib/leaderboardcallresult.d.ts.map +1 -0
  9. package/lib/leaderboardcallresult.js +1 -0
  10. package/lib/leaderboardclient.d.ts +95 -0
  11. package/lib/leaderboardclient.d.ts.map +1 -0
  12. package/lib/leaderboardclient.js +448 -0
  13. package/lib/leaderboardclientoptions.d.ts +12 -0
  14. package/lib/leaderboardclientoptions.d.ts.map +1 -0
  15. package/lib/leaderboardclientoptions.js +1 -0
  16. package/lib/leaderboardentriespage.d.ts +10 -0
  17. package/lib/leaderboardentriespage.d.ts.map +1 -0
  18. package/lib/leaderboardentriespage.js +1 -0
  19. package/lib/leaderboardentryview.d.ts +14 -0
  20. package/lib/leaderboardentryview.d.ts.map +1 -0
  21. package/lib/leaderboardentryview.js +1 -0
  22. package/lib/leaderboarderrorbody.d.ts +6 -0
  23. package/lib/leaderboarderrorbody.d.ts.map +1 -0
  24. package/lib/leaderboarderrorbody.js +1 -0
  25. package/lib/leaderboardfetch.d.ts +2 -0
  26. package/lib/leaderboardfetch.d.ts.map +1 -0
  27. package/lib/leaderboardfetch.js +1 -0
  28. package/lib/leaderboardkeeppolicy.d.ts +2 -0
  29. package/lib/leaderboardkeeppolicy.d.ts.map +1 -0
  30. package/lib/leaderboardkeeppolicy.js +1 -0
  31. package/lib/leaderboardmetadata.d.ts +5 -0
  32. package/lib/leaderboardmetadata.d.ts.map +1 -0
  33. package/lib/leaderboardmetadata.js +1 -0
  34. package/lib/leaderboardmetadatavalue.d.ts +4 -0
  35. package/lib/leaderboardmetadatavalue.d.ts.map +1 -0
  36. package/lib/leaderboardmetadatavalue.js +1 -0
  37. package/lib/leaderboardplayersession.d.ts +6 -0
  38. package/lib/leaderboardplayersession.d.ts.map +1 -0
  39. package/lib/leaderboardplayersession.js +1 -0
  40. package/lib/leaderboardplayerview.d.ts +7 -0
  41. package/lib/leaderboardplayerview.d.ts.map +1 -0
  42. package/lib/leaderboardplayerview.js +1 -0
  43. package/lib/leaderboardpostattempt.d.ts +6 -0
  44. package/lib/leaderboardpostattempt.d.ts.map +1 -0
  45. package/lib/leaderboardpostattempt.js +1 -0
  46. package/lib/leaderboardpublicboardview.d.ts +17 -0
  47. package/lib/leaderboardpublicboardview.d.ts.map +1 -0
  48. package/lib/leaderboardpublicboardview.js +1 -0
  49. package/lib/leaderboardrangequery.d.ts +7 -0
  50. package/lib/leaderboardrangequery.d.ts.map +1 -0
  51. package/lib/leaderboardrangequery.js +1 -0
  52. package/lib/leaderboardrefusalreason.d.ts +2 -0
  53. package/lib/leaderboardrefusalreason.d.ts.map +1 -0
  54. package/lib/leaderboardrefusalreason.js +1 -0
  55. package/lib/leaderboardscoreformat.d.ts +2 -0
  56. package/lib/leaderboardscoreformat.d.ts.map +1 -0
  57. package/lib/leaderboardscoreformat.js +1 -0
  58. package/lib/leaderboardscoreresult.d.ts +18 -0
  59. package/lib/leaderboardscoreresult.d.ts.map +1 -0
  60. package/lib/leaderboardscoreresult.js +1 -0
  61. package/lib/leaderboardscoresubmission.d.ts +8 -0
  62. package/lib/leaderboardscoresubmission.d.ts.map +1 -0
  63. package/lib/leaderboardscoresubmission.js +1 -0
  64. package/lib/leaderboardseasonview.d.ts +7 -0
  65. package/lib/leaderboardseasonview.d.ts.map +1 -0
  66. package/lib/leaderboardseasonview.js +1 -0
  67. package/lib/leaderboardsortorder.d.ts +2 -0
  68. package/lib/leaderboardsortorder.d.ts.map +1 -0
  69. package/lib/leaderboardsortorder.js +1 -0
  70. package/lib/leaderboardstorage.d.ts +6 -0
  71. package/lib/leaderboardstorage.d.ts.map +1 -0
  72. package/lib/leaderboardstorage.js +1 -0
  73. package/lib/leaderboardstoredplayer.d.ts +5 -0
  74. package/lib/leaderboardstoredplayer.d.ts.map +1 -0
  75. package/lib/leaderboardstoredplayer.js +1 -0
  76. package/lib/leaderboardsubmission.d.ts +8 -0
  77. package/lib/leaderboardsubmission.d.ts.map +1 -0
  78. package/lib/leaderboardsubmission.js +1 -0
  79. package/lib/leaderboardsubmissionrequest.d.ts +12 -0
  80. package/lib/leaderboardsubmissionrequest.d.ts.map +1 -0
  81. package/lib/leaderboardsubmissionrequest.js +1 -0
  82. package/lib/leaderboardsubmissionresponse.d.ts +7 -0
  83. package/lib/leaderboardsubmissionresponse.d.ts.map +1 -0
  84. package/lib/leaderboardsubmissionresponse.js +1 -0
  85. package/lib/leaderboardsubmitresult.d.ts +15 -0
  86. package/lib/leaderboardsubmitresult.d.ts.map +1 -0
  87. package/lib/leaderboardsubmitresult.js +1 -0
  88. package/lib/leaderboardtarget.d.ts +6 -0
  89. package/lib/leaderboardtarget.d.ts.map +1 -0
  90. package/lib/leaderboardtarget.js +1 -0
  91. package/lib/leaderboardtargetentry.d.ts +9 -0
  92. package/lib/leaderboardtargetentry.d.ts.map +1 -0
  93. package/lib/leaderboardtargetentry.js +1 -0
  94. package/lib/leaderboardtrustlevel.d.ts +2 -0
  95. package/lib/leaderboardtrustlevel.d.ts.map +1 -0
  96. package/lib/leaderboardtrustlevel.js +1 -0
  97. package/lib/leaderboardvariantdimension.d.ts +6 -0
  98. package/lib/leaderboardvariantdimension.d.ts.map +1 -0
  99. package/lib/leaderboardvariantdimension.js +1 -0
  100. package/lib/leaderboardwriteoutcome.d.ts +2 -0
  101. package/lib/leaderboardwriteoutcome.d.ts.map +1 -0
  102. package/lib/leaderboardwriteoutcome.js +1 -0
  103. package/lib/project.d.ts +16 -732
  104. package/lib/project.d.ts.map +1 -1
  105. package/lib/project.js +76 -3226
  106. package/lib/scenemanager.d.ts +424 -6
  107. package/lib/scenemanager.d.ts.map +1 -1
  108. package/lib/scenemanager.js +2633 -258
  109. package/package.json +17 -13
@@ -143,7 +143,7 @@ import { UnityScrollBar } from './unityscrollbar';
143
143
  import { UnityDropdownMenu } from './unitydropdownmenu';
144
144
  import { RectAreaLight } from '@babylonjs/core/Lights/rectAreaLight';
145
145
  export class SceneManager {
146
- static get Version() { return "9.34.10 - R1"; }
146
+ static get Version() { return "9.35.0 - R1"; }
147
147
  static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
148
148
  static IsPrecompilingShaders() {
149
149
  return SceneManager.PrecompileShaders !== false;
@@ -9198,7 +9198,6 @@ export class CVTOOLS_unity_metadata {
9198
9198
  return;
9199
9199
  const levels = [];
9200
9200
  const rendererCounts = [];
9201
- let multiRenderer = false;
9202
9201
  let totalMeshes = 0;
9203
9202
  for (let index = 0; index < levelCount; index++) {
9204
9203
  const entry = lodentries[index];
@@ -9212,8 +9211,6 @@ export class CVTOOLS_unity_metadata {
9212
9211
  meshes.push(lodnode);
9213
9212
  }
9214
9213
  });
9215
- if (meshes.length > 1)
9216
- multiRenderer = true;
9217
9214
  totalMeshes += meshes.length;
9218
9215
  rendererCounts.push(meshes.length);
9219
9216
  levels.push({ distance: thresholds[index], meshes: meshes });
@@ -9221,14 +9218,15 @@ export class CVTOOLS_unity_metadata {
9221
9218
  if (totalMeshes === 0)
9222
9219
  return;
9223
9220
  const fadeMode = UnityLodGroups.ParseFadeMode(metadata.fademode);
9224
- const nativeCandidate = (useDistances === false && multiRenderer === false && fadeMode === UnityLodGroups.FADE_NONE
9221
+ const requiresSwitcher = UnityLodGroups.RequiresSwitcher(levels.map((level) => level.meshes), fadeMode);
9222
+ const nativeCandidate = (useDistances === false && requiresSwitcher === false
9225
9223
  && levels[0].meshes.length === 1 && levels[0].meshes[0] instanceof Mesh && !(levels[0].meshes[0] instanceof InstancedMesh));
9226
9224
  if (nativeCandidate === true && this._setupNativeCoverageLevelOfDetail(scene, transform, levels, lodBias, metadata) === true) {
9227
9225
  }
9228
9226
  else if (useDistances === false) {
9229
9227
  this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_COVERAGE, lodBias, metadata);
9230
9228
  }
9231
- else if (multiRenderer === true || fadeMode !== UnityLodGroups.FADE_NONE) {
9229
+ else if (requiresSwitcher === true) {
9232
9230
  this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_DISTANCE, lodBias, metadata);
9233
9231
  }
9234
9232
  else {
@@ -9735,10 +9733,14 @@ export class CVTOOLS_unity_metadata {
9735
9733
  return { mesh: skyboxMesh, texture: skyboxTexture };
9736
9734
  };
9737
9735
  const pbSkyClass = HdrpPhysicallyBasedSky;
9738
- const hdrpSkyType = (HdrpRendering.IsParityExport(metadata) === true && metadata.hdrp.sky != null && typeof metadata.hdrp.sky.type === "string") ? metadata.hdrp.sky.type.toLowerCase() : null;
9736
+ const physicalSky = PhysicalSkyBlock.SkyOf(metadata);
9737
+ const hdrpSkyType = (physicalSky != null && typeof physicalSky.type === "string") ? physicalSky.type.toLowerCase() : null;
9739
9738
  const liveSky = (pbSkyClass != null && hdrpSkyType === "physicallybased")
9740
9739
  ? pbSkyClass.TryCreate(babylonScene, metadata, root, { rendergroup: rendergroup, bakedSky: hasBakedSkyFaces ? (() => buildBakedSky().mesh) : null })
9741
9740
  : null;
9741
+ const volumetricClouds = VolumetricClouds;
9742
+ if (volumetricClouds != null)
9743
+ volumetricClouds.TryCreate(babylonScene, metadata, root);
9742
9744
  const graphSky = (liveSky == null && hdrpSkyType !== "none" && typeof skybox.customMaterial === "string" && skybox.bags != null && ShaderGraphSky != null)
9743
9745
  ? ShaderGraphSky.CreateGraphSkybox(babylonScene, skybox.bags, { size: skysize, rendergroup: rendergroup, skyfog: skyfog, tags: skytags, environment: (skybox.environment == null && babylonScene.environmentTexture == null) })
9744
9746
  : null;
@@ -9845,8 +9847,10 @@ export class CVTOOLS_unity_metadata {
9845
9847
  const hasDiffuseIbl = Array.isArray(environment.sh) && environment.sh.length === 27;
9846
9848
  const useUnitySH = (mode === 0 && hasDiffuseIbl === true);
9847
9849
  const environmentLevel = (useUnitySH ? (specIbl * scale * SPECULAR_SCALE) : specIbl * SPECULAR_SCALE) * HdrpRendering.Indirect(babylonScene).reflection;
9850
+ const hasEnvironmentFile = (envinfo != null && envinfo !== "");
9851
+ const ambientScale = LightingConversions.EnvironmentAmbientScale(diffuseIbl, hasEnvironmentFile ? environmentLevel : null, units, HdrpRendering.IsParity(babylonScene));
9848
9852
  if (useUnitySH)
9849
- this.generateSphericalHarmonics(babylonScene, environment.sh, diffuseIbl, (envinfo != null && envinfo !== "") ? environmentLevel : null);
9853
+ this.generateSphericalHarmonics(babylonScene, environment.sh, ambientScale, hasEnvironmentFile ? environmentLevel : null);
9850
9854
  if (envinfo != null && envinfo !== "") {
9851
9855
  try {
9852
9856
  const root = (this._rootUrl != null) ? this._rootUrl : "/";
@@ -9954,6 +9958,8 @@ export class CVTOOLS_unity_metadata {
9954
9958
  }
9955
9959
  if (metadata.shaderglobals != null)
9956
9960
  ShaderGlobals.LoadSceneGlobals(babylonScene, metadata.shaderglobals);
9961
+ ShaderGlobals.LoadSceneDrivers(babylonScene, metadata.shaderglobaldrivers);
9962
+ ShaderGraphRuntime.RecordExportReport(babylonScene, metadata.exportreport);
9957
9963
  if (metadata.lights2d != null && Light2DTexture != null)
9958
9964
  Light2DTexture.Load(babylonScene, metadata.lights2d);
9959
9965
  if (metadata.renderfeatures != null && ShaderGraphPass != null)
@@ -15431,6 +15437,8 @@ export class LightingConversions {
15431
15437
  static UnityLinearLightColor(light) {
15432
15438
  if (light == null || light.diffuse == null)
15433
15439
  return new Color3(0.0, 0.0, 0.0);
15440
+ if (light.metadata != null && light.metadata.toolkit != null && light.metadata.toolkit.linearDiffuse === true)
15441
+ return light.diffuse.clone();
15434
15442
  const rider = (light.metadata != null && light.metadata.toolkit != null) ? light.metadata.toolkit.colorTemperature : null;
15435
15443
  const d = light.diffuse;
15436
15444
  const channel = (value, factor) => {
@@ -15443,6 +15451,18 @@ export class LightingConversions {
15443
15451
  const tb = (rider != null) ? rider.b : 1.0;
15444
15452
  return new Color3(channel(d.r, tr), channel(d.g, tg), channel(d.b, tb));
15445
15453
  }
15454
+ static LinearizeLightColor(light, scene) {
15455
+ const sceneMetadata = (scene != null && scene.metadata != null) ? scene.metadata.toolkit : null;
15456
+ const toolkit = (light != null && light.metadata != null) ? light.metadata.toolkit : null;
15457
+ if (sceneMetadata == null || toolkit == null || toolkit.linearDiffuse === true)
15458
+ return false;
15459
+ const isLinearProject = sceneMetadata.colorspace != null && ("" + sceneMetadata.colorspace).toLowerCase() === "linear";
15460
+ if (!isLinearProject || HdrpRendering.IsParityExport(sceneMetadata) === true)
15461
+ return false;
15462
+ light.diffuse = LightingConversions.UnityLinearLightColor(light);
15463
+ toolkit.linearDiffuse = true;
15464
+ return true;
15465
+ }
15446
15466
  static SetShadowFade(scene, fade) {
15447
15467
  if (scene == null || !Array.isArray(fade) || fade.length < 2)
15448
15468
  return;
@@ -15898,6 +15918,10 @@ export class LightingConversions {
15898
15918
  return Math.max(0.0, reflection);
15899
15919
  return Math.min(levelMax, Math.max(0.0, reflection));
15900
15920
  }
15921
+ static EnvironmentAmbientScale(ambientIntensity, environmentLevel, units, isParity) {
15922
+ const isNormalised = units === LightingConversions.PhysicalUnits && environmentLevel != null && environmentLevel > 0;
15923
+ return (isNormalised && isParity !== true) ? ambientIntensity / environmentLevel : ambientIntensity;
15924
+ }
15901
15925
  static WarnBakedSkyWithoutEnvironment(skybox) {
15902
15926
  if (skybox == null || skybox.bakedsky !== true || skybox.environment != null)
15903
15927
  return false;
@@ -17610,6 +17634,7 @@ export class MetadataParser {
17610
17634
  babylonLight.metadata.toolkit.hdrpSky = component.hdrpsky;
17611
17635
  babylonLight.metadata.toolkit.intensityFactor = intensityFactor;
17612
17636
  babylonLight.metadata.toolkit.colorTemperature = (component.colortemperature != null) ? Utilities.ParseColor3(component.colortemperature, Color3.White()) : null;
17637
+ LightingConversions.LinearizeLightColor(babylonLight, scene);
17613
17638
  if (component.cookie != null)
17614
17639
  UnityLightCookies.Apply(scene, babylonLight, entity, component.cookie);
17615
17640
  babylonLight.shadowEnabled = false;
@@ -18098,6 +18123,10 @@ export class OpaqueSceneColor {
18098
18123
  }
18099
18124
  return all;
18100
18125
  }
18126
+ static CopyConversionFor(imageProcessing) {
18127
+ const isEncodedByMaterials = imageProcessing != null && imageProcessing.isEnabled === true && imageProcessing.applyByPostProcess !== true;
18128
+ return isEncodedByMaterials ? OpaqueSceneColor.COPY_TO_LINEAR : OpaqueSceneColor.COPY_PLAIN;
18129
+ }
18101
18130
  static QueueSamples(subMeshes, materials) {
18102
18131
  if (subMeshes == null || materials == null || materials.size === 0)
18103
18132
  return false;
@@ -18168,7 +18197,7 @@ export class OpaqueSceneColor {
18168
18197
  if (!copier.isReady())
18169
18198
  return;
18170
18199
  engine.unBindFramebuffer(current, true);
18171
- copier.copy(current.texture, destination);
18200
+ copier.copy(current.texture, destination, OpaqueSceneColor.CopyConversionFor(scene.imageProcessingConfiguration));
18172
18201
  if (target.__tkMips === true && target.getInternalTexture() != null && typeof engine.generateMipmaps === "function")
18173
18202
  engine.generateMipmaps(target.getInternalTexture());
18174
18203
  engine.bindFramebuffer(current, undefined, undefined, undefined, true);
@@ -18179,6 +18208,27 @@ export class OpaqueSceneColor {
18179
18208
  OpaqueSceneColor.Ratio = null;
18180
18209
  OpaqueSceneColor.StoreKey = "__tkOpaqueSceneColor";
18181
18210
  OpaqueSceneColor.UseCopy = true;
18211
+ OpaqueSceneColor.COPY_PLAIN = 0;
18212
+ OpaqueSceneColor.COPY_TO_LINEAR = 1;
18213
+ export class PhysicalSkyBlock {
18214
+ static UrpBlock(metadata) {
18215
+ const block = (metadata != null) ? metadata.urp : null;
18216
+ return (block != null && typeof block.version === "number" && block.version >= PhysicalSkyBlock.UrpBlockVersion) ? block : null;
18217
+ }
18218
+ static SkyOf(metadata) {
18219
+ if (HdrpRendering.IsParityExport(metadata) === true)
18220
+ return metadata.hdrp.sky;
18221
+ const block = PhysicalSkyBlock.UrpBlock(metadata);
18222
+ return (block != null) ? block.sky : null;
18223
+ }
18224
+ static FogOf(metadata) {
18225
+ if (HdrpRendering.IsParityExport(metadata) === true)
18226
+ return metadata.hdrp.fog;
18227
+ const block = PhysicalSkyBlock.UrpBlock(metadata);
18228
+ return (block != null) ? block.fog : null;
18229
+ }
18230
+ }
18231
+ PhysicalSkyBlock.UrpBlockVersion = 1;
18182
18232
  export class FixedStepClock {
18183
18233
  constructor(fixedDeltaTime = FixedStepClock.DEFAULT_FIXED_DELTA_TIME, maximumDeltaTime = FixedStepClock.DEFAULT_MAXIMUM_DELTA_TIME) {
18184
18234
  this.accumulatedTime = 0;
@@ -19447,7 +19497,9 @@ export class SceneShaderWarmup {
19447
19497
  SceneShaderWarmup.SkipFrustumClipping(scene, frustumState);
19448
19498
  const drawHidden = drawWholeScene && ownPipelines && options.includeHiddenMeshes === true && SceneShaderWarmup.DrawHiddenMeshesOnReveal === true;
19449
19499
  const shownHiddenMeshes = drawHidden ? SceneShaderWarmup.ShowHiddenMeshesForDraws(scene) : [];
19450
- const punctualShadowDraws = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.DrawPunctualShadowMapsFully(scene) : [];
19500
+ const fullShadowDraws = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.DrawShadowMapsFully(scene) : [];
19501
+ const lodCycle = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.CycleNativeLodLevels(scene) : null;
19502
+ const settleFrames = Math.max(SceneShaderWarmup.PIPELINE_SETTLE_FRAMES, (lodCycle != null) ? lodCycle.length : 0);
19451
19503
  const shadowListWraps = SceneShaderWarmup.KeepHiddenCastersInShadowMaps(scene, shownHiddenMeshes);
19452
19504
  const scaleVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateScaleVariants(scene) : null;
19453
19505
  const materialVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateMaterialVariants(scene) : null;
@@ -19458,10 +19510,11 @@ export class SceneShaderWarmup {
19458
19510
  const stageFraction = (requested > startRequested) ? (compiled - startCompiled) / (requested - startRequested) : 0;
19459
19511
  const pipelineStep = { step: "pipelines", ready: compiled - startCompiled, total: requested - startRequested };
19460
19512
  report(effects.ready + compiled - startCompiled, effects.total + requested - startRequested + 1, effectShare + (1 - effectShare) * stageFraction * 0.99, pipelineStep);
19461
- }).then((timedOut) => {
19513
+ }, settleFrames).then((timedOut) => {
19462
19514
  SceneShaderWarmup.RestoreFrustumClipping(scene, frustumState);
19463
19515
  SceneShaderWarmup.RestoreShadowCasterLists(shadowListWraps);
19464
- SceneShaderWarmup.RestorePunctualShadowMaps(punctualShadowDraws);
19516
+ SceneShaderWarmup.RestoreFullShadowMaps(fullShadowDraws);
19517
+ SceneShaderWarmup.RestoreNativeLodLevels(scene, lodCycle);
19465
19518
  SceneShaderWarmup.RestoreHiddenMeshes(scene, shownHiddenMeshes);
19466
19519
  SceneShaderWarmup.RestoreScaleVariants(scene, scaleVariantDraw);
19467
19520
  SceneShaderWarmup.RestoreMaterialVariants(scene, materialVariantDraw);
@@ -21181,13 +21234,15 @@ export class SceneShaderWarmup {
21181
21234
  wrap.shadowMap.getCustomRenderList = wrap.customList;
21182
21235
  }
21183
21236
  }
21184
- static DrawPunctualShadowMapsFully(scene) {
21237
+ static DrawShadowMapsFully(scene) {
21185
21238
  const draws = [];
21186
21239
  for (const shadowMap of SceneShaderWarmup.CollectShadowMaps(scene)) {
21187
21240
  const renderGate = shadowMap;
21188
- if (renderGate._tkSkipInvisible !== true)
21241
+ const isGated = renderGate._tkSkipInvisible === true;
21242
+ if (!isGated && !SceneShaderWarmup.HasCasterCullingList(shadowMap))
21189
21243
  continue;
21190
- renderGate._tkPrewarmDrawAll = true;
21244
+ if (isGated)
21245
+ renderGate._tkPrewarmDrawAll = true;
21191
21246
  draws.push({ shadowMap: shadowMap, customList: shadowMap.getCustomRenderList });
21192
21247
  shadowMap.getCustomRenderList = null;
21193
21248
  try {
@@ -21197,7 +21252,11 @@ export class SceneShaderWarmup {
21197
21252
  }
21198
21253
  return draws;
21199
21254
  }
21200
- static RestorePunctualShadowMaps(draws) {
21255
+ static HasCasterCullingList(shadowMap) {
21256
+ const customList = shadowMap.getCustomRenderList;
21257
+ return customList != null && (customList.tkCasterCulling === true || customList.tkTerrainCulling === true);
21258
+ }
21259
+ static RestoreFullShadowMaps(draws) {
21201
21260
  for (const draw of draws) {
21202
21261
  draw.shadowMap._tkPrewarmDrawAll = false;
21203
21262
  if (draw.shadowMap.getCustomRenderList == null)
@@ -21429,6 +21488,53 @@ export class SceneShaderWarmup {
21429
21488
  draw.frameObserver = null;
21430
21489
  SceneShaderWarmup.SetScaleVariantForced(draw.entries, false);
21431
21490
  }
21491
+ static NativeLodSequence(mesh) {
21492
+ if (!(mesh instanceof Mesh) || mesh.isDisposed())
21493
+ return [];
21494
+ const levels = mesh.getLODLevels();
21495
+ if (levels == null || levels.length === 0)
21496
+ return [];
21497
+ const sequence = [mesh];
21498
+ for (const level of levels) {
21499
+ if (level.mesh != null && !level.mesh.isDisposed() && sequence.indexOf(level.mesh) < 0)
21500
+ sequence.push(level.mesh);
21501
+ }
21502
+ return (sequence.length > 1) ? sequence : [];
21503
+ }
21504
+ static CycleNativeLodLevels(scene) {
21505
+ if (SceneShaderWarmup.WarmNativeLodLevelsOnReveal !== true)
21506
+ return null;
21507
+ const sequences = new Map();
21508
+ let length = 0;
21509
+ for (const mesh of scene.meshes) {
21510
+ const sequence = SceneShaderWarmup.NativeLodSequence(mesh);
21511
+ if (sequence.length === 0)
21512
+ continue;
21513
+ sequences.set(mesh, sequence);
21514
+ length = Math.max(length, sequence.length);
21515
+ }
21516
+ if (sequences.size === 0)
21517
+ return null;
21518
+ const previousSelector = scene.customLODSelector || null;
21519
+ const cycle = { sequences: sequences, length: length, frameIndex: 0, previousSelector: previousSelector, selector: null, frameObserver: null };
21520
+ cycle.selector = (mesh, camera) => {
21521
+ const sequence = sequences.get(mesh);
21522
+ if (sequence != null)
21523
+ return sequence[cycle.frameIndex % sequence.length];
21524
+ return (previousSelector != null) ? previousSelector(mesh, camera) : mesh.getLOD(camera);
21525
+ };
21526
+ cycle.frameObserver = scene.onAfterRenderObservable.add(() => { cycle.frameIndex++; });
21527
+ scene.customLODSelector = cycle.selector;
21528
+ return cycle;
21529
+ }
21530
+ static RestoreNativeLodLevels(scene, cycle) {
21531
+ if (cycle == null)
21532
+ return;
21533
+ scene.onAfterRenderObservable.remove(cycle.frameObserver);
21534
+ cycle.frameObserver = null;
21535
+ if (scene.customLODSelector === cycle.selector)
21536
+ scene.customLODSelector = cycle.previousSelector;
21537
+ }
21432
21538
  static SetScaleVariantForced(entries, forced) {
21433
21539
  const prototypeUpdate = AbstractMesh.prototype._updateNonUniformScalingState;
21434
21540
  for (const entry of entries) {
@@ -21572,8 +21678,8 @@ export class SceneShaderWarmup {
21572
21678
  }
21573
21679
  return phases;
21574
21680
  }
21575
- static WaitForPipelinesPerCamera(scene, pipelines, deadline, phases, onProgress) {
21576
- return SceneShaderWarmup.RunCameraPhasesAsync(scene, phases, false, () => SceneShaderWarmup.WaitForPipelines(scene, pipelines, deadline, onProgress), (timedOut) => timedOut);
21681
+ static WaitForPipelinesPerCamera(scene, pipelines, deadline, phases, onProgress, settleFrames = SceneShaderWarmup.PIPELINE_SETTLE_FRAMES) {
21682
+ return SceneShaderWarmup.RunCameraPhasesAsync(scene, phases, false, () => SceneShaderWarmup.WaitForPipelines(scene, pipelines, deadline, onProgress, settleFrames), (timedOut) => timedOut);
21577
21683
  }
21578
21684
  static RunCameraPhasesAsync(scene, phases, noPhaseRun, runStep, isTimedOut) {
21579
21685
  const runPhase = (phaseIndex, previous) => {
@@ -21591,7 +21697,7 @@ export class SceneShaderWarmup {
21591
21697
  };
21592
21698
  return runPhase(0, noPhaseRun);
21593
21699
  }
21594
- static WaitForPipelines(scene, pipelines, deadline, onProgress) {
21700
+ static WaitForPipelines(scene, pipelines, deadline, onProgress, settleFrames = SceneShaderWarmup.PIPELINE_SETTLE_FRAMES) {
21595
21701
  return new Promise((resolve) => {
21596
21702
  let quietFrames = 0;
21597
21703
  let lastRequested = -1;
@@ -21622,7 +21728,7 @@ export class SceneShaderWarmup {
21622
21728
  quietFrames = quiet ? quietFrames + 1 : 0;
21623
21729
  if (onProgress != null)
21624
21730
  onProgress(pipelines.compiled, pipelines.requested);
21625
- if (quietFrames >= SceneShaderWarmup.PIPELINE_SETTLE_FRAMES)
21731
+ if (quietFrames >= settleFrames)
21626
21732
  finish(false);
21627
21733
  });
21628
21734
  timer = setTimeout(() => finish(true), Math.max(0, deadline - SceneShaderWarmup.NowMs()));
@@ -22179,6 +22285,7 @@ SceneShaderWarmup.BuildScenePipelinesOnReveal = true;
22179
22285
  SceneShaderWarmup.DrawHiddenMeshesOnReveal = true;
22180
22286
  SceneShaderWarmup.WarmEmptyThinInstancesOnReveal = true;
22181
22287
  SceneShaderWarmup.WarmScaleVariantsOnReveal = true;
22288
+ SceneShaderWarmup.WarmNativeLodLevelsOnReveal = true;
22182
22289
  SceneShaderWarmup.OPTIONAL_UNIT_GRACE_MS = 3000;
22183
22290
  SceneShaderWarmup.GIVEN_UP_NAMES_SHOWN = 10;
22184
22291
  SceneShaderWarmup.LateCompileReporting = null;
@@ -23815,6 +23922,27 @@ export class ShaderGlobals {
23815
23922
  ShaderGlobals._keywords.add(k);
23816
23923
  ShaderGlobals._version++;
23817
23924
  }
23925
+ static LoadSceneDrivers(scene, drivers) {
23926
+ if (scene == null || drivers == null || drivers.length === 0)
23927
+ return;
23928
+ const observer = scene.onBeforeRenderObservable.add(() => {
23929
+ const deltaSeconds = ShaderGlobals.GetClock(scene).deltaTime;
23930
+ for (const driver of drivers)
23931
+ ShaderGlobals.AdvanceDriver(driver, deltaSeconds);
23932
+ });
23933
+ scene.onDisposeObservable.addOnce(() => { scene.onBeforeRenderObservable.remove(observer); });
23934
+ }
23935
+ static AdvanceDriver(driver, deltaSeconds) {
23936
+ const entry = ShaderGlobals._vectors.get(driver.name);
23937
+ if (entry == null)
23938
+ return;
23939
+ const componentName = ShaderGlobals.DriverComponentNames[driver.component];
23940
+ let advancedValue = entry.value[componentName] + driver.rate * deltaSeconds;
23941
+ if (driver.wrap > 0 && advancedValue > driver.wrap)
23942
+ advancedValue -= driver.wrap;
23943
+ entry.value[componentName] = advancedValue;
23944
+ ShaderGlobals._version++;
23945
+ }
23818
23946
  static GetClock(scene) {
23819
23947
  if (scene == null)
23820
23948
  return { time: 0, deltaTime: 0, smoothDeltaTime: 0, frame: 0 };
@@ -23849,6 +23977,7 @@ export class ShaderGlobals {
23849
23977
  return clock;
23850
23978
  }
23851
23979
  }
23980
+ ShaderGlobals.DriverComponentNames = ["x", "y", "z", "w"];
23852
23981
  ShaderGlobals._version = 0;
23853
23982
  ShaderGlobals._floats = new Map();
23854
23983
  ShaderGlobals._vectors = new Map();
@@ -25073,6 +25202,23 @@ export class ShaderGraphRuntime {
25073
25202
  loadTexture(textures[k], (texture) => { clone.setTextureValue(k, texture); });
25074
25203
  return clone;
25075
25204
  }
25205
+ static RecordExportReport(scene, entries) {
25206
+ if (entries == null || entries.length === 0)
25207
+ return;
25208
+ const record = ShaderGraphRuntime.Record(scene);
25209
+ if (record == null)
25210
+ return;
25211
+ record.exportReport = entries;
25212
+ ShaderGraphRuntime.ScheduleSceneSummary(scene);
25213
+ }
25214
+ static PrintExportReport(record) {
25215
+ const entries = record.exportReport;
25216
+ console.groupCollapsed("Export report: " + entries.length + " items not carried");
25217
+ for (const entry of entries)
25218
+ console.log(entry.feature + " - " + entry.reason + ": " + entry.items.join(", "));
25219
+ console.groupEnd();
25220
+ record.exportReport = null;
25221
+ }
25076
25222
  static FlushSceneSummary(scene) {
25077
25223
  const record = ShaderGraphRuntime._records.get(scene != null ? scene.uniqueId : -1);
25078
25224
  if (record == null || record.scene !== scene)
@@ -25092,6 +25238,8 @@ export class ShaderGraphRuntime {
25092
25238
  const report = (ShaderGraphRuntime.ReportDeviations != null) ? ShaderGraphRuntime.ReportDeviations
25093
25239
  : (SceneManager != null && typeof SceneManager.IsDebugMode === "function" ? SceneManager.IsDebugMode() : true);
25094
25240
  const n = record.materials.size, m = record.lastResorts.size;
25241
+ if (report && record.exportReport != null)
25242
+ ShaderGraphRuntime.PrintExportReport(record);
25095
25243
  if (report && (n > 0 || m > 0)) {
25096
25244
  console.groupCollapsed("Shader Graph: " + n + " materials with deviations, " + m + " last resort" + (record.flushed ? " (built after the scene summary)" : ""));
25097
25245
  record.materials.forEach((entry, name) => {
@@ -29818,8 +29966,9 @@ export class UnityLodGroups {
29818
29966
  static ExportedSize(root, metadata) {
29819
29967
  if (root == null || metadata == null || !(metadata.lodsize > 0))
29820
29968
  return null;
29821
- const c = metadata.lodcenter;
29822
- const localPoint = (Array.isArray(c) && c.length >= 3) ? new Vector3(c[0], c[1], c[2]) : Vector3.Zero();
29969
+ const exportedCenter = metadata.lodcenter;
29970
+ const hasCenter = (Array.isArray(exportedCenter) && exportedCenter.length >= 3);
29971
+ const localPoint = hasCenter ? new Vector3(0 - exportedCenter[0], exportedCenter[1], exportedCenter[2]) : Vector3.Zero();
29823
29972
  const scale = new Vector3();
29824
29973
  root.computeWorldMatrix(true).decompose(scale, null, null);
29825
29974
  const largest = Math.max(Math.abs(scale.x), Math.abs(scale.y), Math.abs(scale.z));
@@ -29830,6 +29979,35 @@ export class UnityLodGroups {
29830
29979
  const useDistances = !hasCoverages && (distances != null && distances.length > 0);
29831
29980
  return { useDistances: useDistances, thresholds: useDistances ? distances : (hasCoverages ? coverages : null) };
29832
29981
  }
29982
+ static LevelsShareTransform(master, details) {
29983
+ const masterMatrix = master.computeWorldMatrix(true);
29984
+ for (const detail of details) {
29985
+ if (UnityLodGroups.MatricesShareTransform(masterMatrix, detail.computeWorldMatrix(true)) === false)
29986
+ return false;
29987
+ }
29988
+ return true;
29989
+ }
29990
+ static MatricesShareTransform(first, second) {
29991
+ const offset = Vector3.Distance(first.getTranslation(), second.getTranslation());
29992
+ if (offset > UnityLodGroups.NATIVE_TRANSFORM_TOLERANCE_METERS)
29993
+ return false;
29994
+ for (const element of UnityLodGroups.BASIS_ELEMENTS) {
29995
+ if (Math.abs(first.m[element] - second.m[element]) > UnityLodGroups.NATIVE_BASIS_TOLERANCE)
29996
+ return false;
29997
+ }
29998
+ return true;
29999
+ }
30000
+ static RequiresSwitcher(meshLevels, fadeMode) {
30001
+ if (fadeMode !== UnityLodGroups.FADE_NONE)
30002
+ return true;
30003
+ if (meshLevels.some((meshes) => meshes.length > 1))
30004
+ return true;
30005
+ const master = meshLevels[0][0];
30006
+ if (master == null)
30007
+ return false;
30008
+ const details = meshLevels.slice(1).map((meshes) => meshes[0]).filter((mesh) => mesh != null);
30009
+ return UnityLodGroups.LevelsShareTransform(master, details) === false;
30010
+ }
29833
30011
  static ParseFadeMode(mode) {
29834
30012
  const m = (mode != null) ? String(mode).toLowerCase() : "";
29835
30013
  if (m === "crossfade")
@@ -29992,21 +30170,25 @@ export class UnityLodGroups {
29992
30170
  addNativeGroup(master, details, coverages, worldSize) {
29993
30171
  if (!(master instanceof Mesh) || master instanceof InstancedMesh || coverages == null)
29994
30172
  return false;
29995
- const n = Math.min(coverages.length, details.length + 1);
29996
- if (n <= 0)
30173
+ if (master.instances.length > 0)
30174
+ return false;
30175
+ const levelCount = Math.min(coverages.length, details.length + 1);
30176
+ if (levelCount <= 0)
29997
30177
  return false;
29998
- for (let k = 0; k < n - 1; k++) {
29999
- const d = details[k];
30000
- if (!(d instanceof Mesh) || d instanceof InstancedMesh || d === master || d._masterMesh != null || details.indexOf(d) !== k)
30178
+ for (let k = 0; k < levelCount - 1; k++) {
30179
+ const detail = details[k];
30180
+ if (!(detail instanceof Mesh) || detail instanceof InstancedMesh || detail === master || detail._masterMesh != null || details.indexOf(detail) !== k)
30001
30181
  return false;
30002
30182
  }
30003
- for (let k = 1; k < n; k++)
30183
+ if (UnityLodGroups.LevelsShareTransform(master, details.slice(0, levelCount - 1)) === false)
30184
+ return false;
30185
+ for (let k = 1; k < levelCount; k++)
30004
30186
  master.addLODLevel(1e6 + k, details[k - 1]);
30005
- master.addLODLevel(1e6 + n, null);
30187
+ master.addLODLevel(1e6 + levelCount, null);
30006
30188
  const all = master.getLODLevels();
30007
30189
  const entries = [];
30008
- for (let i = 0; i < n; i++) {
30009
- const mesh = (i < n - 1) ? details[i] : null;
30190
+ for (let i = 0; i < levelCount; i++) {
30191
+ const mesh = (i < levelCount - 1) ? details[i] : null;
30010
30192
  let found = null;
30011
30193
  for (const level of all) {
30012
30194
  if (level.mesh === mesh && entries.indexOf(level) === -1) {
@@ -30016,7 +30198,7 @@ export class UnityLodGroups {
30016
30198
  }
30017
30199
  entries.push(found);
30018
30200
  }
30019
- this._native.push({ master: master, entries: entries, coverages: coverages.slice(0, n), worldSize: worldSize });
30201
+ this._native.push({ master: master, entries: entries, coverages: coverages.slice(0, levelCount), worldSize: worldSize });
30020
30202
  this._nativeKey[0] = NaN;
30021
30203
  return true;
30022
30204
  }
@@ -30266,6 +30448,9 @@ UnityLodGroups.FADE_NONE = 0;
30266
30448
  UnityLodGroups.FADE_CROSSFADE = 1;
30267
30449
  UnityLodGroups.FADE_SPEEDTREE = 2;
30268
30450
  UnityLodGroups.FADE_SECONDS = 0.5;
30451
+ UnityLodGroups.NATIVE_TRANSFORM_TOLERANCE_METERS = 0.005;
30452
+ UnityLodGroups.NATIVE_BASIS_TOLERANCE = 0.001;
30453
+ UnityLodGroups.BASIS_ELEMENTS = [0, 1, 2, 4, 5, 6, 8, 9, 10];
30269
30454
  UnityLodGroups.DeferLoadFadeClones = true;
30270
30455
  UnityLodGroups.FadeCloneSliceMs = 8;
30271
30456
  UnityLodGroups._cloneQueues = new Map();
@@ -69897,16 +70082,36 @@ export class HdrpFogPass {
69897
70082
  return HdrpFogPass.Uniforms.concat((S != null && Array.isArray(S.AtmosphereUniforms)) ? S.AtmosphereUniforms : [], ["scale"]);
69898
70083
  }
69899
70084
  static FogBlock(scene) {
69900
- const block = (HdrpRendering != null) ? HdrpRendering.Block(scene) : null;
70085
+ if (HdrpRendering == null)
70086
+ return null;
70087
+ const fog = PhysicalSkyBlock.FogOf(HdrpRendering.SceneMetadata(scene));
70088
+ if (fog != null && typeof fog.enabled === "boolean")
70089
+ return fog;
70090
+ const block = HdrpRendering.Block(scene);
69901
70091
  return (block != null && block.fog != null && typeof block.fog === "object") ? block.fog : null;
69902
70092
  }
69903
70093
  static SkyOf(scene) {
69904
70094
  const live = (HdrpPhysicallyBasedSky != null) ? HdrpPhysicallyBasedSky.Get(scene) : null;
69905
70095
  if (live != null)
69906
70096
  return live;
69907
- const block = (HdrpRendering != null) ? HdrpRendering.Block(scene) : null;
70097
+ if (HdrpRendering == null)
70098
+ return null;
70099
+ const sky = PhysicalSkyBlock.SkyOf(HdrpRendering.SceneMetadata(scene));
70100
+ if (sky != null)
70101
+ return sky;
70102
+ const block = HdrpRendering.Block(scene);
69908
70103
  return (block != null) ? block.sky : null;
69909
70104
  }
70105
+ static BindChain(effect, scene, fog) {
70106
+ if (HdrpRendering.IsParity(scene) === true) {
70107
+ effect.setFloat4("fogChain", 0, 0, 0, 0);
70108
+ return;
70109
+ }
70110
+ const water = (MeshWaterSurface != null) ? MeshWaterSurface.Get(scene) : null;
70111
+ const isDisplayEncoded = scene.imageProcessingConfiguration.applyByPostProcess !== true;
70112
+ const isFogOn = fog != null && fog.enabled === true;
70113
+ effect.setFloat4("fogChain", isDisplayEncoded ? 1 : 0, (water != null) ? 1 : 0, (water != null) ? water.getSurfaceHeight() : 0, isFogOn ? 1 : 0);
70114
+ }
69910
70115
  static Frame(scene, fog) {
69911
70116
  const s = scene;
69912
70117
  const renderId = scene.getRenderId();
@@ -70204,6 +70409,113 @@ fn hdrpFogAmbient(dir: vec3f) -> vec3f {
70204
70409
  + uniforms.fogAmb4.rgb * (d.x * d.y) + uniforms.fogAmb5.rgb * (d.y * d.z) + uniforms.fogAmb6.rgb * (3.0 * d.z * d.z - 1.0)
70205
70410
  + uniforms.fogAmb7.rgb * (d.x * d.z) + uniforms.fogAmb8.rgb * (d.x * d.x - d.y * d.y);
70206
70411
  }
70412
+ `;
70413
+ }
70414
+ static AerialPerspectiveGlsl() {
70415
+ return `
70416
+ vec3 fogRescaledChapman(vec2 z, vec2 Z, float c) {
70417
+ float sinTheta = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
70418
+ vec2 ch = vec2(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
70419
+ if (c < 0.0) {
70420
+ vec2 z0 = z * sinTheta;
70421
+ ch = vec2(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
70422
+ }
70423
+ return vec3(ch, 0.0);
70424
+ }
70425
+ vec3 fogApOpticalDepth(vec3 O, vec3 V, float t) {
70426
+ float R = pbAtmo0.x;
70427
+ vec2 n = vec2(pbAtmo1.x, pbAtmo1.z);
70428
+ vec2 H = vec2(pbAtmo1.y, pbAtmo1.w);
70429
+ float r = length(O);
70430
+ vec3 N = O / r;
70431
+ float cosChi = dot(N, V);
70432
+ vec2 Z = R * n;
70433
+ vec3 P1 = O + t * V;
70434
+ float r1 = length(P1);
70435
+ float cosChi1 = dot(P1, V) / r1;
70436
+ float cosChi0 = (cosChi1 >= 0.0) ? cosChi : -cosChi;
70437
+ vec2 ch0 = fogRescaledChapman(r * n, Z, cosChi0).xy;
70438
+ vec2 z1 = r1 * n;
70439
+ vec2 ch1 = vec2(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
70440
+ vec2 od = abs(ch0 - ch1) * H;
70441
+ float ozone = pbOzoneOpticalDepth(r, cosChi, t);
70442
+ return od.x * pbAirExt.xyz + od.y * pbAirExt.w + ozone * pbOzone0.xyz;
70443
+ }
70444
+ void fogAerialPerspective(vec2 uv, vec3 O, vec3 dir, float tFrag, out vec3 apColor, out vec3 apOpacity) {
70445
+ float r = length(O);
70446
+ float cosChi = dot(O / r, dir);
70447
+ float tAp = min(tFrag, 128000.0);
70448
+ float off = 1.0 / 128.0;
70449
+ float s = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
70450
+ float layer = clamp(s * 64.0 - 0.5, 0.0, 63.0);
70451
+ float l0 = floor(layer);
70452
+ float l1 = min(l0 + 1.0, 63.0);
70453
+ apColor = mix(textureLod(apLut, vec3(uv, l0), 0.0).rgb, textureLod(apLut, vec3(uv, l1), 0.0).rgb, layer - l0);
70454
+ apOpacity = vec3(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
70455
+ apOpacity = pbDesaturate(apOpacity, pbZenith.w) * pbArt.x;
70456
+ float chi = acos(clamp(cosChi, -1.0, 1.0));
70457
+ float hor = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
70458
+ float nrm = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
70459
+ float te = exp(pbArt.y * nrm) * pbArt.z - pbArt.z;
70460
+ apColor *= mix(pbHorizon.xyz, pbZenith.xyz, te);
70461
+ }
70462
+ `;
70463
+ }
70464
+ static AerialPerspectiveWgsl() {
70465
+ return `
70466
+ fn fogRescaledChapman(z: vec2f, Z: vec2f, c: f32) -> vec2f {
70467
+ let sinTheta: f32 = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
70468
+ var ch: vec2f = vec2f(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
70469
+ if (c < 0.0) {
70470
+ let z0: vec2f = z * sinTheta;
70471
+ ch = vec2f(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
70472
+ }
70473
+ return ch;
70474
+ }
70475
+ fn fogApOpticalDepth(O: vec3f, V: vec3f, t: f32) -> vec3f {
70476
+ let R: f32 = uniforms.pbAtmo0.x;
70477
+ let n: vec2f = vec2f(uniforms.pbAtmo1.x, uniforms.pbAtmo1.z);
70478
+ let H: vec2f = vec2f(uniforms.pbAtmo1.y, uniforms.pbAtmo1.w);
70479
+ let r: f32 = length(O);
70480
+ let N: vec3f = O / r;
70481
+ let cosChi: f32 = dot(N, V);
70482
+ let Z: vec2f = R * n;
70483
+ let P1: vec3f = O + t * V;
70484
+ let r1: f32 = length(P1);
70485
+ let cosChi1: f32 = dot(P1, V) / r1;
70486
+ let cosChi0: f32 = select(-cosChi, cosChi, cosChi1 >= 0.0);
70487
+ let ch0: vec2f = fogRescaledChapman(r * n, Z, cosChi0);
70488
+ let z1: vec2f = r1 * n;
70489
+ let ch1: vec2f = vec2f(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
70490
+ let od: vec2f = abs(ch0 - ch1) * H;
70491
+ let ozone: f32 = pbOzoneOpticalDepth(r, cosChi, t);
70492
+ return od.x * uniforms.pbAirExt.xyz + od.y * uniforms.pbAirExt.w + ozone * uniforms.pbOzone0.xyz;
70493
+ }
70494
+ struct FogAerial {
70495
+ color: vec3f,
70496
+ opacity: vec3f,
70497
+ };
70498
+ fn fogAerialPerspective(uv: vec2f, O: vec3f, dir: vec3f, tFrag: f32) -> FogAerial {
70499
+ let r: f32 = length(O);
70500
+ let cosChi: f32 = dot(O / r, dir);
70501
+ let tAp: f32 = min(tFrag, 128000.0);
70502
+ let off: f32 = 1.0 / 128.0;
70503
+ let s: f32 = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
70504
+ let layer: f32 = clamp(s * 64.0 - 0.5, 0.0, 63.0);
70505
+ let l0: f32 = floor(layer);
70506
+ let l1: f32 = min(l0 + 1.0, 63.0);
70507
+ let c0: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l0), 0.0).rgb;
70508
+ let c1: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l1), 0.0).rgb;
70509
+ var apColor: vec3f = mix(c0, c1, vec3f(layer - l0));
70510
+ var apOpacity: vec3f = vec3f(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
70511
+ apOpacity = pbDesaturate(apOpacity, uniforms.pbZenith.w) * uniforms.pbArt.x;
70512
+ let chi: f32 = acos(clamp(cosChi, -1.0, 1.0));
70513
+ let hor: f32 = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
70514
+ let nrm: f32 = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
70515
+ let te: f32 = exp(uniforms.pbArt.y * nrm) * uniforms.pbArt.z - uniforms.pbArt.z;
70516
+ apColor *= mix(uniforms.pbHorizon.xyz, uniforms.pbZenith.xyz, vec3f(te));
70517
+ return FogAerial(apColor, apOpacity);
70518
+ }
70207
70519
  `;
70208
70520
  }
70209
70521
  static GlslHelpers(prefix) {
@@ -70251,6 +70563,7 @@ fn ${prefix}PhaseHG(g: f32, c: f32) -> f32 {
70251
70563
  precision highp sampler2DArray;
70252
70564
  #endif
70253
70565
  precision highp float;
70566
+ #define FOG_DISPLAY_GAMMA 2.2
70254
70567
  varying vec2 vUV;
70255
70568
  uniform sampler2D textureSampler;
70256
70569
  uniform sampler2D depthSampler;
@@ -70270,40 +70583,14 @@ ${HdrpFogPass.AmbientDeclarations(false)}uniform vec4 fogEye;
70270
70583
  uniform vec4 fogForward;
70271
70584
  uniform vec4 fogViewport;
70272
70585
  uniform vec4 fogInput;
70586
+ uniform vec4 fogChain;
70273
70587
  uniform mat4 fogInvViewProj;
70274
70588
  uniform mat4 fogEnvMatrix;
70275
70589
  #ifdef HDRP_FOG_AP
70276
70590
  uniform sampler2DArray apLut;
70277
70591
  uniform vec4 fogApCamera;
70278
70592
  ${ap}
70279
- vec3 fogRescaledChapman(vec2 z, vec2 Z, float c) {
70280
- float sinTheta = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
70281
- vec2 ch = vec2(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
70282
- if (c < 0.0) {
70283
- vec2 z0 = z * sinTheta;
70284
- ch = vec2(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
70285
- }
70286
- return vec3(ch, 0.0);
70287
- }
70288
- vec3 fogApOpticalDepth(vec3 O, vec3 V, float t) {
70289
- float R = pbAtmo0.x;
70290
- vec2 n = vec2(pbAtmo1.x, pbAtmo1.z);
70291
- vec2 H = vec2(pbAtmo1.y, pbAtmo1.w);
70292
- float r = length(O);
70293
- vec3 N = O / r;
70294
- float cosChi = dot(N, V);
70295
- vec2 Z = R * n;
70296
- vec3 P1 = O + t * V;
70297
- float r1 = length(P1);
70298
- float cosChi1 = dot(P1, V) / r1;
70299
- float cosChi0 = (cosChi1 >= 0.0) ? cosChi : -cosChi;
70300
- vec2 ch0 = fogRescaledChapman(r * n, Z, cosChi0).xy;
70301
- vec2 z1 = r1 * n;
70302
- vec2 ch1 = vec2(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
70303
- vec2 od = abs(ch0 - ch1) * H;
70304
- float ozone = pbOzoneOpticalDepth(r, cosChi, t);
70305
- return od.x * pbAirExt.xyz + od.y * pbAirExt.w + ozone * pbOzone0.xyz;
70306
- }
70593
+ ${HdrpFogPass.AerialPerspectiveGlsl()}
70307
70594
  #endif
70308
70595
  ${HdrpFogPass.AmbientGlsl()}${HdrpFogPass.GlslHelpers("hdrpFog")}${HdrpFogPass.LocalGlsl()}
70309
70596
  vec3 hdrpFogColor(vec3 dir, float dist) {
@@ -70356,29 +70643,28 @@ void main(void) {
70356
70643
  opacity = vec3(1.0 - trCamera);
70357
70644
  }
70358
70645
  #ifdef HDRP_FOG_AP
70359
- if (!isSky && fogApCamera.w > 0.5 && fogEye.w > 0.5) {
70360
- vec3 O = fogApCamera.xyz;
70361
- float r = length(O);
70362
- float cosChi = dot(O / r, dir);
70363
- float tAp = min(tFrag, 128000.0);
70364
- float off = 1.0 / 128.0;
70365
- float s = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
70366
- float layer = clamp(s * 64.0 - 0.5, 0.0, 63.0);
70367
- float l0 = floor(layer);
70368
- float l1 = min(l0 + 1.0, 63.0);
70369
- vec3 apColor = mix(textureLod(apLut, vec3(vUV, l0), 0.0).rgb, textureLod(apLut, vec3(vUV, l1), 0.0).rgb, layer - l0);
70370
- vec3 apOpacity = vec3(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
70371
- apOpacity = pbDesaturate(apOpacity, pbZenith.w) * pbArt.x;
70372
- float chi = acos(clamp(cosChi, -1.0, 1.0));
70373
- float hor = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
70374
- float nrm = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
70375
- float te = exp(pbArt.y * nrm) * pbArt.z - pbArt.z;
70376
- apColor *= mix(pbHorizon.xyz, pbZenith.xyz, te);
70646
+ // fogChain.w: a URP sky with its fog on gives sky pixels aerial perspective up to the fog distance, as Unity's opaque pass does
70647
+ if ((!isSky || fogChain.w > 0.5) && fogApCamera.w > 0.5 && fogEye.w > 0.5) {
70648
+ vec3 apColor = vec3(0.0);
70649
+ vec3 apOpacity = vec3(0.0);
70650
+ fogAerialPerspective(vUV, fogApCamera.xyz, dir, tFrag, apColor, apOpacity);
70377
70651
  color = color + (vec3(1.0) - opacity) * apColor;
70378
70652
  opacity = opacity + (vec3(1.0) - opacity) * apOpacity;
70379
70653
  }
70380
70654
  #endif
70381
- gl_FragColor = vec4(src.rgb * (vec3(1.0) - opacity) + color, src.a);
70655
+ // fogChain.yz: a URP sky fogs before transparents, so a ray that meets the water before the opaque depth keeps its colour
70656
+ if (fogChain.y > 0.5 && dir.y < 0.0 && fogEye.y > fogChain.z) {
70657
+ float tWater = (fogChain.z - fogEye.y) / dir.y;
70658
+ if (isSky || tWater < tFrag) {
70659
+ color = vec3(0.0);
70660
+ opacity = vec3(0.0);
70661
+ }
70662
+ }
70663
+ // fogChain.x: the chain holds display values, so the fog composites in linear and encodes again
70664
+ vec3 scene = (fogChain.x > 0.5) ? pow(src.rgb, vec3(FOG_DISPLAY_GAMMA)) : src.rgb;
70665
+ vec3 fogged = scene * (vec3(1.0) - opacity) + color;
70666
+ if (fogChain.x > 0.5) fogged = pow(clamp(fogged, vec3(0.0), vec3(1.0)), vec3(1.0 / FOG_DISPLAY_GAMMA));
70667
+ gl_FragColor = vec4(fogged, src.a);
70382
70668
  }
70383
70669
  `;
70384
70670
  }
@@ -70386,6 +70672,7 @@ void main(void) {
70386
70672
  const S = HdrpPbSkyShaders;
70387
70673
  const ap = (S != null) ? (S.WgslUniforms() + S.WgslCommon()) : "";
70388
70674
  return `varying vUV: vec2f;
70675
+ const FOG_DISPLAY_GAMMA: f32 = 2.2;
70389
70676
  var textureSamplerSampler: sampler;
70390
70677
  var textureSampler: texture_2d<f32>;
70391
70678
  var depthSamplerSampler: sampler;
@@ -70407,6 +70694,7 @@ ${HdrpFogPass.AmbientDeclarations(true)}uniform fogEye: vec4f;
70407
70694
  uniform fogForward: vec4f;
70408
70695
  uniform fogViewport: vec4f;
70409
70696
  uniform fogInput: vec4f;
70697
+ uniform fogChain: vec4f;
70410
70698
  uniform fogInvViewProj: mat4x4f;
70411
70699
  uniform fogEnvMatrix: mat4x4f;
70412
70700
  #ifdef HDRP_FOG_AP
@@ -70414,34 +70702,7 @@ var apLutSampler: sampler;
70414
70702
  var apLut: texture_2d_array<f32>;
70415
70703
  uniform fogApCamera: vec4f;
70416
70704
  ${ap}
70417
- fn fogRescaledChapman(z: vec2f, Z: vec2f, c: f32) -> vec2f {
70418
- let sinTheta: f32 = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
70419
- var ch: vec2f = vec2f(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
70420
- if (c < 0.0) {
70421
- let z0: vec2f = z * sinTheta;
70422
- ch = vec2f(2.0 * pbChapmanHorizontal(z0.x) * exp(Z.x - z0.x), 2.0 * pbChapmanHorizontal(z0.y) * exp(Z.y - z0.y)) - ch;
70423
- }
70424
- return ch;
70425
- }
70426
- fn fogApOpticalDepth(O: vec3f, V: vec3f, t: f32) -> vec3f {
70427
- let R: f32 = uniforms.pbAtmo0.x;
70428
- let n: vec2f = vec2f(uniforms.pbAtmo1.x, uniforms.pbAtmo1.z);
70429
- let H: vec2f = vec2f(uniforms.pbAtmo1.y, uniforms.pbAtmo1.w);
70430
- let r: f32 = length(O);
70431
- let N: vec3f = O / r;
70432
- let cosChi: f32 = dot(N, V);
70433
- let Z: vec2f = R * n;
70434
- let P1: vec3f = O + t * V;
70435
- let r1: f32 = length(P1);
70436
- let cosChi1: f32 = dot(P1, V) / r1;
70437
- let cosChi0: f32 = select(-cosChi, cosChi, cosChi1 >= 0.0);
70438
- let ch0: vec2f = fogRescaledChapman(r * n, Z, cosChi0);
70439
- let z1: vec2f = r1 * n;
70440
- let ch1: vec2f = vec2f(pbChapmanUpper(z1.x, abs(cosChi1)) * exp(Z.x - z1.x), pbChapmanUpper(z1.y, abs(cosChi1)) * exp(Z.y - z1.y));
70441
- let od: vec2f = abs(ch0 - ch1) * H;
70442
- let ozone: f32 = pbOzoneOpticalDepth(r, cosChi, t);
70443
- return od.x * uniforms.pbAirExt.xyz + od.y * uniforms.pbAirExt.w + ozone * uniforms.pbOzone0.xyz;
70444
- }
70705
+ ${HdrpFogPass.AerialPerspectiveWgsl()}
70445
70706
  #endif
70446
70707
  ${HdrpFogPass.AmbientWgsl()}${HdrpFogPass.WgslHelpers("hdrpFog")}${HdrpFogPass.LocalWgsl()}
70447
70708
  fn hdrpFogColor(dir: vec3f, dist: f32) -> vec3f {
@@ -70497,31 +70758,29 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
70497
70758
  opacity = vec3f(1.0 - trCamera);
70498
70759
  }
70499
70760
  #ifdef HDRP_FOG_AP
70500
- if (!isSky && uniforms.fogApCamera.w > 0.5 && uniforms.fogEye.w > 0.5) {
70501
- let O: vec3f = uniforms.fogApCamera.xyz;
70502
- let r: f32 = length(O);
70503
- let cosChi: f32 = dot(O / r, dir);
70504
- let tAp: f32 = min(tFrag, 128000.0);
70505
- let off: f32 = 1.0 / 128.0;
70506
- let s: f32 = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
70507
- let layer: f32 = clamp(s * 64.0 - 0.5, 0.0, 63.0);
70508
- let l0: f32 = floor(layer);
70509
- let l1: f32 = min(l0 + 1.0, 63.0);
70510
- let c0: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l0), 0.0).rgb;
70511
- let c1: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l1), 0.0).rgb;
70512
- var apColor: vec3f = mix(c0, c1, vec3f(layer - l0));
70513
- var apOpacity: vec3f = vec3f(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
70514
- apOpacity = pbDesaturate(apOpacity, uniforms.pbZenith.w) * uniforms.pbArt.x;
70515
- let chi: f32 = acos(clamp(cosChi, -1.0, 1.0));
70516
- let hor: f32 = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
70517
- let nrm: f32 = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
70518
- let te: f32 = exp(uniforms.pbArt.y * nrm) * uniforms.pbArt.z - uniforms.pbArt.z;
70519
- apColor *= mix(uniforms.pbHorizon.xyz, uniforms.pbZenith.xyz, vec3f(te));
70761
+ // fogChain.w: a URP sky with its fog on gives sky pixels aerial perspective up to the fog distance, as Unity's opaque pass does
70762
+ if ((!isSky || uniforms.fogChain.w > 0.5) && uniforms.fogApCamera.w > 0.5 && uniforms.fogEye.w > 0.5) {
70763
+ let aerial: FogAerial = fogAerialPerspective(uv, uniforms.fogApCamera.xyz, dir, tFrag);
70764
+ let apColor: vec3f = aerial.color;
70765
+ let apOpacity: vec3f = aerial.opacity;
70520
70766
  color = color + (vec3f(1.0) - opacity) * apColor;
70521
70767
  opacity = opacity + (vec3f(1.0) - opacity) * apOpacity;
70522
70768
  }
70523
70769
  #endif
70524
- fragmentOutputs.color = vec4f(src.rgb * (vec3f(1.0) - opacity) + color, src.a);
70770
+ // fogChain.yz: a URP sky fogs before transparents, so a ray that meets the water before the opaque depth keeps its colour
70771
+ if (uniforms.fogChain.y > 0.5 && dir.y < 0.0 && uniforms.fogEye.y > uniforms.fogChain.z) {
70772
+ let tWater: f32 = (uniforms.fogChain.z - uniforms.fogEye.y) / dir.y;
70773
+ if (isSky || tWater < tFrag) {
70774
+ color = vec3f(0.0);
70775
+ opacity = vec3f(0.0);
70776
+ }
70777
+ }
70778
+ // fogChain.x: the chain holds display values, so the fog composites in linear and encodes again
70779
+ var scene: vec3f = src.rgb;
70780
+ if (uniforms.fogChain.x > 0.5) { scene = pow(src.rgb, vec3f(FOG_DISPLAY_GAMMA)); }
70781
+ var fogged: vec3f = scene * (vec3f(1.0) - opacity) + color;
70782
+ if (uniforms.fogChain.x > 0.5) { fogged = pow(clamp(fogged, vec3f(0.0), vec3f(1.0)), vec3f(1.0 / FOG_DISPLAY_GAMMA)); }
70783
+ fragmentOutputs.color = vec4f(fogged, src.a);
70525
70784
  }
70526
70785
  `;
70527
70786
  }
@@ -70602,6 +70861,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
70602
70861
  effect.setFloat4("fogE", frame.e[0], frame.e[1], frame.e[2], frame.e[3]);
70603
70862
  effect.setFloat4("fogSunDir", frame.sunDir[0], frame.sunDir[1], frame.sunDir[2], frame.sunDir[3]);
70604
70863
  effect.setFloat4("fogSun", frame.sun[0], frame.sun[1], frame.sun[2], frame.sun[3]);
70864
+ HdrpFogPass.BindChain(effect, scene, state.fog);
70605
70865
  effect.setFloat4("fogEnv", frame.env[0], frame.env[1], frame.env[2], frame.env[3]);
70606
70866
  effect.setFloat4("fogSky", frame.sky[0], frame.sky[1], frame.sky[2], frame.sky[3]);
70607
70867
  for (let i = 0; i < HdrpFogPass.AmbientVectors; i++)
@@ -70633,7 +70893,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
70633
70893
  }
70634
70894
  }
70635
70895
  HdrpFogPass.ShaderName = "toolkitHdrpFog";
70636
- HdrpFogPass.Uniforms = ["fogA", "fogB", "fogC", "fogD", "fogE", "fogSunDir", "fogSun", "fogEnv", "fogSky", "fogAmb0", "fogAmb1", "fogAmb2", "fogAmb3", "fogAmb4", "fogAmb5", "fogAmb6", "fogAmb7", "fogAmb8", "fogEye", "fogForward", "fogViewport", "fogInput", "fogInvViewProj", "fogEnvMatrix", "fogApCamera"];
70896
+ HdrpFogPass.Uniforms = ["fogA", "fogB", "fogC", "fogD", "fogE", "fogSunDir", "fogSun", "fogEnv", "fogSky", "fogAmb0", "fogAmb1", "fogAmb2", "fogAmb3", "fogAmb4", "fogAmb5", "fogAmb6", "fogAmb7", "fogAmb8", "fogEye", "fogForward", "fogViewport", "fogInput", "fogInvViewProj", "fogEnvMatrix", "fogApCamera", "fogChain"];
70637
70897
  HdrpFogPass.Samplers = ["depthSampler", "fogEnvSampler", "apLut"];
70638
70898
  HdrpFogPass.AmbientVectors = 9;
70639
70899
  HdrpFogPass.registered = false;
@@ -71258,6 +71518,8 @@ export class HdrpPbSkyShaders {
71258
71518
  #define PB_TWO_PI 6.28318530718
71259
71519
  #define PB_HALF_PI 1.57079632679
71260
71520
  #define PB_INV_PI 0.31830988618
71521
+ #define PB_DISPLAY_GAMMA 0.45454545454
71522
+ #define PB_NO_BODY_HIT 3.0e38
71261
71523
  #define PB_FLT_MIN 1.175494351e-38
71262
71524
  #define PB_FLT_EPS 5.960464478e-8
71263
71525
  float pbSat(float x) { return clamp(x, 0.0, 1.0); }
@@ -71687,6 +71949,10 @@ vec2 pbCloudLighting${X}(vec2 uv) {
71687
71949
  ` + HdrpPbSkyShaders.GlslCloudUniforms() + `#ifdef PB_CLOUDS
71688
71950
  uniform sampler2D cloudTex;
71689
71951
  #endif
71952
+ #ifdef PBSKY_SPACE_EMISSION
71953
+ uniform samplerCube spaceCube;
71954
+ uniform vec4 pbSpace;
71955
+ #endif
71690
71956
  ` + HdrpPbSkyShaders.GlslCommon() + `
71691
71957
  vec2 pbMapSkyView(float cosChi, vec3 V) {
71692
71958
  float coord = pbFastACos(cosChi) / PB_HALF_PI;
@@ -71781,6 +72047,12 @@ vec3 pbRenderSky(vec3 d) {
71781
72047
  }
71782
72048
  }
71783
72049
  }
72050
+ #ifdef PBSKY_SPACE_EMISSION
72051
+ if (lookAbove && tFrag >= PB_NO_BODY_HIT) {
72052
+ vec3 spaceDirection = vec3((pbSpace.y * d.x - pbSpace.z * d.z) * pbSpace.w, d.y, pbSpace.z * d.x + pbSpace.y * d.z);
72053
+ radiance += textureLod(spaceCube, spaceDirection, 0.0).rgb * pbSpace.x;
72054
+ }
72055
+ #endif
71784
72056
  vec3 skyColor = vec3(0.0);
71785
72057
  vec3 skyOpacity = vec3(0.0);
71786
72058
  if (lookAbove) {
@@ -71803,7 +72075,12 @@ vec3 pbRenderSky(vec3 d) {
71803
72075
  }
71804
72076
  void main(void) {
71805
72077
  vec3 d = normalize(vDir);
71806
- gl_FragColor = vec4(pbRenderSky(d) * pbArt.w, 1.0);
72078
+ // pbCounts.w: a URP sky on an LDR camera. Its world has no ground below the horizon, where the sky pass has no in-scatter
72079
+ if (pbCounts.w > 0.5) d.y = max(d.y, 0.0);
72080
+ vec3 color = pbRenderSky(normalize(d)) * pbArt.w;
72081
+ // the same flag: the camera has no HDR chain, so the sky encodes for display as materials do
72082
+ if (pbCounts.w > 0.5) color = pow(clamp(color, vec3(0.0), vec3(1.0)), vec3(PB_DISPLAY_GAMMA));
72083
+ gl_FragColor = vec4(color, 1.0);
71807
72084
  }
71808
72085
  `;
71809
72086
  }
@@ -71830,6 +72107,8 @@ const PB_PI: f32 = 3.14159265359;
71830
72107
  const PB_TWO_PI: f32 = 6.28318530718;
71831
72108
  const PB_HALF_PI: f32 = 1.57079632679;
71832
72109
  const PB_INV_PI: f32 = 0.31830988618;
72110
+ const PB_DISPLAY_GAMMA: f32 = 0.45454545454;
72111
+ const PB_NO_BODY_HIT: f32 = 3.0e38;
71833
72112
  const PB_FLT_MIN: f32 = 1.175494351e-38;
71834
72113
  const PB_FLT_EPS: f32 = 5.960464478e-8;
71835
72114
  fn pbSat(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
@@ -72272,6 +72551,11 @@ fn pbCloudLighting${X}(uv: vec2f) -> vec2f {
72272
72551
  var cloudTexSampler: sampler;
72273
72552
  var cloudTex: texture_2d<f32>;
72274
72553
  #endif
72554
+ #ifdef PBSKY_SPACE_EMISSION
72555
+ var spaceCubeSampler: sampler;
72556
+ var spaceCube: texture_cube<f32>;
72557
+ uniform pbSpace: vec4f;
72558
+ #endif
72275
72559
  ` + HdrpPbSkyShaders.WgslCommon() + `
72276
72560
  fn pbMapSkyView(cosChi: f32, V: vec3f) -> vec2f {
72277
72561
  let coord: f32 = pbFastACos(cosChi) / PB_HALF_PI;
@@ -72367,6 +72651,12 @@ fn pbRenderSky(d: vec3f) -> vec3f {
72367
72651
  }
72368
72652
  }
72369
72653
  }
72654
+ #ifdef PBSKY_SPACE_EMISSION
72655
+ if (lookAbove && tFrag >= PB_NO_BODY_HIT) {
72656
+ let spaceDirection: vec3f = vec3f((uniforms.pbSpace.y * d.x - uniforms.pbSpace.z * d.z) * uniforms.pbSpace.w, d.y, uniforms.pbSpace.z * d.x + uniforms.pbSpace.y * d.z);
72657
+ radiance += textureSampleLevel(spaceCube, spaceCubeSampler, spaceDirection, 0.0).rgb * uniforms.pbSpace.x;
72658
+ }
72659
+ #endif
72370
72660
  var skyColor: vec3f = vec3f(0.0);
72371
72661
  var skyOpacity: vec3f = vec3f(0.0);
72372
72662
  if (lookAbove) {
@@ -72389,8 +72679,13 @@ fn pbRenderSky(d: vec3f) -> vec3f {
72389
72679
  }
72390
72680
  @fragment
72391
72681
  fn main(input: FragmentInputs) -> FragmentOutputs {
72392
- let d: vec3f = normalize(fragmentInputs.vDir);
72393
- fragmentOutputs.color = vec4f(pbRenderSky(d) * uniforms.pbArt.w, 1.0);
72682
+ var d: vec3f = normalize(fragmentInputs.vDir);
72683
+ // pbCounts.w: a URP sky on an LDR camera. Its world has no ground below the horizon, where the sky pass has no in-scatter
72684
+ if (uniforms.pbCounts.w > 0.5) { d.y = max(d.y, 0.0); }
72685
+ var color: vec3f = pbRenderSky(normalize(d)) * uniforms.pbArt.w;
72686
+ // the same flag: the camera has no HDR chain, so the sky encodes for display as materials do
72687
+ if (uniforms.pbCounts.w > 0.5) { color = pow(clamp(color, vec3f(0.0), vec3f(1.0)), vec3f(PB_DISPLAY_GAMMA)); }
72688
+ fragmentOutputs.color = vec4f(color, 1.0);
72394
72689
  }
72395
72690
  `;
72396
72691
  }
@@ -72643,17 +72938,16 @@ export class HdrpPhysicallyBasedSky {
72643
72938
  return (sky != null && sky.disposed !== true) ? sky : null;
72644
72939
  }
72645
72940
  static Decide(metadata, engine) {
72646
- if (HdrpRendering.IsParityExport(metadata) === false)
72647
- return { live: false, key: null, reason: "not an HDRP parity export" };
72648
- const sky = metadata.hdrp.sky;
72941
+ const sky = PhysicalSkyBlock.SkyOf(metadata);
72942
+ const isUrp = PhysicalSkyBlock.UrpBlock(metadata) != null && !HdrpRendering.IsParityExport(metadata);
72649
72943
  if (sky == null)
72650
- return { live: false, key: null, reason: "no hdrp.sky block (export older than the live sky)" };
72944
+ return { live: false, key: null, reason: "no physical sky block" };
72651
72945
  const type = (typeof sky.type === "string") ? sky.type.toLowerCase() : "";
72652
72946
  if (type !== "physicallybased")
72653
72947
  return { live: false, key: null, reason: "sky type " + (type || "unknown") };
72654
72948
  if (HdrpPhysicallyBasedSky.IsSupported(engine) === false)
72655
72949
  return { live: false, key: "hdrp:pbsky:unsupported", reason: "no WebGL2 / WebGPU half-float render target" };
72656
- if (sky.volumetricclouds === true)
72950
+ if (isUrp === false && sky.volumetricclouds === true)
72657
72951
  return { live: false, key: "hdrp:pbsky:volumetricclouds", reason: "an active VolumetricClouds is not ported (the bake carries it)" };
72658
72952
  return { live: true, key: null, reason: "live" };
72659
72953
  }
@@ -72665,11 +72959,12 @@ export class HdrpPhysicallyBasedSky {
72665
72959
  if (decision.key != null)
72666
72960
  LightingConversions.WarnOnce(decision.key, "HDRP PhysicallyBasedSky: " + decision.reason + " - the baked sky is used");
72667
72961
  if (decision.key != null)
72668
- HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, metadata.hdrp.sky);
72962
+ HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, PhysicalSkyBlock.SkyOf(metadata));
72669
72963
  return null;
72670
72964
  }
72671
72965
  try {
72672
- return HdrpPhysicallyBasedSky.Create(scene, metadata.hdrp.sky, null, { root: root, bakedSky: (options != null) ? options.bakedSky : null, rendergroup: (options != null) ? options.rendergroup : 0, environmentSh: (metadata.skybox != null && metadata.skybox.environment != null) ? metadata.skybox.environment.sh : null });
72966
+ const environment = (metadata.skybox != null) ? metadata.skybox.environment : null;
72967
+ return HdrpPhysicallyBasedSky.Create(scene, PhysicalSkyBlock.SkyOf(metadata), null, { root: root, bakedSky: (options != null) ? options.bakedSky : null, rendergroup: (options != null) ? options.rendergroup : 0, environmentSh: (environment != null) ? environment.sh : null });
72673
72968
  }
72674
72969
  catch (e) {
72675
72970
  LightingConversions.WarnOnce("hdrp:pbsky:create", "HDRP PhysicallyBasedSky could not be created (" + ((e && e.message) ? e.message : e) + ") - the baked sky is used");
@@ -72755,6 +73050,13 @@ export class HdrpPhysicallyBasedSky {
72755
73050
  }
72756
73051
  return precomputationHash !== state.lastPrecompute || lightHash !== state.lastLight || state.requested === true;
72757
73052
  }
73053
+ static ProbeRenderOutcome(capturing, pendingFilter, drewSky) {
73054
+ if (capturing === true)
73055
+ return (drewSky === true) ? HdrpPhysicallyBasedSky.ProbeCaptureAccepted : HdrpPhysicallyBasedSky.ProbeCaptureRetry;
73056
+ if (pendingFilter === true && drewSky !== true)
73057
+ return HdrpPhysicallyBasedSky.ProbeCaptureRetry;
73058
+ return HdrpPhysicallyBasedSky.ProbeRenderKept;
73059
+ }
72758
73060
  static SelectAmbientSh(skyAmbientMode, readback, exported) {
72759
73061
  if (skyAmbientMode === 1)
72760
73062
  return (readback != null) ? readback : exported;
@@ -72843,6 +73145,7 @@ export class HdrpPhysicallyBasedSky {
72843
73145
  this.filtering = null;
72844
73146
  this.pendingFilter = false;
72845
73147
  this.capturing = false;
73148
+ this.probeDrawsSky = false;
72846
73149
  this.shReading = false;
72847
73150
  this.readbackHarmonics = null;
72848
73151
  this.exportedHarmonics = null;
@@ -72857,6 +73160,7 @@ export class HdrpPhysicallyBasedSky {
72857
73160
  this.startTime = Date.now();
72858
73161
  this.scene = scene;
72859
73162
  this.sky = sky || {};
73163
+ this.sunIntensityScale = (typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
72860
73164
  this.options = options;
72861
73165
  this.fixedLights = lights;
72862
73166
  this.coefficients = HdrpPbSkyMath.Coefficients(this.sky);
@@ -72907,16 +73211,23 @@ export class HdrpPhysicallyBasedSky {
72907
73211
  defines.push("#define PB_CLOUDS");
72908
73212
  if (layers.length >= 2)
72909
73213
  defines.push("#define PB_CLOUDS2");
73214
+ this.spaceCube = this.loadSpaceEmission();
73215
+ if (this.spaceCube != null)
73216
+ defines.push("#define PBSKY_SPACE_EMISSION");
72910
73217
  const material = new ShaderMaterial("HDRP PBSky Material", scene, { vertex: HdrpPbSkyShaders.SkyName, fragment: HdrpPbSkyShaders.SkyName }, {
72911
73218
  attributes: ["position"],
72912
- uniforms: ["world", "viewProjection"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
72913
- samplers: ["skyViewLut", "cloudTex"],
73219
+ uniforms: ["world", "viewProjection", "pbSpace"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
73220
+ samplers: ["skyViewLut", "cloudTex", "spaceCube"],
72914
73221
  defines: defines,
72915
73222
  shaderLanguage: language
72916
73223
  });
72917
73224
  material.backFaceCulling = false;
72918
73225
  material.disableDepthWrite = true;
72919
73226
  material.setTexture("skyViewLut", this.skyViewLut);
73227
+ if (this.spaceCube != null) {
73228
+ material.setTexture("spaceCube", this.spaceCube);
73229
+ material.setVector4("pbSpace", this.spaceEmissionUniform());
73230
+ }
72920
73231
  if (layers.length > 0) {
72921
73232
  for (let i = 0; i < layers.length; i++)
72922
73233
  this.cloudMaps.push(this.loadCloudMap(layers[i]));
@@ -73055,7 +73366,7 @@ export class HdrpPhysicallyBasedSky {
73055
73366
  let exposure = 1;
73056
73367
  const body = new Array(HdrpPbSkyShaders.BodyVec4s * HdrpPbSkyShaders.MaxBodies * 4).fill(0);
73057
73368
  const peSafe = (pe > 0) ? pe : 1;
73058
- const lux = (l) => l.intensity / peSafe;
73369
+ const lux = (l) => (l.intensity * this.sunIntensityScale) / peSafe;
73059
73370
  const sunFor = (self) => {
73060
73371
  let best = null, max = 0;
73061
73372
  for (const l of this.scene.lights)
@@ -73128,7 +73439,7 @@ export class HdrpPhysicallyBasedSky {
73128
73439
  U.pbHorizon = [horizon[0], horizon[1], horizon[2], (typeof p.colorsaturation === "number") ? p.colorsaturation : 1];
73129
73440
  U.pbZenith = [zenith[0], zenith[1], zenith[2], (typeof p.alphasaturation === "number") ? p.alphasaturation : 1];
73130
73441
  U.pbArt = [(typeof p.alphamultiplier === "number") ? p.alphamultiplier : 1, exp[0], exp[1], pe];
73131
- U.pbCounts = [lightCount, lights.length, 1, 0];
73442
+ U.pbCounts = [lightCount, lights.length, 1, this.encodesForDisplay() ? 1 : 0];
73132
73443
  U.pbBody = body;
73133
73444
  this.writeCloudUniforms(lights);
73134
73445
  }
@@ -73163,14 +73474,16 @@ export class HdrpPhysicallyBasedSky {
73163
73474
  }
73164
73475
  }
73165
73476
  cloudAmbient() {
73166
- const down = new Vector3(0, -1, 0);
73477
+ return this.ambientAt(new Vector3(0, -1, 0));
73478
+ }
73479
+ ambientAt(direction) {
73167
73480
  if (this.ambientPolynomial != null)
73168
- return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial, down);
73481
+ return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial, direction);
73169
73482
  if (this.exportedHarmonics == null)
73170
73483
  this.exportedHarmonics = this.parseExportedAmbient();
73171
73484
  if (this.exportedHarmonics == null)
73172
73485
  return new Color3(0, 0, 0);
73173
- return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1), down);
73486
+ return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1), direction);
73174
73487
  }
73175
73488
  cloudLayers() {
73176
73489
  const cl = this.sky.cloudlayer;
@@ -73178,6 +73491,22 @@ export class HdrpPhysicallyBasedSky {
73178
73491
  return [];
73179
73492
  return cl.layerlist.filter((l) => l != null && l.cloudmap != null && typeof l.cloudmap.url === "string").slice(0, 2);
73180
73493
  }
73494
+ loadSpaceEmission() {
73495
+ const url = (this.sky.params != null) ? this.sky.params.spaceemissionurl : null;
73496
+ if (typeof url !== "string")
73497
+ return null;
73498
+ const root = (typeof this.options.root === "string") ? this.options.root : "";
73499
+ const cube = new CubeTexture(root + url, this.scene, null, false, null, null, null, Constants.TEXTUREFORMAT_RGBA, false, ".env");
73500
+ cube.name = "HDRP PBSky Space Emission";
73501
+ return cube;
73502
+ }
73503
+ spaceEmissionUniform() {
73504
+ const params = this.sky.params;
73505
+ const rotation = Array.isArray(params.spacerotation) ? params.spacerotation : [0, 0, 0];
73506
+ const radians = Tools.ToRadians(rotation[1]);
73507
+ const multiplier = (typeof params.spaceemissionmultiplier === "number") ? params.spaceemissionmultiplier : 1;
73508
+ return new Vector4(multiplier, Math.cos(radians), Math.sin(radians), this.scene.useRightHandedSystem ? -1 : 1);
73509
+ }
73181
73510
  loadCloudMap(layer) {
73182
73511
  const url = ((typeof this.options.root === "string") ? this.options.root : "") + layer.cloudmap.url;
73183
73512
  const tex = new Texture(url, this.scene, true, true, Texture.BILINEAR_SAMPLINGMODE);
@@ -73209,7 +73538,10 @@ export class HdrpPhysicallyBasedSky {
73209
73538
  this.applyMaterialUniforms();
73210
73539
  return;
73211
73540
  }
73212
- this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0, counts[3]));
73541
+ this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0, 0));
73542
+ }
73543
+ encodesForDisplay() {
73544
+ return HdrpRendering.IsParity(this.scene) !== true && this.scene.imageProcessingConfiguration.applyByPostProcess !== true;
73213
73545
  }
73214
73546
  applyMaterialUniforms() {
73215
73547
  const m = this.material;
@@ -73238,7 +73570,7 @@ export class HdrpPhysicallyBasedSky {
73238
73570
  const p = this.sky.params || {};
73239
73571
  if (p.renderingmode === 1)
73240
73572
  LightingConversions.WarnOnce("hdrp:pbsky:material", "HDRP PhysicallyBasedSky: the Material rendering mode (a Shader Graph sky) renders the default live atmosphere");
73241
- if (p.hasgroundtextures === true || p.hasspaceemission === true)
73573
+ if (p.hasgroundtextures === true || (p.hasspaceemission === true && typeof p.spaceemissionurl !== "string"))
73242
73574
  LightingConversions.WarnOnce("hdrp:pbsky:textures", "HDRP PhysicallyBasedSky: ground / space emission textures are not rendered");
73243
73575
  for (const layer of this.cloudLayers())
73244
73576
  if (typeof layer.distortion === "number" && layer.distortion !== 0)
@@ -73271,20 +73603,28 @@ export class HdrpPhysicallyBasedSky {
73271
73603
  scene.customRenderTargets.push(probe.cubeTexture);
73272
73604
  const p = this.sky.params || {};
73273
73605
  probe.cubeTexture.onBeforeRenderObservable.add(() => {
73274
- if (this.capturing !== true)
73275
- return;
73606
+ const subMesh = (this.mesh.subMeshes != null) ? this.mesh.subMeshes[0] : null;
73607
+ this.probeDrawsSky = (subMesh != null) ? this.material.isReadyForSubMesh(this.mesh, subMesh) : this.material.isReady(this.mesh);
73276
73608
  const art = this.uniforms.pbArt, counts = this.uniforms.pbCounts;
73277
73609
  if (art != null)
73278
73610
  this.material.setVector4("pbArt", new Vector4(art[0], art[1], art[2], 1));
73279
73611
  if (counts != null)
73280
- this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0, counts[3]));
73612
+ this.material.setVector4("pbCounts", new Vector4(counts[0], counts[1], (this.sky.includesuninbaking === true) ? 1 : 0, 0));
73281
73613
  });
73282
73614
  probe.cubeTexture.onAfterRenderObservable.add((faceIndex) => {
73283
- if (this.capturing !== true || (typeof faceIndex === "number" && faceIndex < 5))
73615
+ if (typeof faceIndex === "number" && faceIndex < 5)
73284
73616
  return;
73285
- this.capturing = false;
73286
73617
  this.applyMaterialUniforms();
73287
- this.pendingFilter = true;
73618
+ const state = HdrpPhysicallyBasedSky.ProbeRenderOutcome(this.capturing, this.pendingFilter, this.probeDrawsSky);
73619
+ if (state === HdrpPhysicallyBasedSky.ProbeCaptureAccepted) {
73620
+ this.capturing = false;
73621
+ this.pendingFilter = true;
73622
+ }
73623
+ else if (state === HdrpPhysicallyBasedSky.ProbeCaptureRetry) {
73624
+ this.capturing = true;
73625
+ this.pendingFilter = false;
73626
+ probe.cubeTexture.resetRefreshCounter();
73627
+ }
73288
73628
  });
73289
73629
  this.exportedHarmonics = this.parseExportedAmbient();
73290
73630
  }
@@ -73487,6 +73827,8 @@ export class HdrpPhysicallyBasedSky {
73487
73827
  this.apTarget = engine.createRenderTargetTexture({ width: size[0], height: size[1], layers: size[2] }, { generateMipMaps: false, type: this.textureType, samplingMode: Constants.TEXTURE_BILINEAR_SAMPLINGMODE, format: Constants.TEXTUREFORMAT_RGBA, generateDepthBuffer: false, generateStencilBuffer: false, label: "HDRP PBSky Aerial Perspective" });
73488
73828
  this.apTexture = new BaseTexture(scene, this.apTarget.texture);
73489
73829
  this.apTexture.name = "HDRP PBSky Aerial Perspective";
73830
+ this.apTexture.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
73831
+ this.apTexture.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
73490
73832
  LightingConversions.WarnOnce("hdrp:pbsky:aerialshadow", "HDRP PhysicallyBasedSky: the aerial perspective has no shadowed in-scatter (the sun shadow map is not sampled)");
73491
73833
  }
73492
73834
  if (!this.apPass.isReady())
@@ -73575,7 +73917,7 @@ export class HdrpPhysicallyBasedSky {
73575
73917
  catch (e) { }
73576
73918
  for (const light of this.attenuated)
73577
73919
  HdrpPhysicallyBasedSky.RestoreLight(light);
73578
- const disposables = [this.msPass, this.viewPass, this.cloudPass, this.renderer, this.msLut, this.skyViewLut, this.cloudTex, this.material, this.mesh].concat(this.cloudMaps);
73920
+ const disposables = [this.msPass, this.viewPass, this.cloudPass, this.renderer, this.msLut, this.skyViewLut, this.cloudTex, this.spaceCube, this.material, this.mesh].concat(this.cloudMaps);
73579
73921
  for (const d of disposables) {
73580
73922
  try {
73581
73923
  if (d != null)
@@ -73594,6 +73936,9 @@ HdrpPhysicallyBasedSky.AerialPerspectiveSize = [32, 32, 64];
73594
73936
  HdrpPhysicallyBasedSky.AerialMaxDistance = 128000;
73595
73937
  HdrpPhysicallyBasedSky.MeshName = "HDRP Physically Based Sky";
73596
73938
  HdrpPhysicallyBasedSky.OnEnvironmentUpdated = new Observable();
73939
+ HdrpPhysicallyBasedSky.ProbeRenderKept = 0;
73940
+ HdrpPhysicallyBasedSky.ProbeCaptureAccepted = 1;
73941
+ HdrpPhysicallyBasedSky.ProbeCaptureRetry = 2;
73597
73942
  HdrpPhysicallyBasedSky.RealtimeMinFrames = 9;
73598
73943
  HdrpPhysicallyBasedSky.ShProjectionFaceSize = 64;
73599
73944
  HdrpPhysicallyBasedSky.CaptureFaceCount = 6;
@@ -91394,6 +91739,7 @@ export class LightProbeNetwork extends ScriptComponent {
91394
91739
  }
91395
91740
  if (this._dynCount === 0)
91396
91741
  return;
91742
+ this.dropDisposedEntries();
91397
91743
  this._walker.maxWalkSteps = LightProbeNetwork.MaxWalkSteps;
91398
91744
  for (let i = 0; i < this._dynCount; i++)
91399
91745
  this.sampleDynamic(i);
@@ -91556,10 +91902,10 @@ export class LightProbeNetwork extends ScriptComponent {
91556
91902
  const meshState = LightProbeNetwork.MeshState(mesh);
91557
91903
  meshState._tkLightProbeOwner = this;
91558
91904
  if (mesh.onDisposeObservable != null && meshState._tkLightProbeDisposeObserver == null) {
91559
- meshState._tkLightProbeDisposeObserver = mesh.onDisposeObservable.addOnce(() => {
91905
+ meshState._tkLightProbeDisposeObserver = mesh.onDisposeObservable.add(() => {
91560
91906
  meshState._tkLightProbeDisposeObserver = null;
91561
- this.detach(mesh);
91562
- });
91907
+ this.detachDisposed(mesh);
91908
+ }, undefined, true, undefined, true);
91563
91909
  }
91564
91910
  return true;
91565
91911
  }
@@ -91615,6 +91961,16 @@ export class LightProbeNetwork extends ScriptComponent {
91615
91961
  return;
91616
91962
  LightProbeNetwork.SetRider(mesh, toolkitMetadata, null);
91617
91963
  toolkitMetadata.lightProbeOcclusion = null;
91964
+ toolkitMetadata.lightProbeSlot = -1;
91965
+ this.releaseMesh(mesh, registryIndex);
91966
+ }
91967
+ detachDisposed(mesh) {
91968
+ this.detach(mesh);
91969
+ if (LightProbeNetwork.MeshState(mesh)._tkLightProbeOwner !== this)
91970
+ return;
91971
+ this.releaseMesh(mesh, this._dynMeshes.indexOf(mesh));
91972
+ }
91973
+ releaseMesh(mesh, registryIndex) {
91618
91974
  if (this._noEnvironment === true)
91619
91975
  LightProbeAmbient.ExcludeFromAmbient(this.scene, mesh, false);
91620
91976
  for (let lightIndex = 0; lightIndex < this._bakedLights.length; lightIndex++) {
@@ -91623,26 +91979,11 @@ export class LightProbeNetwork extends ScriptComponent {
91623
91979
  if (excludedIndex >= 0)
91624
91980
  excluded.splice(excludedIndex, 1);
91625
91981
  }
91626
- if (registryIndex >= 0) {
91627
- const lastIndex = this._dynCount - 1;
91628
- if (registryIndex !== lastIndex) {
91629
- const moved = this._dynMeshes[lastIndex];
91630
- this._dynMeshes[registryIndex] = moved;
91631
- this._dynTet[registryIndex] = this._dynTet[lastIndex];
91632
- this._dynHasAnchor[registryIndex] = this._dynHasAnchor[lastIndex];
91633
- for (let axis = 0; axis < 3; axis++) {
91634
- this._dynLast[registryIndex * 3 + axis] = this._dynLast[lastIndex * 3 + axis];
91635
- this._dynAnchor[registryIndex * 3 + axis] = this._dynAnchor[lastIndex * 3 + axis];
91636
- }
91637
- moved.metadata.toolkit.lightProbeSlot = registryIndex;
91638
- }
91639
- this._dynMeshes[lastIndex] = null;
91640
- this._dynCount--;
91641
- }
91642
- else {
91982
+ if (registryIndex >= 0)
91983
+ this.removeDynamicEntry(registryIndex);
91984
+ else
91643
91985
  this._staticCount--;
91644
- }
91645
- toolkitMetadata.lightProbeSlot = -1;
91986
+ const meshState = LightProbeNetwork.MeshState(mesh);
91646
91987
  meshState._tkLightProbeOwner = null;
91647
91988
  if (meshState._tkLightProbeDisposeObserver != null) {
91648
91989
  if (mesh.onDisposeObservable != null)
@@ -91650,6 +91991,33 @@ export class LightProbeNetwork extends ScriptComponent {
91650
91991
  meshState._tkLightProbeDisposeObserver = null;
91651
91992
  }
91652
91993
  }
91994
+ removeDynamicEntry(registryIndex) {
91995
+ const lastIndex = this._dynCount - 1;
91996
+ if (registryIndex !== lastIndex) {
91997
+ const moved = this._dynMeshes[lastIndex];
91998
+ this._dynMeshes[registryIndex] = moved;
91999
+ this._dynTet[registryIndex] = this._dynTet[lastIndex];
92000
+ this._dynHasAnchor[registryIndex] = this._dynHasAnchor[lastIndex];
92001
+ for (let axis = 0; axis < 3; axis++) {
92002
+ this._dynLast[registryIndex * 3 + axis] = this._dynLast[lastIndex * 3 + axis];
92003
+ this._dynAnchor[registryIndex * 3 + axis] = this._dynAnchor[lastIndex * 3 + axis];
92004
+ }
92005
+ const movedToolkit = (moved.metadata != null) ? moved.metadata.toolkit : null;
92006
+ if (movedToolkit != null)
92007
+ movedToolkit.lightProbeSlot = registryIndex;
92008
+ }
92009
+ this._dynMeshes[lastIndex] = null;
92010
+ this._dynCount--;
92011
+ }
92012
+ dropDisposedEntries() {
92013
+ for (let i = this._dynCount - 1; i >= 0; i--) {
92014
+ const mesh = this._dynMeshes[i];
92015
+ const hasToolkitMetadata = (mesh.metadata != null && mesh.metadata.toolkit != null);
92016
+ if (hasToolkitMetadata && !mesh.isDisposed())
92017
+ continue;
92018
+ this.releaseMesh(mesh, i);
92019
+ }
92020
+ }
91653
92021
  sampleMeshNow(mesh) {
91654
92022
  const tk = (mesh != null && mesh.metadata != null) ? mesh.metadata.toolkit : null;
91655
92023
  if (tk == null || tk.lightProbeSH == null)
@@ -91904,6 +92272,238 @@ LightProbeNetwork.DebugProbeSize = 0.1;
91904
92272
  LightProbeNetwork.InspectorSection = true;
91905
92273
  LightProbeNetwork.OnNetworkChangedObservable = new Observable();
91906
92274
  LightProbeNetwork.Sections = ["positions", "probesh", "tetraindices", "tetraadjacency", "renderersh", "rendererocclusion", "rendereranchor"];
92275
+ export class MeshWaterSurface extends ScriptComponent {
92276
+ constructor(transform, scene, properties = {}, alias = "TOOLKIT.MeshWaterSurface") {
92277
+ super(transform, scene, properties, alias);
92278
+ this.waterProperties = null;
92279
+ this.isUnderwater = false;
92280
+ this.fogColorChannels = [0, 0, 0];
92281
+ this.surfaceFogColor = new Color3(0, 0, 0);
92282
+ this.surfaceFogDensity = 0;
92283
+ this.surfaceFogStart = 0;
92284
+ this.surfaceFogEnd = 0;
92285
+ this.shownNodes = [];
92286
+ this.shownParticles = [];
92287
+ this.volumeNodes = [];
92288
+ this.flippedNodes = [];
92289
+ this.followers = [];
92290
+ this.followPosition = new Vector3(0, 0, 0);
92291
+ }
92292
+ static Get(scene) {
92293
+ if (scene == null)
92294
+ return null;
92295
+ return MeshWaterSurface.Registry.get(scene.uniqueId) ?? null;
92296
+ }
92297
+ awake() {
92298
+ this.waterProperties = this.getProperties();
92299
+ if (MeshWaterSurface.Registry.has(this.scene.uniqueId)) {
92300
+ Tools.Warn("MeshWaterSurface: '" + this.transform.name + "' replaces the scene's first water surface");
92301
+ }
92302
+ MeshWaterSurface.Registry.set(this.scene.uniqueId, this);
92303
+ }
92304
+ ready() {
92305
+ const properties = this.getProperties();
92306
+ if (properties.underwater == null)
92307
+ return;
92308
+ for (const node of this.findNodes(properties.underwater.shownodes)) {
92309
+ for (const component of SceneManager.GetComponents(node) ?? [])
92310
+ ScriptComponent.AwakeInstance(component);
92311
+ }
92312
+ }
92313
+ start() {
92314
+ const underwater = this.waterProperties.underwater;
92315
+ if (underwater == null)
92316
+ return;
92317
+ this.shownNodes = this.findNodes(underwater.shownodes);
92318
+ this.shownParticles = this.findParticles(this.shownNodes);
92319
+ this.volumeNodes = this.findNodes(underwater.volumenodes);
92320
+ this.flippedNodes = this.findNodes(underwater.flipnodes).map((node) => this.prepareFlip(node));
92321
+ this.followers = this.prepareFollowers(underwater.follownodes);
92322
+ this.update();
92323
+ }
92324
+ update() {
92325
+ if (this.waterProperties == null || this.waterProperties.underwater == null)
92326
+ return;
92327
+ const camera = this.scene.activeCamera;
92328
+ if (camera == null)
92329
+ return;
92330
+ const isCameraUnder = this.isCameraUnderwater(camera);
92331
+ if (isCameraUnder !== this.isUnderwater) {
92332
+ this.isUnderwater = isCameraUnder;
92333
+ if (isCameraUnder)
92334
+ this.enterUnderwater();
92335
+ else
92336
+ this.leaveUnderwater();
92337
+ }
92338
+ if (!isCameraUnder)
92339
+ return;
92340
+ this.updateFog(this.waterProperties.underwater.sealevel - camera.globalPosition.y);
92341
+ this.updateFollowers(camera);
92342
+ }
92343
+ destroy() {
92344
+ if (this.isUnderwater)
92345
+ this.leaveUnderwater();
92346
+ this.isUnderwater = false;
92347
+ if (MeshWaterSurface.Registry.get(this.scene.uniqueId) === this)
92348
+ MeshWaterSurface.Registry.delete(this.scene.uniqueId);
92349
+ }
92350
+ getSeaLevel() {
92351
+ return this.waterProperties.sealevel;
92352
+ }
92353
+ getSurfaceHeight() {
92354
+ const properties = this.waterProperties;
92355
+ const clockSeconds = ShaderGlobals.GetClock(this.scene).time;
92356
+ return properties.sealevel + Math.sin(clockSeconds * properties.wavespeed) * properties.waveamplitude;
92357
+ }
92358
+ getCameraHeight(camera) {
92359
+ if (camera == null)
92360
+ return 0;
92361
+ return camera.globalPosition.y - this.getSurfaceHeight();
92362
+ }
92363
+ isCameraUnderwater(camera) {
92364
+ if (camera == null)
92365
+ return false;
92366
+ const underwater = this.waterProperties.underwater;
92367
+ const crossingHeight = (underwater != null) ? underwater.sealevel : this.getSurfaceHeight();
92368
+ return camera.globalPosition.y <= crossingHeight;
92369
+ }
92370
+ ProjectPointOnWaterSurface(searchParameters) {
92371
+ if (searchParameters == null || searchParameters.targetPositionWS == null)
92372
+ return null;
92373
+ const target = searchParameters.targetPositionWS;
92374
+ const targetX = Array.isArray(target) ? target[0] : target.x;
92375
+ const targetZ = Array.isArray(target) ? target[2] : target.z;
92376
+ const projectedPosition = new Vector3(targetX, this.getSurfaceHeight(), targetZ);
92377
+ return {
92378
+ projectedPositionWS: projectedPosition,
92379
+ normalWS: Vector3.Up(),
92380
+ candidateLocationWS: projectedPosition.clone(),
92381
+ currentDirectionWS: Vector3.Zero(),
92382
+ numIterations: 1,
92383
+ error: 0
92384
+ };
92385
+ }
92386
+ FindWaterSurfaceHeight(searchParameters) {
92387
+ return this.ProjectPointOnWaterSurface(searchParameters);
92388
+ }
92389
+ findNodes(nodeIds) {
92390
+ const nodes = [];
92391
+ for (const nodeId of nodeIds ?? []) {
92392
+ const node = SceneManager.GetTransformNodeByID(this.scene, nodeId);
92393
+ if (node != null)
92394
+ nodes.push(node);
92395
+ }
92396
+ return nodes;
92397
+ }
92398
+ findParticles(nodes) {
92399
+ const particles = [];
92400
+ for (const node of nodes) {
92401
+ const system = SceneManager.GetComponent(node, MeshWaterSurface.PARTICLES_CLASS);
92402
+ if (system != null)
92403
+ particles.push(system);
92404
+ }
92405
+ return particles;
92406
+ }
92407
+ prepareFlip(node) {
92408
+ if (node.isWorldMatrixFrozen)
92409
+ node.unfreezeWorldMatrix();
92410
+ if (node.rotationQuaternion == null)
92411
+ node.rotationQuaternion = Quaternion.FromEulerVector(node.rotation);
92412
+ const surfaceRotation = node.rotationQuaternion.clone();
92413
+ const flipRotation = Quaternion.FromEulerAngles(Math.PI, 0, 0);
92414
+ return { node: node, surfaceRotation: surfaceRotation, underwaterRotation: flipRotation.multiply(surfaceRotation) };
92415
+ }
92416
+ prepareFollowers(follownodes) {
92417
+ const followers = [];
92418
+ for (const follower of follownodes ?? []) {
92419
+ const node = SceneManager.GetTransformNodeByID(this.scene, follower.node);
92420
+ if (node == null)
92421
+ continue;
92422
+ if (follower.scale != null)
92423
+ node.scaling.set(follower.scale[0], follower.scale[1], follower.scale[2]);
92424
+ followers.push({ node: node, offset: Vector3.FromArray(follower.offset) });
92425
+ }
92426
+ return followers;
92427
+ }
92428
+ enterUnderwater() {
92429
+ const underwater = this.waterProperties.underwater;
92430
+ if (underwater.fogcolors.length > 0)
92431
+ this.writeUnderwaterFog(underwater);
92432
+ for (const node of this.shownNodes)
92433
+ node.setEnabled(true);
92434
+ for (const particles of this.shownParticles)
92435
+ particles.play();
92436
+ this.setVolumesEnabled(true);
92437
+ for (const flipped of this.flippedNodes)
92438
+ flipped.node.rotationQuaternion.copyFrom(flipped.underwaterRotation);
92439
+ }
92440
+ leaveUnderwater() {
92441
+ if (this.waterProperties.underwater.fogcolors.length > 0)
92442
+ this.restoreSurfaceFog();
92443
+ for (const particles of this.shownParticles)
92444
+ particles.stop(true, MeshWaterSurface.STOP_AND_CLEAR);
92445
+ for (const node of this.shownNodes)
92446
+ node.setEnabled(false);
92447
+ this.setVolumesEnabled(false);
92448
+ for (const flipped of this.flippedNodes)
92449
+ flipped.node.rotationQuaternion.copyFrom(flipped.surfaceRotation);
92450
+ }
92451
+ writeUnderwaterFog(underwater) {
92452
+ const scene = this.scene;
92453
+ this.surfaceFogColor.copyFrom(scene.fogColor);
92454
+ this.surfaceFogDensity = scene.fogDensity;
92455
+ this.surfaceFogStart = scene.fogStart;
92456
+ this.surfaceFogEnd = scene.fogEnd;
92457
+ const isExponentialSquared = scene.fogMode === Scene.FOGMODE_EXP2;
92458
+ const densityScale = isExponentialSquared ? SceneManager.FogExp2DensityScale : SceneManager.FogExpDensityScale;
92459
+ scene.fogDensity = underwater.fogdensity * densityScale;
92460
+ scene.fogStart = underwater.fogstart;
92461
+ scene.fogEnd = underwater.fogend * MeshWaterSurface.LINEAR_FOG_END_SCALE;
92462
+ }
92463
+ restoreSurfaceFog() {
92464
+ const scene = this.scene;
92465
+ scene.fogColor.copyFrom(this.surfaceFogColor);
92466
+ scene.fogDensity = this.surfaceFogDensity;
92467
+ scene.fogStart = this.surfaceFogStart;
92468
+ scene.fogEnd = this.surfaceFogEnd;
92469
+ }
92470
+ setVolumesEnabled(enabled) {
92471
+ for (const node of this.volumeNodes) {
92472
+ node.setEnabled(enabled);
92473
+ const volume = SceneManager.GetComponent(node, MeshWaterSurface.POST_PROCESSOR_CLASS);
92474
+ if (volume != null)
92475
+ volume.SetVolumeEnabled(enabled);
92476
+ }
92477
+ }
92478
+ updateFog(depthMeters) {
92479
+ const underwater = this.waterProperties.underwater;
92480
+ if (underwater.fogcolors.length === 0)
92481
+ return;
92482
+ const depthFraction = Scalar.Clamp(depthMeters / underwater.fogdepth, 0, 1);
92483
+ const keyPosition = depthFraction * (underwater.fogcolors.length - 1);
92484
+ const lowerKey = Math.floor(keyPosition);
92485
+ const upperKey = Math.min(lowerKey + 1, underwater.fogcolors.length - 1);
92486
+ const keyBlend = keyPosition - lowerKey;
92487
+ for (let channel = 0; channel < 3; channel++) {
92488
+ const gradientValue = Scalar.Lerp(underwater.fogcolors[lowerKey][channel], underwater.fogcolors[upperKey][channel], keyBlend);
92489
+ const litValue = gradientValue * underwater.suncolor[channel];
92490
+ const addedValue = underwater.fogcolormultiplier[channel];
92491
+ this.fogColorChannels[channel] = (addedValue >= 0) ? Scalar.Clamp(litValue + addedValue, 0, 1) : litValue;
92492
+ }
92493
+ this.scene.fogColor.set(this.fogColorChannels[0], this.fogColorChannels[1], this.fogColorChannels[2]);
92494
+ }
92495
+ updateFollowers(camera) {
92496
+ for (const follower of this.followers) {
92497
+ camera.globalPosition.addToRef(follower.offset, this.followPosition);
92498
+ follower.node.setAbsolutePosition(this.followPosition);
92499
+ }
92500
+ }
92501
+ }
92502
+ MeshWaterSurface.POST_PROCESSOR_CLASS = "TOOLKIT.PostProcessor";
92503
+ MeshWaterSurface.PARTICLES_CLASS = "TOOLKIT.ShurikenParticles";
92504
+ MeshWaterSurface.STOP_AND_CLEAR = 1;
92505
+ MeshWaterSurface.LINEAR_FOG_END_SCALE = 2;
92506
+ MeshWaterSurface.Registry = new Map();
91907
92507
  export class NavigationAgent extends ScriptComponent {
91908
92508
  isReady() { return this.m_agentReady; }
91909
92509
  isNavigating() { return (this.m_navStatus === NavMoveStatus.Moving); }
@@ -108703,6 +109303,7 @@ export class PostProcessor extends ScriptComponent {
108703
109303
  const props = this.getProperties();
108704
109304
  this.cameraOwner = PostProcessingContract.isCameraOwner(props);
108705
109305
  this.volumeProperties = this.cameraOwner ? null : PostProcessingContract.parseVolume(props);
109306
+ this.volumeEnabled = !this.isSwitchedVolume();
108706
109307
  this.volumeLayer = SceneManager.GetTransformLayer(this.transform);
108707
109308
  PostProcessor.Registry.push(this);
108708
109309
  if (PostProcessor.GlobalInstance == null)
@@ -108939,7 +109540,8 @@ export class PostProcessor extends ScriptComponent {
108939
109540
  PostProcessor.warnOnce("cameras:" + selection.mode, selection.warning);
108940
109541
  const hasLocal = entries.some(e => e.properties.isglobal === false && e.properties.bounds != null);
108941
109542
  const hasCinemachine = volumes.some((volume) => volume.isCinemachineVolume());
108942
- const liveBlend = hasLocal || hasCinemachine;
109543
+ const hasSwitched = volumes.some((volume) => volume.isSwitchedVolume());
109544
+ const liveBlend = hasLocal || hasCinemachine || hasSwitched;
108943
109545
  const localLuts = liveBlend ? entries.map((e) => {
108944
109546
  const lut = PostProcessingContract.normalise(e.properties).lut;
108945
109547
  return (lut != null && lut.identity !== true && typeof lut.url === "string" && lut.url !== "") ? lut : null;
@@ -109034,6 +109636,9 @@ export class PostProcessor extends ScriptComponent {
109034
109636
  isCinemachineVolume() {
109035
109637
  return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.CINEMACHINE_VOLUME_ROLE;
109036
109638
  }
109639
+ isSwitchedVolume() {
109640
+ return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.SWITCHED_VOLUME_ROLE;
109641
+ }
109037
109642
  IsVolumeEnabled() {
109038
109643
  if (this.volumeEnabled === false)
109039
109644
  return false;
@@ -109648,7 +110253,7 @@ export class PostProcessor extends ScriptComponent {
109648
110253
  applyHdrpFog(camera, textureType) {
109649
110254
  if (camera == null || this.scene == null || HdrpRendering == null || HdrpFogPass == null)
109650
110255
  return;
109651
- if (HdrpRendering.IsParity(this.scene) !== true)
110256
+ if (HdrpRendering.IsParity(this.scene) !== true && PhysicalSkyBlock.UrpBlock(HdrpRendering.SceneMetadata(this.scene)) == null)
109652
110257
  return;
109653
110258
  const fog = HdrpFogPass.FogBlock(this.scene);
109654
110259
  if (!HdrpFogMath.PassNeeded(fog, HdrpFogPass.SkyOf(this.scene)))
@@ -115080,6 +115685,7 @@ export class PostProcessingContract {
115080
115685
  }
115081
115686
  PostProcessingContract.VOLUME_TYPES = ["BuiltIn", "URP", "HDRP"];
115082
115687
  PostProcessingContract.CINEMACHINE_VOLUME_ROLE = "cinemachine";
115688
+ PostProcessingContract.SWITCHED_VOLUME_ROLE = "switched";
115083
115689
  PostProcessingContract.GRADING_MODES = ["LowDefinitionRange", "HighDefinitionRange", "External"];
115084
115690
  PostProcessingContract.RENDERING_PATH_DEFERRED = "DeferredShading";
115085
115691
  PostProcessingContract.RENDERING_PATH_FORWARD = "Forward";
@@ -130843,6 +131449,20 @@ export class RigidbodyPhysics extends ScriptComponent {
130843
131449
  RigidbodyPhysics.ConfigRigidbodyPhysics(scene, entity, false, false, physics, mass, centerOfMass);
130844
131450
  return body;
130845
131451
  }
131452
+ static MarkCompoundChildForDisposal(entity) {
131453
+ if (RigidbodyPhysics.RendersItself(entity) === false) {
131454
+ Utilities.AddLoaderItemMarkedForDisposal(entity);
131455
+ return;
131456
+ }
131457
+ const colliderMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
131458
+ if (colliderMesh != null)
131459
+ Utilities.AddLoaderItemMarkedForDisposal(colliderMesh);
131460
+ }
131461
+ static RendersItself(node) {
131462
+ if (!(node instanceof AbstractMesh) || node.getTotalVertices() <= 0 || node.name.endsWith(".Collider"))
131463
+ return false;
131464
+ return node.metadata != null && node.metadata.toolkit != null && node.metadata.toolkit.renderer != null;
131465
+ }
130846
131466
  static CreateCompoundPhysicsShapeAndBody(scene, root, entity, element, impostortype, staticfriction, dynamicfriction, restitution, fcombine, rcombine, sitems, item, center, complex, trigger, persist, layer, filter) {
130847
131467
  let center_xx = 0;
130848
131468
  let center_yy = 0;
@@ -130853,7 +131473,7 @@ export class RigidbodyPhysics extends ScriptComponent {
130853
131473
  const rootScale = RigidbodyPhysics.GetColliderScale(root);
130854
131474
  const entityScale = (entity === root) ? rootScale : RigidbodyPhysics.RoundColliderScale(entity.scaling);
130855
131475
  if (persist === false && complex === true)
130856
- Utilities.AddLoaderItemMarkedForDisposal(entity);
131476
+ RigidbodyPhysics.MarkCompoundChildForDisposal(entity);
130857
131477
  if (impostortype == RigidbodyPhysics.ConvexHullImpostor) {
130858
131478
  let convexHullMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
130859
131479
  if (convexHullMesh != null) {
@@ -133691,7 +134311,7 @@ export class ShurikenParticles extends ScriptComponent {
133691
134311
  }
133692
134312
  canBeginWarmupDraw() {
133693
134313
  const particleSystem = this._prtSystem;
133694
- if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null || this._prtProps.enabled === false)
134314
+ if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null)
133695
134315
  return false;
133696
134316
  if (this._prtWarming || this._prtPrewarmJob != null)
133697
134317
  return false;
@@ -141523,6 +142143,7 @@ export class TerrainBuilder extends ScriptComponent {
141523
142143
  if (heights == null)
141524
142144
  throw new Error("heights block has no data");
141525
142145
  hf = new TerrainHeightfield(props, heights);
142146
+ hf.setEdgeNeighbors(this.resolveEdgeNeighbors());
141526
142147
  this._heightfield = hf;
141527
142148
  }
141528
142149
  catch (e) {
@@ -141715,6 +142336,45 @@ export class TerrainBuilder extends ScriptComponent {
141715
142336
  this._isBuilt = true;
141716
142337
  this.onBuiltObservable.notifyObservers(this);
141717
142338
  }
142339
+ resolveEdgeNeighbors() {
142340
+ const contract = this._contract;
142341
+ const tiles = TerrainBuilder.GetTerrains(this.scene);
142342
+ const ids = contract.neighbors;
142343
+ const hasIds = ids != null && (ids.left != null || ids.right != null || ids.top != null || ids.bottom != null);
142344
+ const neighbors = {
142345
+ left: this.edgeNeighborFor(tiles, hasIds ? ids.left : null, hasIds, -1, 0),
142346
+ right: this.edgeNeighborFor(tiles, hasIds ? ids.right : null, hasIds, 1, 0),
142347
+ top: this.edgeNeighborFor(tiles, hasIds ? ids.top : null, hasIds, 0, 1),
142348
+ bottom: this.edgeNeighborFor(tiles, hasIds ? ids.bottom : null, hasIds, 0, -1),
142349
+ };
142350
+ const anyNeighbor = neighbors.left != null || neighbors.right != null || neighbors.top != null || neighbors.bottom != null;
142351
+ return anyNeighbor ? neighbors : null;
142352
+ }
142353
+ edgeNeighborFor(tiles, neighborId, useIds, offsetX, offsetZ) {
142354
+ for (const other of tiles) {
142355
+ if (other === this || !this.canShareEdgeWith(other))
142356
+ continue;
142357
+ const isNeighbor = useIds ? other.transform.id === neighborId : this.isAdjacentTile(other, offsetX, offsetZ);
142358
+ if (!isNeighbor)
142359
+ continue;
142360
+ const heights = TerrainContract.GetBlock(other.contract, "heights", "u16");
142361
+ return (heights != null) ? { heights: heights, heightScale: other.contract.size[1] } : null;
142362
+ }
142363
+ return null;
142364
+ }
142365
+ canShareEdgeWith(other) {
142366
+ const contract = this._contract, otherContract = other.contract;
142367
+ return otherContract.heightresolution === contract.heightresolution && otherContract.size[0] === contract.size[0] && otherContract.size[2] === contract.size[2];
142368
+ }
142369
+ isAdjacentTile(other, offsetX, offsetZ) {
142370
+ const contract = this._contract, otherContract = other.contract;
142371
+ if (contract.allowautoconnect !== true || otherContract.allowautoconnect !== true || otherContract.groupingid !== contract.groupingid)
142372
+ return false;
142373
+ this.transform.computeWorldMatrix(true);
142374
+ const offset = this.worldToUnityUV(other.transform.getAbsolutePosition());
142375
+ const toleranceU = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[0], toleranceV = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[2];
142376
+ return Math.abs(offset[0] - offsetX) <= toleranceU && Math.abs(offset[1] - offsetZ) <= toleranceV;
142377
+ }
141718
142378
  applyTreesAndFoliageVisibility() {
141719
142379
  if (this._trees != null)
141720
142380
  this._trees.setVisible(this._drawTreesAndFoliage);
@@ -142299,12 +142959,21 @@ export class TerrainBuilder extends ScriptComponent {
142299
142959
  worldToUnityUV(worldPosition) {
142300
142960
  if (this._contract == null || worldPosition == null)
142301
142961
  return null;
142302
- const inv = Matrix.Invert(this.transform.getWorldMatrix());
142303
- const local = Vector3.TransformCoordinates(worldPosition, inv);
142962
+ this.transform.getWorldMatrix().invertToRef(TerrainBuilder.ScratchInverse);
142963
+ const local = TerrainBuilder.ScratchLocal;
142964
+ Vector3.TransformCoordinatesToRef(worldPosition, TerrainBuilder.ScratchInverse, local);
142304
142965
  const size = this._contract.size;
142305
142966
  const ux = TerrainMath.UnityToLocalX(local.x, this._contract.handedness);
142306
142967
  return [ux / size[0], local.z / size[2]];
142307
142968
  }
142969
+ containsWorldPosition(worldPosition, tolerance) {
142970
+ const uv = this.worldToUnityUV(worldPosition);
142971
+ if (uv == null)
142972
+ return false;
142973
+ const size = this._contract.size;
142974
+ const slackU = tolerance / size[0], slackV = tolerance / size[2];
142975
+ return uv[0] >= -slackU && uv[0] <= 1 + slackU && uv[1] >= -slackV && uv[1] <= 1 + slackV;
142976
+ }
142308
142977
  SampleHeight(worldPosition) {
142309
142978
  if (this._heightfield == null)
142310
142979
  return 0;
@@ -142373,6 +143042,10 @@ export class TerrainBuilder extends ScriptComponent {
142373
143042
  if (list[i]._heightfield != null && list[i].WorldToTerrainUV(worldPosition) != null)
142374
143043
  return list[i];
142375
143044
  }
143045
+ for (let i = list.length - 1; i >= 0; i--) {
143046
+ if (list[i]._heightfield != null && list[i].containsWorldPosition(worldPosition, TERRAIN_EDGE_TOLERANCE_METERS))
143047
+ return list[i];
143048
+ }
142376
143049
  return null;
142377
143050
  }
142378
143051
  static GetWorldHeightAt(scene, worldPosition) {
@@ -142390,6 +143063,8 @@ export class TerrainBuilder extends ScriptComponent {
142390
143063
  }
142391
143064
  }
142392
143065
  TerrainBuilder._registry = new Map();
143066
+ TerrainBuilder.ScratchInverse = new Matrix();
143067
+ TerrainBuilder.ScratchLocal = new Vector3();
142393
143068
  TerrainBuilder.OnLoadProgressObservable = new Observable();
142394
143069
  TerrainBuilder.PRELOAD_TIMEOUT_MS = 120000;
142395
143070
  TerrainBuilder.SETTLE_MS = 30000;
@@ -142810,7 +143485,7 @@ export class TerrainDetails {
142810
143485
  }
142811
143486
  const F = TerrainFoliagePlugin;
142812
143487
  const layer = rt.data;
142813
- const sway = layer.sway;
143488
+ const sway = F.GraphOwnsMotion(source) ? null : layer.sway;
142814
143489
  const urp = ((this._owner.contract != null && this._owner.contract.flavour) || "urp") === "urp";
142815
143490
  const modes = F.MODE_FADE | ((sway != null) ? F.MODE_SWAY : 0) | ((layer.rendermode === "Grass") ? F.MODE_GRASSWAVE : 0) | (urp ? F.MODE_URPSPECULAR : 0);
142816
143491
  const swayKey = (sway != null) ? (sway.amount + "," + sway.speed + "," + sway.wavelength) : "-";
@@ -143087,19 +143762,12 @@ export class TerrainDetails {
143087
143762
  rt.mainMatrices = matrices;
143088
143763
  rt.mainColors = colors;
143089
143764
  rt.main.thinInstanceSetBuffer("matrix", matrices, 16, false);
143090
- TerrainDetails.ResizePreviousMatrices(rt.main, matrices);
143765
+ TerrainTrees.ResizePreviousMatrices(rt.main, matrices);
143091
143766
  if (colors != null)
143092
143767
  rt.main.thinInstanceSetBuffer(TerrainDetails.TintBuffer(rt), colors, 4, false);
143093
143768
  if (ShaderGraphRuntime != null)
143094
143769
  ShaderGraphRuntime.EnsureThinInstanceBuffers(rt.main);
143095
143770
  }
143096
- static ResizePreviousMatrices(mesh, matrices) {
143097
- const storage = mesh._thinInstanceDataStorage;
143098
- if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
143099
- return;
143100
- storage.previousMatrixBuffer.dispose();
143101
- storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
143102
- }
143103
143771
  static TintBuffer(rt) { return (rt.grass != null) ? "tkGrassTint" : "color"; }
143104
143772
  chunkDistance(c, x, z, size) {
143105
143773
  const x0 = c.cx * this._cw, x1 = Math.min((c.cx + 1) * this._cw, size[0]);
@@ -143693,6 +144361,9 @@ export class TerrainFoliagePlugin extends MaterialPluginBase {
143693
144361
  const shader = m.getShaderName();
143694
144362
  return shader != null && shader !== "UniversalShaderMaterial" && shader !== "CustomShaderMaterial";
143695
144363
  }
144364
+ static GraphOwnsMotion(material) {
144365
+ return TerrainFoliagePlugin.IsGraphMaterial(material) && material.__speedTreeWind == null;
144366
+ }
143696
144367
  getClassName() {
143697
144368
  return "TerrainFoliagePlugin";
143698
144369
  }
@@ -144865,6 +145536,7 @@ export class TerrainHeightfield {
144865
145536
  this._errors = null;
144866
145537
  this._minH = null;
144867
145538
  this._maxH = null;
145539
+ this.edgeNeighbors = null;
144868
145540
  this.R = contract.heightresolution;
144869
145541
  this.size = [contract.size[0], contract.size[1], contract.size[2]];
144870
145542
  this.handedness = (contract.handedness != null) ? contract.handedness : 1;
@@ -144893,9 +145565,12 @@ export class TerrainHeightfield {
144893
145565
  return this.heights[z * this.R + x] / 65535 * this.size[1];
144894
145566
  }
144895
145567
  normalAt(u, v, out) {
144896
- TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out);
145568
+ TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out, this.edgeNeighbors);
144897
145569
  out[0] *= (this.handedness === 0 ? -1 : 1);
144898
145570
  }
145571
+ setEdgeNeighbors(neighbors) {
145572
+ this.edgeNeighbors = neighbors;
145573
+ }
144899
145574
  setHoles(pixels, width) {
144900
145575
  const n = this.R - 1;
144901
145576
  const height = (width > 0) ? Math.floor(pixels.length / 4 / width) : 0;
@@ -145174,15 +145849,31 @@ export class TerrainMath {
145174
145849
  const b = h01 + (h11 - h01) * tx;
145175
145850
  return TerrainMath.DecodeHeight(a + (b - a) * tz, sizeY);
145176
145851
  }
145177
- static SampleNormal(h, R, size, u, v, out) {
145852
+ static SampleNormal(h, R, size, u, v, out, neighbors = null) {
145178
145853
  const i = Math.round(TerrainMath.Clamp01(u) * (R - 1));
145179
145854
  const k = Math.round(TerrainMath.Clamp01(v) * (R - 1));
145180
145855
  const iL = Math.max(i - 1, 0), iR = Math.min(i + 1, R - 1);
145181
145856
  const kD = Math.max(k - 1, 0), kU = Math.min(k + 1, R - 1);
145182
145857
  const cellX = size[0] / (R - 1), cellZ = size[2] / (R - 1);
145183
145858
  const sy = size[1];
145184
- const dhdx = (iR > iL) ? (TerrainMath.DecodeHeight(h[k * R + iR], sy) - TerrainMath.DecodeHeight(h[k * R + iL], sy)) / ((iR - iL) * cellX) : 0;
145185
- const dhdz = (kU > kD) ? (TerrainMath.DecodeHeight(h[kU * R + i], sy) - TerrainMath.DecodeHeight(h[kD * R + i], sy)) / ((kU - kD) * cellZ) : 0;
145859
+ let dhdx = (iR > iL) ? (TerrainMath.DecodeHeight(h[k * R + iR], sy) - TerrainMath.DecodeHeight(h[k * R + iL], sy)) / ((iR - iL) * cellX) : 0;
145860
+ let dhdz = (kU > kD) ? (TerrainMath.DecodeHeight(h[kU * R + i], sy) - TerrainMath.DecodeHeight(h[kD * R + i], sy)) / ((kU - kD) * cellZ) : 0;
145861
+ if (i === 0 && neighbors?.left != null) {
145862
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.left.heights[k * R + (R - 2)], neighbors.left.heightScale);
145863
+ dhdx = (TerrainMath.DecodeHeight(h[k * R + 1], sy) - outsideHeight) / (2 * cellX);
145864
+ }
145865
+ else if (i === R - 1 && neighbors?.right != null) {
145866
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.right.heights[k * R + 1], neighbors.right.heightScale);
145867
+ dhdx = (outsideHeight - TerrainMath.DecodeHeight(h[k * R + (R - 2)], sy)) / (2 * cellX);
145868
+ }
145869
+ if (k === 0 && neighbors?.bottom != null) {
145870
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.bottom.heights[(R - 2) * R + i], neighbors.bottom.heightScale);
145871
+ dhdz = (TerrainMath.DecodeHeight(h[1 * R + i], sy) - outsideHeight) / (2 * cellZ);
145872
+ }
145873
+ else if (k === R - 1 && neighbors?.top != null) {
145874
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.top.heights[1 * R + i], neighbors.top.heightScale);
145875
+ dhdz = (outsideHeight - TerrainMath.DecodeHeight(h[(R - 2) * R + i], sy)) / (2 * cellZ);
145876
+ }
145186
145877
  const nx = -dhdx, ny = 1, nz = -dhdz;
145187
145878
  const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
145188
145879
  out[0] = nx / len;
@@ -146441,6 +147132,7 @@ export class TerrainTrees {
146441
147132
  this._colliderShapes = [];
146442
147133
  this._distance = 2000;
146443
147134
  this._built = false;
147135
+ this._warnedUniqueHost = false;
146444
147136
  this._n = 0;
146445
147137
  this._proto = null;
146446
147138
  this._local = null;
@@ -146533,13 +147225,21 @@ export class TerrainTrees {
146533
147225
  return casters;
146534
147226
  }
146535
147227
  getStats() {
146536
- let drawn = 0, visibleHosts = 0;
147228
+ let drawn = 0, visibleHosts = 0, capacity = 0;
146537
147229
  for (let i = 0; i < this._hosts.length; i++) {
146538
- drawn += this._hosts[i].count;
146539
- if (this._hosts[i].count > 0)
147230
+ const host = this._hosts[i];
147231
+ drawn += host.count;
147232
+ if (host.count > 0)
146540
147233
  visibleHosts++;
147234
+ capacity += host.matrices.length / 16;
147235
+ for (let slotIndex = 0; slotIndex < host.slots.length; slotIndex++)
147236
+ capacity += host.slots[slotIndex].matrices.length / 16;
146541
147237
  }
146542
- return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null };
147238
+ return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null, capacity: capacity };
147239
+ }
147240
+ static PreparedHostCount(scene) {
147241
+ const prepared = TerrainTrees.PreparedHosts.get(scene.uniqueId);
147242
+ return (prepared != null) ? prepared.size : 0;
146543
147243
  }
146544
147244
  build() {
146545
147245
  const owner = this._owner;
@@ -146597,43 +147297,44 @@ export class TerrainTrees {
146597
147297
  this._built = true;
146598
147298
  this._dirty = true;
146599
147299
  }
146600
- buildPrototype(p, tpl, cap) {
146601
- const lods = Array.isArray(p.lods) ? p.lods : [];
146602
- const runtime = { data: p, heights: lods.map((l) => (l != null && l.height != null) ? l.height : 0), lodHosts: [], fade: (p.fademode > 0 && p.animatecrossfade === true) };
146603
- const tplWorldInv = Matrix.Invert(tpl.computeWorldMatrix(true));
146604
- let any = false;
146605
- for (let l = 0; l < lods.length; l++) {
146606
- const list = [];
146607
- runtime.lodHosts.push(list);
146608
- const renderers = (lods[l] != null && Array.isArray(lods[l].renderers)) ? lods[l].renderers : [];
146609
- for (let r = 0; r < renderers.length; r++) {
146610
- const node = this._scene.getNodeById(renderers[r]);
147300
+ buildPrototype(prototype, template, instanceCount) {
147301
+ const prototypeLods = Array.isArray(prototype.lods) ? prototype.lods : [];
147302
+ const runtime = { data: prototype, heights: prototypeLods.map((prototypeLod) => (prototypeLod != null && prototypeLod.height != null) ? prototypeLod.height : 0), lodHosts: [], fade: (prototype.fademode > 0 && prototype.animatecrossfade === true) };
147303
+ const templateWorldInverse = Matrix.Invert(template.computeWorldMatrix(true));
147304
+ const initialCapacity = Math.min(instanceCount, TREE_INITIAL_CAPACITY);
147305
+ let hasHost = false;
147306
+ for (let lodIndex = 0; lodIndex < prototypeLods.length; lodIndex++) {
147307
+ const lodHostIndices = [];
147308
+ runtime.lodHosts.push(lodHostIndices);
147309
+ const renderers = (prototypeLods[lodIndex] != null && Array.isArray(prototypeLods[lodIndex].renderers)) ? prototypeLods[lodIndex].renderers : [];
147310
+ for (let rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
147311
+ const node = this._scene.getNodeById(renderers[rendererIndex]);
146611
147312
  if (node == null)
146612
147313
  continue;
146613
147314
  let sources = [];
146614
147315
  if (node instanceof AbstractMesh && TerrainTrees.HasGeometry(node))
146615
147316
  sources = [node];
146616
147317
  else
146617
- sources = node.getChildMeshes(true).filter((m) => TerrainTrees.HasGeometry(m));
146618
- const flags = (lods[l] != null) ? lods[l].lightprobes : null;
146619
- const probeLit = TerrainTrees.RendererUsesProbes(flags, r) && TerrainProbePlugin != null;
146620
- for (let s = 0; s < sources.length; s++) {
146621
- const matrices = new Float32Array(16 * cap), fades = new Float32Array(cap);
146622
- const cast = !(lods[l] != null && lods[l].billboard === true) && TerrainTrees.RendererCastsShadows(sources[s], node);
146623
- const host = this.createHost(p, l, sources[s], tplWorldInv, matrices, fades);
147318
+ sources = node.getChildMeshes(true).filter((mesh) => TerrainTrees.HasGeometry(mesh));
147319
+ const flags = (prototypeLods[lodIndex] != null) ? prototypeLods[lodIndex].lightprobes : null;
147320
+ const probeLit = TerrainTrees.RendererUsesProbes(flags, rendererIndex) && TerrainProbePlugin != null;
147321
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
147322
+ const matrices = new Float32Array(16 * initialCapacity), fades = new Float32Array(initialCapacity);
147323
+ const cast = !(prototypeLods[lodIndex] != null && prototypeLods[lodIndex].billboard === true) && TerrainTrees.RendererCastsShadows(sources[sourceIndex], node);
147324
+ const host = this.createHost(prototype, lodIndex, sources[sourceIndex], templateWorldInverse, matrices, fades);
146624
147325
  if (host == null)
146625
147326
  continue;
146626
147327
  if (probeLit)
146627
147328
  this._probeHosts++;
146628
- list.push(this._hosts.length);
146629
- this._hosts.push({ mesh: host, matrices: matrices, fades: fades, count: 0, lastCount: -1, cast: cast, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3(), probes: null, probeBuffer: null, probeLit: probeLit, slots: [] });
147329
+ lodHostIndices.push(this._hosts.length);
147330
+ this._hosts.push({ mesh: host, matrices: matrices, fades: fades, count: 0, lastCount: -1, cast: cast, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3(), probes: null, probeBuffer: null, probeLit: probeLit, slots: [], capacityCeiling: instanceCount, grown: false });
146630
147331
  if (cast)
146631
- this.createCasterSlots(this._hosts[this._hosts.length - 1], cap);
146632
- any = true;
147332
+ this.createCasterSlots(this._hosts[this._hosts.length - 1], initialCapacity);
147333
+ hasHost = true;
146633
147334
  }
146634
147335
  }
146635
147336
  }
146636
- return any ? runtime : null;
147337
+ return hasHost ? runtime : null;
146637
147338
  }
146638
147339
  static RendererUsesProbes(flags, index) {
146639
147340
  if (!Array.isArray(flags) || index < 0 || index >= flags.length)
@@ -146641,20 +147342,32 @@ export class TerrainTrees {
146641
147342
  return flags[index] !== false;
146642
147343
  }
146643
147344
  static AttachProbeBuffer(host, probes) {
146644
- const P = TerrainProbePlugin;
146645
- for (let o = 0; o < probes.length; o += P.STRIDE)
146646
- probes[o] = -1;
146647
- const buffer = new Buffer(host.getEngine(), probes, true, P.STRIDE, false, true);
146648
- for (let k = 0; k < 3; k++)
146649
- host.setVerticesBuffer(buffer.createVertexBuffer(P.ATTRIBUTES[k], 4 * k, 4, P.STRIDE, true));
146650
- host.onDisposeObservable.addOnce(() => { buffer.dispose(); });
147345
+ const probePlugin = TerrainProbePlugin;
147346
+ for (let recordOffset = 0; recordOffset < probes.length; recordOffset += probePlugin.STRIDE)
147347
+ probes[recordOffset] = -1;
147348
+ const buffer = TerrainTrees.CreateProbeBuffer(host, probes);
147349
+ TerrainTrees.ProbeBuffers.set(host, buffer);
147350
+ host.onDisposeObservable.addOnce(() => { TerrainTrees.ProbeBuffers.get(host).dispose(); });
146651
147351
  const material = host.material;
146652
- const subs = (material != null && material.subMaterials != null) ? material.subMaterials : [material];
146653
- for (let i = 0; i < subs.length; i++)
146654
- if (subs[i] != null)
146655
- P.Attach(subs[i]);
147352
+ const subMaterials = (material != null && material.subMaterials != null) ? material.subMaterials : [material];
147353
+ for (let i = 0; i < subMaterials.length; i++)
147354
+ if (subMaterials[i] != null)
147355
+ probePlugin.Attach(subMaterials[i]);
146656
147356
  return buffer;
146657
147357
  }
147358
+ static CreateProbeBuffer(host, probes) {
147359
+ const probePlugin = TerrainProbePlugin;
147360
+ const buffer = new Buffer(host.getEngine(), probes, true, probePlugin.STRIDE, false, true);
147361
+ for (let viewIndex = 0; viewIndex < 3; viewIndex++)
147362
+ host.setVerticesBuffer(buffer.createVertexBuffer(probePlugin.ATTRIBUTES[viewIndex], 4 * viewIndex, 4, probePlugin.STRIDE, true));
147363
+ return buffer;
147364
+ }
147365
+ static ResizeProbeBuffer(host) {
147366
+ const buffer = TerrainTrees.CreateProbeBuffer(host.mesh, host.probes);
147367
+ host.probeBuffer.dispose();
147368
+ host.probeBuffer = buffer;
147369
+ TerrainTrees.ProbeBuffers.set(host.mesh, buffer);
147370
+ }
146658
147371
  sampleProbes(network) {
146659
147372
  if (network == null || typeof network.sampleInto !== "function" || !TerrainTrees.NetworkLightsProbes(network))
146660
147373
  return false;
@@ -146810,31 +147523,85 @@ export class TerrainTrees {
146810
147523
  throw error;
146811
147524
  }
146812
147525
  }
146813
- createHost(p, lod, src, tplWorldInv, matrices, fades) {
146814
- const geo = (src instanceof InstancedMesh) ? src.sourceMesh : src;
146815
- const host = geo.clone(p.name + ".L" + lod + "." + src.name, null, true);
146816
- if (host == null)
147526
+ static BakeHostGeometry(source, templateWorldInverse, name) {
147527
+ const geometrySource = (source instanceof InstancedMesh) ? source.sourceMesh : source;
147528
+ const mesh = geometrySource.clone(name, null, true);
147529
+ if (mesh == null)
147530
+ return null;
147531
+ mesh.metadata = null;
147532
+ mesh.makeGeometryUnique();
147533
+ const rendererToTemplate = source.computeWorldMatrix(true).multiply(templateWorldInverse);
147534
+ mesh.position.setAll(0);
147535
+ mesh.rotationQuaternion = null;
147536
+ mesh.rotation.setAll(0);
147537
+ mesh.scaling.setAll(1);
147538
+ mesh.bakeTransformIntoVertices(rendererToTemplate);
147539
+ TerrainTrees.InterleaveVertexData(mesh);
147540
+ return mesh;
147541
+ }
147542
+ prepareHostGeometry(prototype, lod, source, templateWorldInverse) {
147543
+ const scene = this._scene;
147544
+ const sceneKey = scene.uniqueId;
147545
+ let prepared = TerrainTrees.PreparedHosts.get(sceneKey);
147546
+ if (prepared == null) {
147547
+ const hosts = new Map();
147548
+ prepared = hosts;
147549
+ TerrainTrees.PreparedHosts.set(sceneKey, hosts);
147550
+ scene.onDisposeObservable.addOnce(() => {
147551
+ TerrainTrees.PreparedHosts.delete(sceneKey);
147552
+ hosts.forEach((mesh) => { mesh.dispose(false, false); });
147553
+ });
147554
+ }
147555
+ const key = prototype.templateid + "|" + lod + "|" + source.uniqueId;
147556
+ const existing = prepared.get(key);
147557
+ if (existing != null)
147558
+ return existing;
147559
+ const mesh = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, prototype.name + ".L" + lod + "." + source.name + ".Prepared");
147560
+ if (mesh == null)
146817
147561
  return null;
146818
- host.metadata = null;
146819
- host.makeGeometryUnique();
146820
- const rel = src.computeWorldMatrix(true).multiply(tplWorldInv);
146821
- host.position.setAll(0);
146822
- host.rotationQuaternion = null;
146823
- host.rotation.setAll(0);
146824
- host.scaling.setAll(1);
146825
- host.bakeTransformIntoVertices(rel);
147562
+ mesh.setEnabled(false);
147563
+ mesh.isVisible = false;
147564
+ mesh.isPickable = false;
147565
+ prepared.set(key, mesh);
147566
+ return mesh;
147567
+ }
147568
+ createUniqueHost(source, templateWorldInverse, name, reason) {
147569
+ if (!this._warnedUniqueHost) {
147570
+ this._warnedUniqueHost = true;
147571
+ Tools.Warn("[TerrainBuilder] " + this._owner.transform.name + ": tree hosts cannot share gpu buffers, copying geometry per tile — " + reason);
147572
+ }
147573
+ const host = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, name);
147574
+ if (host != null)
147575
+ host.setEnabled(true);
147576
+ return host;
147577
+ }
147578
+ createHost(prototype, lod, source, templateWorldInverse, matrices, fades) {
147579
+ const prepared = this.prepareHostGeometry(prototype, lod, source, templateWorldInverse);
147580
+ if (prepared == null)
147581
+ return null;
147582
+ const name = prototype.name + ".L" + lod + "." + source.name;
147583
+ let host = null;
147584
+ try {
147585
+ host = TerrainTrees.CreateSharedHost(this._scene, prepared, name);
147586
+ for (const key of TerrainTrees.HOST_DRAW_STATE)
147587
+ host[key] = prepared[key];
147588
+ }
147589
+ catch (error) {
147590
+ host = this.createUniqueHost(source, templateWorldInverse, name, String(error));
147591
+ if (host == null)
147592
+ return null;
147593
+ }
147594
+ const sourceMaterial = prepared.material;
146826
147595
  host.parent = this._root;
146827
- host.setEnabled(true);
146828
147596
  host.isVisible = false;
146829
147597
  host.isPickable = false;
146830
147598
  host.receiveShadows = true;
146831
147599
  host.alwaysSelectAsActiveMesh = true;
146832
147600
  host.doNotSyncBoundingInfo = true;
146833
- TerrainTrees.InterleaveVertexData(host);
146834
147601
  host.thinInstanceSetBuffer("matrix", matrices, 16, false);
146835
147602
  host.thinInstanceSetBuffer("tkLodFade", fades, 1, false);
146836
147603
  host.thinInstanceCount = 0;
146837
- host.material = this.hostMaterial(p, lod, geo.material, TerrainTrees.HasSpeedTreeWindVertexData(host));
147604
+ host.material = this.hostMaterial(prototype, lod, sourceMaterial, TerrainTrees.HasSpeedTreeWindVertexData(prepared));
146838
147605
  if (ShaderGraphRuntime != null)
146839
147606
  ShaderGraphRuntime.EnsureThinInstanceBuffers(host);
146840
147607
  return host;
@@ -146862,7 +147629,7 @@ export class TerrainTrees {
146862
147629
  ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
146863
147630
  if (TerrainBuilder != null)
146864
147631
  TerrainBuilder.SetCasterSlot(mesh, slotIndex);
146865
- slots.push({ mesh: mesh, matrices: matrices, fades: fades, probes: null, count: 0, lastCount: -1, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3() });
147632
+ slots.push({ mesh: mesh, matrices: matrices, fades: fades, probes: null, count: 0, lastCount: -1, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3(), capacityCeiling: host.capacityCeiling, grown: false });
146866
147633
  }
146867
147634
  host.slots = slots;
146868
147635
  }
@@ -146903,7 +147670,8 @@ export class TerrainTrees {
146903
147670
  const hue = (info != null && Array.isArray(info.hue) && info.hue.length >= 4) ? info.hue : null;
146904
147671
  const F = TerrainFoliagePlugin;
146905
147672
  const urp = (this._owner.contract != null && (this._owner.contract.flavour || "urp") === "urp");
146906
- const bend = ownWind ? 0 : F.MODE_TREE;
147673
+ const graphMoves = p.speedtree !== true && F.GraphOwnsMotion(source);
147674
+ const bend = (ownWind || graphMoves) ? 0 : F.MODE_TREE;
146907
147675
  const plugin = F.Attach(entry.material, bend | F.MODE_CROSSFADE | (billboard ? F.MODE_BILLBOARD : 0) | (hue != null ? F.MODE_HUE : 0) | (urp ? F.MODE_URPSPECULAR : 0));
146908
147676
  if (plugin != null) {
146909
147677
  plugin.params.bendFactor = (p.bendfactor != null) ? p.bendfactor : 0;
@@ -147608,7 +148376,7 @@ export class TerrainTrees {
147608
148376
  }
147609
148377
  appendInstance(target, instanceIndex, fade) {
147610
148378
  const count = target.count;
147611
- if (16 * (count + 1) > target.matrices.length)
148379
+ if (16 * (count + 1) > target.matrices.length && !TerrainTrees.GrowTarget(target, target.capacityCeiling))
147612
148380
  return;
147613
148381
  const sourceOffset = 16 * instanceIndex;
147614
148382
  const targetOffset = 16 * count;
@@ -147646,6 +148414,26 @@ export class TerrainTrees {
147646
148414
  if (z + radius > box[5])
147647
148415
  box[5] = z + radius;
147648
148416
  }
148417
+ static GrowTarget(target, ceiling) {
148418
+ const capacity = target.matrices.length / 16;
148419
+ if (capacity >= ceiling)
148420
+ return false;
148421
+ const grownCapacity = Math.min(ceiling, Math.max(target.count + 1, Math.ceil(capacity * TREE_GROWTH_FACTOR)));
148422
+ const matrices = new Float32Array(16 * grownCapacity);
148423
+ matrices.set(target.matrices.subarray(0, 16 * target.count));
148424
+ target.matrices = matrices;
148425
+ const fades = new Float32Array(grownCapacity);
148426
+ fades.set(target.fades.subarray(0, target.count));
148427
+ target.fades = fades;
148428
+ if (target.probes != null) {
148429
+ const stride = TerrainProbePlugin.STRIDE;
148430
+ const probes = new Float32Array(stride * grownCapacity);
148431
+ probes.set(target.probes.subarray(0, stride * target.count));
148432
+ target.probes = probes;
148433
+ }
148434
+ target.grown = true;
148435
+ return true;
148436
+ }
147649
148437
  static LodCasts(hosts, proto, lod) {
147650
148438
  if (lod < 0 || lod >= proto.lodHosts.length)
147651
148439
  return false;
@@ -147683,16 +148471,39 @@ export class TerrainTrees {
147683
148471
  }
147684
148472
  }
147685
148473
  }
148474
+ static RebindGrownTarget(target) {
148475
+ const mesh = target.mesh;
148476
+ mesh.thinInstanceSetBuffer("matrix", target.matrices, 16, false);
148477
+ TerrainTrees.ResizePreviousMatrices(mesh, target.matrices);
148478
+ mesh.thinInstanceSetBuffer("tkLodFade", target.fades, 1, false);
148479
+ if (target.probes != null)
148480
+ TerrainTrees.ResizeProbeBuffer(target);
148481
+ if (ShaderGraphRuntime != null)
148482
+ ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
148483
+ target.grown = false;
148484
+ }
148485
+ static ResizePreviousMatrices(mesh, matrices) {
148486
+ const storage = mesh._thinInstanceDataStorage;
148487
+ if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
148488
+ return;
148489
+ storage.previousMatrixBuffer.dispose();
148490
+ storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
148491
+ }
147686
148492
  uploadTarget(target, probeBuffer, isSlot) {
147687
148493
  const mesh = target.mesh;
147688
148494
  if (target.count > 0) {
147689
148495
  if (isSlot && target.lastCount <= 0)
147690
148496
  mesh.setEnabled(true);
148497
+ const grown = target.grown;
148498
+ if (grown)
148499
+ TerrainTrees.RebindGrownTarget(target);
147691
148500
  mesh.thinInstanceCount = target.count;
147692
- mesh.thinInstancePartialBufferUpdate("matrix", target.count, 0);
147693
- mesh.thinInstancePartialBufferUpdate("tkLodFade", target.count, 0);
147694
- if (probeBuffer != null)
147695
- probeBuffer.updateDirectly(target.probes, 0, target.count);
148501
+ if (!grown) {
148502
+ mesh.thinInstancePartialBufferUpdate("matrix", target.count, 0);
148503
+ mesh.thinInstancePartialBufferUpdate("tkLodFade", target.count, 0);
148504
+ if (probeBuffer != null)
148505
+ probeBuffer.updateDirectly(target.probes, 0, target.count);
148506
+ }
147696
148507
  target.boxMin.set(target.box[0], target.box[1], target.box[2]);
147697
148508
  target.boxMax.set(target.box[3], target.box[4], target.box[5]);
147698
148509
  mesh.getBoundingInfo().reConstruct(target.boxMin, target.boxMax, mesh.computeWorldMatrix(isSlot));
@@ -147816,6 +148627,9 @@ TerrainTrees.CASTER_SLOTS = 4;
147816
148627
  TerrainTrees.SLOT_MARGIN = 4;
147817
148628
  TerrainTrees.NO_PROBE_RECORD = -1;
147818
148629
  TerrainTrees.LIGHT_TURN_DOT = 0.99996192;
148630
+ TerrainTrees.HOST_DRAW_STATE = ["sideOrientation", "renderingGroupId", "layerMask", "alphaIndex", "hasVertexAlpha", "useVertexColors", "numBoneInfluencers", "skeleton"];
148631
+ TerrainTrees.PreparedHosts = new Map();
148632
+ TerrainTrees.ProbeBuffers = new WeakMap();
147819
148633
  TerrainTrees._materials = new Map();
147820
148634
  TerrainTrees.ProbeSampleHeight = 0;
147821
148635
  TerrainTrees.SUBSURFACE_FIELDS = ["isTranslucencyEnabled", "isScatteringEnabled", "translucencyIntensity", "legacyTranslucency", "useGltfStyleTextures",
@@ -166744,6 +167558,1531 @@ export class VisualEffectSpawner {
166744
167558
  }
166745
167559
  VisualEffectSpawner.COUNT_EPSILON = 1e-6;
166746
167560
  VisualEffectSpawner.MAX_PHASE_CHANGES_PER_STEP = 64;
167561
+ export class VolumetricCloudsMath {
167562
+ static ShellInterval(rayOrigin, rayDirection, innerRadius, outerRadius, planetRadius) {
167563
+ const radialDistance = Math.hypot(rayOrigin[0], rayOrigin[1], rayOrigin[2]);
167564
+ if (radialDistance <= 0)
167565
+ return null;
167566
+ const cosChi = (rayOrigin[0] * rayDirection[0] + rayOrigin[1] * rayDirection[1] + rayOrigin[2] * rayDirection[2]) / radialDistance;
167567
+ const inner = VolumetricCloudsMath.IntersectSphere(innerRadius, cosChi, radialDistance);
167568
+ const outer = VolumetricCloudsMath.IntersectSphere(outerRadius, cosChi, radialDistance);
167569
+ if (inner[0] < 0 && inner[1] >= 0) {
167570
+ const radius = (typeof planetRadius === "number") ? planetRadius : innerRadius;
167571
+ const sinHorizon = Math.min(1, radius / radialDistance);
167572
+ const horizonCosine = -Math.sqrt(Math.max(0, 1 - sinHorizon * sinHorizon));
167573
+ return (cosChi >= horizonCosine) ? [inner[1], outer[1]] : null;
167574
+ }
167575
+ if (outer[1] < 0)
167576
+ return null;
167577
+ return [Math.max(outer[0], 0), (inner[0] >= 0) ? inner[0] : outer[1]];
167578
+ }
167579
+ static StepSize(shellLength, steps, maximumStep) {
167580
+ return Math.min(shellLength / Math.max(1, steps), maximumStep);
167581
+ }
167582
+ static MaximumStep(altitudeRange, authoredSteps, tierSteps) {
167583
+ const stretch = (typeof authoredSteps === "number" && tierSteps > 0) ? Math.min(Math.max(1, authoredSteps / tierSteps), CLOUD_MAX_STEP_STRETCH) : 1;
167584
+ return altitudeRange / CLOUD_MAX_STEP_DIVISOR * stretch;
167585
+ }
167586
+ static TierSettings(tier, authored) {
167587
+ const block = authored || {};
167588
+ const positiveOr = (value, fallback) => (typeof value === "number" && value > 0) ? value : fallback;
167589
+ const caps = CLOUD_TIER_CAPS[Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, Math.floor(tier)))];
167590
+ return {
167591
+ resolutionScale: Math.min(positiveOr(block.resolutionscale, CLOUD_DEFAULT_SETTINGS.resolutionScale), caps.resolutionScale),
167592
+ primarySteps: Math.min(positiveOr(block.numprimarysteps, CLOUD_DEFAULT_SETTINGS.primarySteps), caps.primarySteps),
167593
+ longRayMultiplier: Math.min(positiveOr(block.longraysamplemultiplier, CLOUD_DEFAULT_SETTINGS.longRayMultiplier), caps.longRayMultiplier),
167594
+ lightSteps: Math.min(positiveOr(block.numlightsteps, CLOUD_DEFAULT_SETTINGS.lightSteps), caps.lightSteps)
167595
+ };
167596
+ }
167597
+ static StartTier(maximumTextureSize, viewportCssWidth) {
167598
+ if (maximumTextureSize < CLOUD_WEAK_GPU_TEXTURE_SIZE || viewportCssWidth < CLOUD_PHONE_WIDTH_CSS_PX)
167599
+ return 1;
167600
+ return 0;
167601
+ }
167602
+ static IsCameraCut(previousPosition, currentPosition) {
167603
+ if (previousPosition == null)
167604
+ return true;
167605
+ const distance = Math.hypot(currentPosition[0] - previousPosition[0], currentPosition[1] - previousPosition[1], currentPosition[2] - previousPosition[2]);
167606
+ return distance > CLOUD_CAMERA_CUT_METERS;
167607
+ }
167608
+ static PackCurves(density, erosion, ambientOcclusion) {
167609
+ const texels = new Float32Array(CLOUD_CURVE_SAMPLES * 4);
167610
+ const hasCurves = Array.isArray(density) && density.length >= CLOUD_CURVE_SAMPLES;
167611
+ const clamp01 = (value) => Math.max(0, Math.min(1, (typeof value === "number") ? value : 0));
167612
+ for (let i = 0; i < CLOUD_CURVE_SAMPLES; i++) {
167613
+ const isEnd = (i === 0 || i === CLOUD_CURVE_SAMPLES - 1);
167614
+ texels[i * 4] = !hasCurves ? 1 : (isEnd ? 0 : clamp01(density[i]));
167615
+ texels[i * 4 + 1] = (!hasCurves || !Array.isArray(erosion)) ? 1 : clamp01(erosion[i]);
167616
+ texels[i * 4 + 2] = (!hasCurves || !Array.isArray(ambientOcclusion)) ? 1 : clamp01(1 - ambientOcclusion[i]);
167617
+ texels[i * 4 + 3] = 1;
167618
+ }
167619
+ return texels;
167620
+ }
167621
+ static IntersectSphere(sphereRadius, cosChi, radialDistance) {
167622
+ const ratio = sphereRadius / radialDistance;
167623
+ const discriminant = ratio * ratio - Math.max(0, Math.min(1, 1 - cosChi * cosChi));
167624
+ if (discriminant < 0)
167625
+ return [discriminant, discriminant];
167626
+ const root = Math.sqrt(discriminant);
167627
+ return [radialDistance * (-cosChi - root), radialDistance * (-cosChi + root)];
167628
+ }
167629
+ }
167630
+ VolumetricCloudsMath.LowestTier = 2;
167631
+ export class VolumetricCloudsTier {
167632
+ constructor(startTier) {
167633
+ this.isSettled = false;
167634
+ this.windowMilliseconds = 0;
167635
+ this.windowFrames = 0;
167636
+ this.meanBeforeStep = 0;
167637
+ this.previousMean = 0;
167638
+ this.tier = Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, startTier));
167639
+ }
167640
+ addFrame(frameMilliseconds) {
167641
+ this.windowMilliseconds += frameMilliseconds;
167642
+ this.windowFrames++;
167643
+ if (this.windowFrames < CLOUD_TIER_WINDOW_FRAMES)
167644
+ return false;
167645
+ const meanMilliseconds = this.windowMilliseconds / this.windowFrames;
167646
+ this.windowMilliseconds = 0;
167647
+ this.windowFrames = 0;
167648
+ if (this.meanBeforeStep > 0) {
167649
+ const savedEnough = meanMilliseconds <= this.meanBeforeStep * (1 - CLOUD_TIER_MINIMUM_SAVING);
167650
+ this.meanBeforeStep = 0;
167651
+ if (!savedEnough) {
167652
+ this.tier--;
167653
+ this.isSettled = true;
167654
+ return true;
167655
+ }
167656
+ }
167657
+ const previousMean = this.previousMean;
167658
+ this.previousMean = meanMilliseconds;
167659
+ const isSteady = previousMean > 0 && meanMilliseconds > previousMean * (1 - CLOUD_TIER_MINIMUM_SAVING);
167660
+ if (this.isSettled || !isSteady || meanMilliseconds <= CLOUD_TIER_BUDGET_MS || this.tier >= VolumetricCloudsMath.LowestTier)
167661
+ return false;
167662
+ this.tier++;
167663
+ this.meanBeforeStep = meanMilliseconds;
167664
+ return true;
167665
+ }
167666
+ }
167667
+ export class VolumetricClouds {
167668
+ static TryCreate(scene, metadata, root) {
167669
+ const block = PhysicalSkyBlock.UrpBlock(metadata);
167670
+ const clouds = (block != null) ? block.clouds : null;
167671
+ if (clouds == null || clouds.state === false || scene == null)
167672
+ return null;
167673
+ if (HdrpPhysicallyBasedSky == null || !HdrpPhysicallyBasedSky.IsSupported(scene.getEngine()))
167674
+ return null;
167675
+ const existing = VolumetricClouds.Get(scene);
167676
+ if (existing != null)
167677
+ return existing;
167678
+ return new VolumetricClouds(scene, clouds, block.sky, (typeof root === "string") ? root : "");
167679
+ }
167680
+ static Get(scene) {
167681
+ if (scene == null)
167682
+ return null;
167683
+ const clouds = scene._tkVolumetricClouds;
167684
+ return (clouds != null && clouds.disposed !== true) ? clouds : null;
167685
+ }
167686
+ constructor(scene, block, sky, root) {
167687
+ this.disposed = false;
167688
+ this.tier = 0;
167689
+ this.hasAerial = false;
167690
+ this.placeholderAerial = null;
167691
+ this.worleyTexture = null;
167692
+ this.erosionTexture = null;
167693
+ this.curveTexture = null;
167694
+ this.placeholderDepth = null;
167695
+ this.marchTarget = null;
167696
+ this.renderer = null;
167697
+ this.marchPass = null;
167698
+ this.temporalPass = null;
167699
+ this.historyTargets = [];
167700
+ this.historyIndex = 0;
167701
+ this.hasHistory = false;
167702
+ this.compositeMesh = null;
167703
+ this.compositeMaterial = null;
167704
+ this.isReady = false;
167705
+ this.isPending = false;
167706
+ this.preExposure = 1;
167707
+ this.preExposureBinding = null;
167708
+ this.frameSeed = 0;
167709
+ this.windOffset = [0, 0];
167710
+ this.verticalShapeOffset = 0;
167711
+ this.verticalErosionOffset = 0;
167712
+ this.sceneDepth = null;
167713
+ this.beforeRenderObserver = null;
167714
+ this.afterRenderObserver = null;
167715
+ this.tierTracker = null;
167716
+ this.targetWidth = 1;
167717
+ this.targetHeight = 1;
167718
+ this.lastCamera = null;
167719
+ this.sunLight = null;
167720
+ this.ambientSource = null;
167721
+ this.ambientCaptureCount = -1;
167722
+ this.previousEye = [0, 0, 0];
167723
+ this.currentEye = [0, 0, 0];
167724
+ this.hasPreviousEye = false;
167725
+ this.beforeTargetsObserver = null;
167726
+ this.afterTargetsObserver = null;
167727
+ this.disposeObserver = null;
167728
+ this.mirror = Matrix.Identity();
167729
+ this.inverseViewProjection = new Matrix();
167730
+ this.previousViewProjection = new Matrix();
167731
+ this.previousViewProjectionUnity = new Matrix();
167732
+ this.sunDirection = new Vector3();
167733
+ this.cameraForward = new Vector3();
167734
+ this.ambientTop = new Color3();
167735
+ this.ambientBottom = new Color3();
167736
+ this.scene = scene;
167737
+ this.block = block;
167738
+ this.sky = sky;
167739
+ this.root = root;
167740
+ scene._tkVolumetricClouds = this;
167741
+ const planet = (sky != null) ? sky.planet : null;
167742
+ this.isLocal = (planet != null && typeof planet.renderingspace === "number") ? planet.renderingspace === 1 : block.localclouds === true;
167743
+ const curvature = (typeof block.earthcurvature === "number") ? block.earthcurvature : 0;
167744
+ this.planetRadius = (planet != null && typeof planet.radius === "number" && planet.radius > 0) ? planet.radius : Scalar.Lerp(1, CLOUD_MINIMUM_CURVATURE_SCALE, curvature) * CLOUD_EARTH_RADIUS_METERS;
167745
+ this.planetCenter = (this.isLocal && planet != null && Array.isArray(planet.center)) ? Vector3.FromArray(planet.center) : new Vector3(0, -this.planetRadius, 0);
167746
+ this.scatteringTint = VolumetricClouds.LinearScatteringTint(block.scatteringtint);
167747
+ if (scene.useRightHandedSystem === true)
167748
+ Matrix.ScalingToRef(-1, 1, 1, this.mirror);
167749
+ this.localForward = new Vector3(0, 0, (scene.useRightHandedSystem === true) ? -1 : 1);
167750
+ this.tier = VolumetricCloudsMath.StartTier(scene.getEngine().getCaps().maxTextureSize, VolumetricClouds.CanvasCssWidth(scene));
167751
+ this.tierTracker = new VolumetricCloudsTier(this.tier);
167752
+ this.settings = VolumetricCloudsMath.TierSettings(this.tier, block);
167753
+ this.reportLoweredSettings();
167754
+ this.isPending = true;
167755
+ scene.addPendingData(this);
167756
+ this.preExposureBinding = HdrpRendering.Bind(scene, 1, 1, (scaled) => { this.preExposure = scaled; });
167757
+ this.disposeObserver = scene.onDisposeObservable.add(() => this.dispose());
167758
+ this.createPasses();
167759
+ this.loadNoise().then((loaded) => {
167760
+ if (this.disposed)
167761
+ return;
167762
+ if (!loaded) {
167763
+ this.releasePending();
167764
+ return;
167765
+ }
167766
+ this.waitUntilReady();
167767
+ });
167768
+ }
167769
+ dispose() {
167770
+ if (this.disposed)
167771
+ return;
167772
+ this.disposed = true;
167773
+ const scene = this.scene;
167774
+ this.releasePending();
167775
+ if (this.beforeRenderObserver != null)
167776
+ scene.onBeforeRenderObservable.remove(this.beforeRenderObserver);
167777
+ if (this.afterRenderObserver != null)
167778
+ scene.onAfterRenderObservable.remove(this.afterRenderObserver);
167779
+ if (this.beforeTargetsObserver != null)
167780
+ scene.onBeforeRenderTargetsRenderObservable.remove(this.beforeTargetsObserver);
167781
+ if (this.afterTargetsObserver != null)
167782
+ scene.onAfterRenderTargetsRenderObservable.remove(this.afterTargetsObserver);
167783
+ if (this.disposeObserver != null)
167784
+ scene.onDisposeObservable.remove(this.disposeObserver);
167785
+ if (this.preExposureBinding != null)
167786
+ HdrpRendering.Unbind(scene, this.preExposureBinding);
167787
+ const disposables = [this.compositeMesh, this.compositeMaterial, this.marchPass, this.temporalPass, this.renderer, this.marchTarget, this.worleyTexture, this.erosionTexture, this.curveTexture, this.placeholderDepth, this.placeholderAerial].concat(this.historyTargets);
167788
+ for (const disposable of disposables) {
167789
+ try {
167790
+ if (disposable != null)
167791
+ disposable.dispose();
167792
+ }
167793
+ catch (error) { }
167794
+ }
167795
+ const sceneWithClouds = scene;
167796
+ if (sceneWithClouds._tkVolumetricClouds === this)
167797
+ sceneWithClouds._tkVolumetricClouds = null;
167798
+ }
167799
+ createPasses() {
167800
+ const scene = this.scene;
167801
+ const engine = scene.getEngine();
167802
+ const isWebGPU = engine.isWebGPU === true;
167803
+ const language = isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL;
167804
+ const defines = [];
167805
+ if (this.isLocal)
167806
+ defines.push("#define CLOUD_LOCAL");
167807
+ if (this.block.microerosion === true && this.block.microerosionfactor > 0)
167808
+ defines.push("#define CLOUD_MICRO_EROSION");
167809
+ const liveSky = HdrpPhysicallyBasedSky.Get(scene);
167810
+ this.hasAerial = liveSky != null && HdrpFogMath != null && HdrpFogMath.HasAerialPerspective(liveSky);
167811
+ const marchUniforms = VolumetricCloudsShaders.MarchUniforms.slice();
167812
+ const marchSamplers = VolumetricCloudsShaders.MarchSamplers.slice();
167813
+ if (this.hasAerial) {
167814
+ defines.push("#define CLOUD_AERIAL");
167815
+ marchUniforms.push(VolumetricCloudsShaders.AerialCameraUniform, ...HdrpPbSkyShaders.AtmosphereUniforms);
167816
+ marchSamplers.push(VolumetricCloudsShaders.AerialSampler);
167817
+ this.placeholderAerial = new RawTexture2DArray(new Uint8Array(4), 1, 1, 1, Constants.TEXTUREFORMAT_RGBA, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
167818
+ this.placeholderAerial.name = "Volumetric Clouds Aerial Placeholder";
167819
+ }
167820
+ VolumetricCloudsShaders.Register();
167821
+ this.curveTexture = this.createCurveTexture();
167822
+ this.placeholderDepth = new RawTexture(new Float32Array([0]), 1, 1, Constants.TEXTUREFORMAT_R, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
167823
+ this.placeholderDepth.name = "Volumetric Clouds Depth Placeholder";
167824
+ const textureType = (engine.getCaps().textureHalfFloatRender === true) ? Constants.TEXTURETYPE_HALF_FLOAT : Constants.TEXTURETYPE_FLOAT;
167825
+ this.updateTargetSize();
167826
+ this.marchTarget = this.createCloudTarget("Volumetric Clouds Lighting", textureType);
167827
+ this.historyTargets.push(this.createCloudTarget("Volumetric Clouds History A", textureType), this.createCloudTarget("Volumetric Clouds History B", textureType));
167828
+ this.renderer = new EffectRenderer(engine);
167829
+ this.marchPass = new EffectWrapper({
167830
+ engine: engine, name: VolumetricCloudsShaders.MarchName, fragmentShader: VolumetricCloudsShaders.MarchName, useShaderStore: true,
167831
+ uniformNames: marchUniforms, samplerNames: marchSamplers,
167832
+ defines: defines.join("\n"), shaderLanguage: language
167833
+ });
167834
+ this.marchPass.onApplyObservable.add(() => this.bindMarch(this.marchPass.effect));
167835
+ this.temporalPass = new EffectWrapper({
167836
+ engine: engine, name: VolumetricCloudsShaders.TemporalName, fragmentShader: VolumetricCloudsShaders.TemporalName, useShaderStore: true,
167837
+ uniformNames: VolumetricCloudsShaders.TemporalUniforms.slice(), samplerNames: VolumetricCloudsShaders.TemporalSamplers.slice(),
167838
+ defines: defines.join("\n"), shaderLanguage: language
167839
+ });
167840
+ this.temporalPass.onApplyObservable.add(() => this.bindTemporal(this.temporalPass.effect));
167841
+ this.createCompositeMesh(language);
167842
+ }
167843
+ createCloudTarget(name, textureType) {
167844
+ const target = new RenderTargetTexture(name, { width: this.targetWidth, height: this.targetHeight }, this.scene, false, true, textureType, false, Texture.BILINEAR_SAMPLINGMODE, false, false, false, Constants.TEXTUREFORMAT_RGBA);
167845
+ target.wrapU = Texture.CLAMP_ADDRESSMODE;
167846
+ target.wrapV = Texture.CLAMP_ADDRESSMODE;
167847
+ target.anisotropicFilteringLevel = 1;
167848
+ return target;
167849
+ }
167850
+ createCurveTexture() {
167851
+ const curves = this.block.curves || {};
167852
+ const texels = VolumetricCloudsMath.PackCurves(curves.density, curves.erosion, curves.ambientocclusion);
167853
+ const halfTexels = new Uint16Array(texels.length);
167854
+ for (let i = 0; i < texels.length; i++)
167855
+ halfTexels[i] = TextureTools.ToHalfFloat(texels[i]);
167856
+ const texture = new RawTexture(halfTexels, CLOUD_CURVE_SAMPLES, 1, Constants.TEXTUREFORMAT_RGBA, this.scene, false, false, Texture.BILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_HALF_FLOAT);
167857
+ texture.name = "Volumetric Clouds Curves";
167858
+ texture.wrapU = Texture.WRAP_ADDRESSMODE;
167859
+ texture.wrapV = Texture.CLAMP_ADDRESSMODE;
167860
+ return texture;
167861
+ }
167862
+ createCompositeMesh(language) {
167863
+ const scene = this.scene;
167864
+ const mesh = new Mesh(VolumetricClouds.MeshName, scene);
167865
+ const vertexData = new VertexData();
167866
+ vertexData.positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0];
167867
+ vertexData.indices = [0, 1, 2, 0, 2, 3];
167868
+ vertexData.applyToMesh(mesh, false);
167869
+ const material = new ShaderMaterial("Volumetric Clouds Composite", scene, { vertex: VolumetricCloudsShaders.CompositeName, fragment: VolumetricCloudsShaders.CompositeName }, { attributes: ["position"], uniforms: ["cloudDisplayEncoded"], samplers: ["cloudLighting"], needAlphaBlending: true, shaderLanguage: language });
167870
+ material.alphaMode = Constants.ALPHA_PREMULTIPLIED;
167871
+ material.disableDepthWrite = true;
167872
+ material.depthFunction = Constants.ALWAYS;
167873
+ material.backFaceCulling = false;
167874
+ material.setTexture("cloudLighting", this.marchTarget);
167875
+ material.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
167876
+ mesh.material = material;
167877
+ mesh.renderingGroupId = Utilities.DefaultRenderGroup();
167878
+ mesh.alphaIndex = CLOUD_ALPHA_INDEX;
167879
+ mesh.alwaysSelectAsActiveMesh = true;
167880
+ mesh.isPickable = false;
167881
+ mesh.doNotSyncBoundingInfo = true;
167882
+ mesh.applyFog = false;
167883
+ mesh.setEnabled(false);
167884
+ this.compositeMesh = mesh;
167885
+ this.compositeMaterial = material;
167886
+ this.beforeTargetsObserver = scene.onBeforeRenderTargetsRenderObservable.add(() => { mesh.isVisible = false; });
167887
+ this.afterTargetsObserver = scene.onAfterRenderTargetsRenderObservable.add(() => { mesh.isVisible = true; });
167888
+ }
167889
+ async loadNoise() {
167890
+ const noise = this.block.noise;
167891
+ if (noise == null || noise.worley == null || noise.erosion == null) {
167892
+ LightingConversions.WarnOnce("urp:clouds:nonoise", "Volumetric clouds: the export carries no cloud noise, so the clouds do not draw");
167893
+ return false;
167894
+ }
167895
+ try {
167896
+ const buffers = await Promise.all([
167897
+ Tools.LoadFileAsync(this.root + noise.worley.url, true),
167898
+ Tools.LoadFileAsync(this.root + noise.erosion.url, true)
167899
+ ]);
167900
+ if (this.disposed)
167901
+ return false;
167902
+ this.worleyTexture = this.createNoiseTexture("Volumetric Clouds Worley Noise", buffers[0], noise.worley.size);
167903
+ this.erosionTexture = this.createNoiseTexture("Volumetric Clouds Erosion Noise", buffers[1], noise.erosion.size);
167904
+ return this.worleyTexture != null && this.erosionTexture != null;
167905
+ }
167906
+ catch (error) {
167907
+ LightingConversions.WarnOnce("urp:clouds:noiseload", "Volumetric clouds: a cloud noise file failed to load, so the clouds do not draw (" + error + ")");
167908
+ return false;
167909
+ }
167910
+ }
167911
+ createNoiseTexture(name, buffer, size) {
167912
+ if (buffer == null || buffer.byteLength !== size * size * size) {
167913
+ LightingConversions.WarnOnce("urp:clouds:noisesize", "Volumetric clouds: " + name + " is not " + size + "³ bytes, so the clouds do not draw");
167914
+ return null;
167915
+ }
167916
+ const texture = new RawTexture3D(new Uint8Array(buffer), size, size, size, Constants.TEXTUREFORMAT_R, this.scene, true, false, Texture.TRILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
167917
+ texture.name = name;
167918
+ texture.wrapU = Texture.WRAP_ADDRESSMODE;
167919
+ texture.wrapV = Texture.WRAP_ADDRESSMODE;
167920
+ texture.wrapR = Texture.WRAP_ADDRESSMODE;
167921
+ return texture;
167922
+ }
167923
+ waitUntilReady() {
167924
+ if (this.disposed)
167925
+ return;
167926
+ const camera = this.scene.activeCamera;
167927
+ if (camera != null && this.sceneDepth == null)
167928
+ this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
167929
+ const noiseReady = this.worleyTexture.isReady() && this.erosionTexture.isReady() && this.curveTexture.isReady();
167930
+ const compositeReady = this.compositeMaterial.isReady(this.compositeMesh);
167931
+ if (!noiseReady || !this.marchPass.isReady() || !this.temporalPass.isReady() || !compositeReady || camera == null) {
167932
+ setTimeout(() => this.waitUntilReady(), CLOUD_READY_POLL_MS);
167933
+ return;
167934
+ }
167935
+ this.isReady = true;
167936
+ this.compositeMesh.setEnabled(true);
167937
+ this.beforeRenderObserver = this.scene.onBeforeRenderObservable.add(() => this.render());
167938
+ this.releasePending();
167939
+ SceneManager.WhenSceneRevealed(this.scene).then((revealed) => {
167940
+ if (!revealed || this.disposed)
167941
+ return;
167942
+ this.afterRenderObserver = this.scene.onAfterRenderObservable.add(() => this.updateTier());
167943
+ });
167944
+ }
167945
+ releasePending() {
167946
+ if (!this.isPending)
167947
+ return;
167948
+ this.isPending = false;
167949
+ this.scene.removePendingData(this);
167950
+ }
167951
+ render() {
167952
+ const camera = this.scene.activeCamera;
167953
+ if (!this.isReady || this.disposed || camera == null)
167954
+ return;
167955
+ this.advanceWind(this.scene.getEngine().getDeltaTime() / 1000);
167956
+ this.resizeTargets();
167957
+ this.frameSeed = (this.frameSeed + 1) % 65536;
167958
+ this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
167959
+ this.trackCamera(camera);
167960
+ this.renderer.render(this.marchPass, this.marchTarget);
167961
+ const accumulation = (typeof this.block.temporalaccumulationfactor === "number") ? this.block.temporalaccumulationfactor : 0;
167962
+ if (accumulation < CLOUD_MINIMUM_ACCUMULATION) {
167963
+ this.compositeMaterial.setTexture("cloudLighting", this.marchTarget);
167964
+ }
167965
+ else {
167966
+ this.historyIndex = 1 - this.historyIndex;
167967
+ this.renderer.render(this.temporalPass, this.historyTargets[this.historyIndex]);
167968
+ this.compositeMaterial.setTexture("cloudLighting", this.historyTargets[this.historyIndex]);
167969
+ this.hasHistory = true;
167970
+ }
167971
+ this.compositeMaterial.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
167972
+ this.previousViewProjection.copyFrom(camera.getTransformationMatrix());
167973
+ }
167974
+ isDisplayEncoded() {
167975
+ return HdrpRendering.IsParity(this.scene) !== true && this.scene.imageProcessingConfiguration.applyByPostProcess !== true;
167976
+ }
167977
+ trackCamera(camera) {
167978
+ const eye = camera.globalPosition;
167979
+ this.currentEye[0] = eye.x;
167980
+ this.currentEye[1] = eye.y;
167981
+ this.currentEye[2] = eye.z;
167982
+ const isCut = camera !== this.lastCamera || VolumetricCloudsMath.IsCameraCut(this.hasPreviousEye ? this.previousEye : null, this.currentEye);
167983
+ if (isCut)
167984
+ this.hasHistory = false;
167985
+ this.lastCamera = camera;
167986
+ this.previousEye[0] = eye.x;
167987
+ this.previousEye[1] = eye.y;
167988
+ this.previousEye[2] = eye.z;
167989
+ this.hasPreviousEye = true;
167990
+ }
167991
+ updateTier() {
167992
+ if (this.disposed || !this.tierTracker.addFrame(this.scene.getEngine().getDeltaTime()))
167993
+ return;
167994
+ this.tier = this.tierTracker.tier;
167995
+ this.settings = VolumetricCloudsMath.TierSettings(this.tier, this.block);
167996
+ if (this.tierTracker.isSettled) {
167997
+ LightingConversions.WarnOnce("urp:clouds:restored", "Volumetric clouds: a lower tier saved under " + (CLOUD_TIER_MINIMUM_SAVING * 100) + " % of the frame time, so tier " + this.tier + " is kept");
167998
+ return;
167999
+ }
168000
+ LightingConversions.WarnOnce("urp:clouds:tier" + this.tier, "Volumetric clouds: frames ran over " + CLOUD_TIER_BUDGET_MS + " ms, trying tier " + this.tier
168001
+ + " (resolution " + this.settings.resolutionScale + ", " + this.settings.primarySteps + " primary steps, " + this.settings.lightSteps + " light steps)");
168002
+ }
168003
+ reportLoweredSettings() {
168004
+ const block = this.block;
168005
+ const settings = this.settings;
168006
+ if (typeof block.numprimarysteps === "number" && settings.primarySteps < block.numprimarysteps) {
168007
+ LightingConversions.WarnOnce("urp:clouds:primarysteps:" + this.tier, "Volumetric clouds: " + block.numprimarysteps + " primary steps are capped at " + settings.primarySteps + " in the browser (tier " + this.tier + ")");
168008
+ }
168009
+ if (typeof block.longraysamplemultiplier === "number" && settings.longRayMultiplier < block.longraysamplemultiplier) {
168010
+ LightingConversions.WarnOnce("urp:clouds:longray:" + this.tier, "Volumetric clouds: long ray sample multiplier " + block.longraysamplemultiplier + " is capped at " + settings.longRayMultiplier + " in the browser (tier " + this.tier + ")");
168011
+ }
168012
+ if (typeof block.numlightsteps === "number" && settings.lightSteps < block.numlightsteps) {
168013
+ LightingConversions.WarnOnce("urp:clouds:lightsteps:" + this.tier, "Volumetric clouds: " + block.numlightsteps + " light steps are capped at " + settings.lightSteps + " (tier " + this.tier + ")");
168014
+ }
168015
+ if (typeof block.resolutionscale === "number" && settings.resolutionScale < block.resolutionscale) {
168016
+ LightingConversions.WarnOnce("urp:clouds:resolution:" + this.tier, "Volumetric clouds: resolution scale " + block.resolutionscale + " is lowered to " + settings.resolutionScale + " (tier " + this.tier + ")");
168017
+ }
168018
+ }
168019
+ advanceWind(deltaSeconds) {
168020
+ const block = this.block;
168021
+ const signedDelta = Math.min(Math.max(deltaSeconds, 0), 1) * CLOUD_WIND_KMH_TO_MS;
168022
+ const theta = Tools.ToRadians(block.globalorientation || 0);
168023
+ const speed = block.globalspeed || 0;
168024
+ this.windOffset[0] = (this.windOffset[0] + signedDelta * speed * Math.cos(theta)) % CLOUD_WIND_WRAP_METERS;
168025
+ this.windOffset[1] = (this.windOffset[1] + signedDelta * speed * Math.sin(theta)) % CLOUD_WIND_WRAP_METERS;
168026
+ this.verticalShapeOffset = (this.verticalShapeOffset + signedDelta * (block.verticalshapewindspeed || 0)) % CLOUD_WIND_WRAP_METERS;
168027
+ this.verticalErosionOffset = (this.verticalErosionOffset + signedDelta * (block.erosionspeedmultiplier || 0)) % CLOUD_WIND_WRAP_METERS;
168028
+ }
168029
+ resizeTargets() {
168030
+ if (!this.updateTargetSize())
168031
+ return;
168032
+ const size = { width: this.targetWidth, height: this.targetHeight };
168033
+ this.marchTarget.resize(size);
168034
+ for (const history of this.historyTargets)
168035
+ history.resize(size);
168036
+ this.hasHistory = false;
168037
+ }
168038
+ updateTargetSize() {
168039
+ const engine = this.scene.getEngine();
168040
+ const scale = this.settings.resolutionScale;
168041
+ const width = Math.max(1, Math.round(engine.getRenderWidth() * scale));
168042
+ const height = Math.max(1, Math.round(engine.getRenderHeight() * scale));
168043
+ if (width === this.targetWidth && height === this.targetHeight)
168044
+ return false;
168045
+ this.targetWidth = width;
168046
+ this.targetHeight = height;
168047
+ return true;
168048
+ }
168049
+ bindMarch(effect) {
168050
+ const scene = this.scene;
168051
+ const camera = scene.activeCamera;
168052
+ if (camera == null)
168053
+ return;
168054
+ const block = this.block;
168055
+ const settings = this.settings;
168056
+ const mirrorX = this.mirror.m[0];
168057
+ camera.getTransformationMatrix().invertToRef(this.inverseViewProjection);
168058
+ this.inverseViewProjection.multiplyToRef(this.mirror, this.inverseViewProjection);
168059
+ effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
168060
+ const eye = camera.globalPosition;
168061
+ effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
168062
+ camera.getDirectionToRef(this.localForward, this.cameraForward);
168063
+ effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, mirrorX);
168064
+ const hasDepth = this.sceneDepth != null;
168065
+ effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
168066
+ effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.frameSeed, this.preExposure);
168067
+ if (this.hasAerial)
168068
+ this.bindAerial(effect);
168069
+ const center = this.planetCenter;
168070
+ const bottomRadius = this.planetRadius + (block.bottomaltitude || 0);
168071
+ const altitudeRange = block.altituderange || 1;
168072
+ effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
168073
+ effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, VolumetricCloudsMath.MaximumStep(altitudeRange, block.numprimarysteps, settings.primarySteps), settings.primarySteps);
168074
+ const isAutomaticFade = block.fadeinmode !== CLOUD_FADE_MODE_MANUAL;
168075
+ const fadeStart = isAutomaticFade ? Math.max(altitudeRange * CLOUD_AUTO_FADE_START_FRACTION, camera.minZ) : Math.max(block.fadeinstart || 0, camera.minZ);
168076
+ const fadeDistance = isAutomaticFade ? altitudeRange * CLOUD_AUTO_FADE_DISTANCE_FRACTION : (block.fadeindistance || 0);
168077
+ effect.setFloat4("cloudSteps", settings.longRayMultiplier, settings.lightSteps, fadeStart, fadeDistance);
168078
+ effect.setFloat4("cloudShape", block.shapescale, block.shapefactor, block.erosionscale, block.erosionfactor);
168079
+ const density = block.densitymultiplier * block.densitymultiplier * CLOUD_DENSITY_SCALE;
168080
+ effect.setFloat4("cloudDetail", block.erosionocclusion, block.microerosionscale, block.microerosionfactor, density);
168081
+ const shapeOffset = Array.isArray(block.shapeoffset) ? block.shapeoffset : [0, 0, 0];
168082
+ effect.setFloat4("cloudShapeOffset", shapeOffset[0], shapeOffset[2], shapeOffset[1], block.altitudedistortion * CLOUD_ALTITUDE_DISTORTION_SCALE);
168083
+ const theta = Tools.ToRadians(block.globalorientation || 0);
168084
+ effect.setFloat4("cloudWind", -Math.cos(theta), -Math.sin(theta), this.windOffset[0], this.windOffset[1]);
168085
+ effect.setFloat4("cloudWindSpeed", block.shapespeedmultiplier, block.erosionspeedmultiplier, this.verticalShapeOffset, this.verticalErosionOffset);
168086
+ const coverageInverseSize = 1 / Math.max(CLOUD_MINIMUM_COVERAGE_PATTERN_METERS, block.coveragepatternsize || CLOUD_DEFAULT_COVERAGE_PATTERN_METERS);
168087
+ const coverageGap = (typeof block.coveragegapamount === "number") ? block.coveragegapamount : CLOUD_DEFAULT_COVERAGE_GAP;
168088
+ effect.setFloat4("cloudCoverage", block.coveragevariation || 0, coverageInverseSize, coverageGap, 0);
168089
+ const coverageOffset = Array.isArray(block.coveragepatternoffset) ? block.coveragepatternoffset : [0, 0];
168090
+ effect.setFloat4("cloudCoverageOffset", coverageOffset[0], coverageOffset[1], 0, 0);
168091
+ effect.setFloat4("cloudScattering", 1 - block.multiscattering * CLOUD_MULTI_SCATTERING_SCALE, block.powdereffectintensity, 0, 0);
168092
+ effect.setFloat4("cloudTint", this.scatteringTint.r, this.scatteringTint.g, this.scatteringTint.b, 0);
168093
+ this.bindLighting(effect, mirrorX);
168094
+ effect.setTexture("worleyNoise", this.worleyTexture);
168095
+ effect.setTexture("erosionNoise", this.erosionTexture);
168096
+ effect.setTexture("cloudCurves", this.curveTexture);
168097
+ }
168098
+ bindLighting(effect, mirrorX) {
168099
+ const block = this.block;
168100
+ const sun = this.resolveSun();
168101
+ const sunScale = (this.sky != null && typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
168102
+ const dimmer = (typeof block.sunlightdimmer === "number") ? block.sunlightdimmer : 1;
168103
+ if (sun != null) {
168104
+ const direction = this.sunDirection;
168105
+ const strength = sun.intensity * sunScale * dimmer;
168106
+ effect.setFloat4("cloudSunDirection", -direction.x * mirrorX, -direction.y, -direction.z, 0);
168107
+ effect.setFloat4("cloudSunColor", sun.diffuse.r * strength, sun.diffuse.g * strength, sun.diffuse.b * strength, 0);
168108
+ }
168109
+ else {
168110
+ effect.setFloat4("cloudSunDirection", 0, 1, 0, 0);
168111
+ effect.setFloat4("cloudSunColor", 0, 0, 0, 0);
168112
+ }
168113
+ this.refreshAmbient();
168114
+ const ambientDimmer = (typeof block.ambientlightprobedimmer === "number") ? block.ambientlightprobedimmer : 1;
168115
+ effect.setFloat4("cloudAmbientTop", this.ambientTop.r * ambientDimmer, this.ambientTop.g * ambientDimmer, this.ambientTop.b * ambientDimmer, 0);
168116
+ effect.setFloat4("cloudAmbientBottom", this.ambientBottom.r * ambientDimmer, this.ambientBottom.g * ambientDimmer, this.ambientBottom.b * ambientDimmer, 0);
168117
+ }
168118
+ bindAerial(effect) {
168119
+ const sky = HdrpPhysicallyBasedSky.Get(this.scene);
168120
+ const isBound = sky != null && sky.bindAerialPerspective(effect, VolumetricCloudsShaders.AerialSampler, VolumetricCloudsShaders.AerialCameraUniform);
168121
+ if (isBound)
168122
+ return;
168123
+ effect.setTexture(VolumetricCloudsShaders.AerialSampler, this.placeholderAerial);
168124
+ effect.setFloat4(VolumetricCloudsShaders.AerialCameraUniform, 0, 0, 1, 0);
168125
+ }
168126
+ resolveSun() {
168127
+ if (this.sunLight == null || this.sunLight.isDisposed() || !this.sunLight.isEnabled()) {
168128
+ const lights = HdrpPhysicallyBasedSky.InteractingLights(this.scene.lights);
168129
+ this.sunLight = (lights.length > 0) ? lights[0] : null;
168130
+ }
168131
+ const sun = this.sunLight;
168132
+ if (sun == null)
168133
+ return null;
168134
+ sun.computeTransformedInformation();
168135
+ const transformed = sun.transformedDirection;
168136
+ this.sunDirection.copyFrom((transformed != null && sun.parent != null) ? transformed : sun.direction).normalize();
168137
+ return sun;
168138
+ }
168139
+ refreshAmbient() {
168140
+ const sky = HdrpPhysicallyBasedSky.Get(this.scene);
168141
+ const source = (sky != null) ? sky : this.scene.environmentTexture;
168142
+ const captureCount = (sky != null) ? sky.captureCount : 0;
168143
+ if (source === this.ambientSource && captureCount === this.ambientCaptureCount && source != null)
168144
+ return;
168145
+ this.ambientSource = source;
168146
+ this.ambientCaptureCount = captureCount;
168147
+ if (sky != null) {
168148
+ this.ambientTop.copyFrom(sky.ambientAt(Vector3.UpReadOnly));
168149
+ this.ambientBottom.copyFrom(sky.ambientAt(Vector3.DownReadOnly));
168150
+ return;
168151
+ }
168152
+ const environment = this.scene.environmentTexture;
168153
+ const polynomial = (environment != null) ? environment.sphericalPolynomial : null;
168154
+ if (polynomial != null) {
168155
+ this.ambientTop.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.UpReadOnly));
168156
+ this.ambientBottom.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.DownReadOnly));
168157
+ return;
168158
+ }
168159
+ this.ambientTop.copyFrom(this.scene.ambientColor);
168160
+ this.ambientBottom.copyFrom(this.scene.ambientColor);
168161
+ }
168162
+ bindTemporal(effect) {
168163
+ const camera = this.scene.activeCamera;
168164
+ if (camera == null)
168165
+ return;
168166
+ const mirrorX = this.mirror.m[0];
168167
+ this.mirror.multiplyToRef(this.previousViewProjection, this.previousViewProjectionUnity);
168168
+ effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
168169
+ effect.setMatrix("cloudPrevViewProj", this.previousViewProjectionUnity);
168170
+ const eye = camera.globalPosition;
168171
+ effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
168172
+ effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, 0);
168173
+ const hasDepth = this.sceneDepth != null;
168174
+ effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
168175
+ const accumulation = this.block.temporalaccumulationfactor;
168176
+ effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.hasHistory ? 1 : 0, accumulation);
168177
+ const center = this.planetCenter;
168178
+ const bottomRadius = this.planetRadius + (this.block.bottomaltitude || 0);
168179
+ const altitudeRange = this.block.altituderange || 1;
168180
+ effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
168181
+ effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, altitudeRange / CLOUD_MAX_STEP_DIVISOR, this.settings.primarySteps);
168182
+ effect.setFloat4("cloudTexel", 1 / this.targetWidth, 1 / this.targetHeight, 0, 0);
168183
+ effect.setTexture("cloudCurrent", this.marchTarget);
168184
+ effect.setTexture("cloudHistory", this.historyTargets[1 - this.historyIndex]);
168185
+ }
168186
+ static LinearScatteringTint(tint) {
168187
+ const source = Array.isArray(tint) ? tint : [0, 0, 0];
168188
+ return new Color3(1 - source[0] * CLOUD_SCATTERING_TINT_SCALE, 1 - source[1] * CLOUD_SCATTERING_TINT_SCALE, 1 - source[2] * CLOUD_SCATTERING_TINT_SCALE).toLinearSpace();
168189
+ }
168190
+ static CanvasCssWidth(scene) {
168191
+ const engine = scene.getEngine();
168192
+ const canvas = engine.getRenderingCanvas();
168193
+ if (canvas != null && typeof canvas.clientWidth === "number" && canvas.clientWidth > 0)
168194
+ return canvas.clientWidth;
168195
+ return engine.getRenderWidth();
168196
+ }
168197
+ }
168198
+ VolumetricClouds.MeshName = "Volumetric Clouds";
168199
+ export class VolumetricCloudsShaders {
168200
+ static Register() {
168201
+ if (VolumetricCloudsShaders.registered === true)
168202
+ return;
168203
+ const glsl = ShaderStore.ShadersStore;
168204
+ const wgsl = ShaderStore.ShadersStoreWGSL;
168205
+ const shaders = VolumetricCloudsShaders;
168206
+ glsl[shaders.MarchName + "FragmentShader"] = shaders.GlslMarch();
168207
+ glsl[shaders.CompositeName + "VertexShader"] = shaders.GlslCompositeVertex();
168208
+ glsl[shaders.CompositeName + "FragmentShader"] = shaders.GlslCompositeFragment();
168209
+ glsl[shaders.TemporalName + "FragmentShader"] = shaders.GlslTemporal();
168210
+ wgsl[shaders.MarchName + "FragmentShader"] = shaders.WgslMarch();
168211
+ wgsl[shaders.CompositeName + "VertexShader"] = shaders.WgslCompositeVertex();
168212
+ wgsl[shaders.CompositeName + "FragmentShader"] = shaders.WgslCompositeFragment();
168213
+ wgsl[shaders.TemporalName + "FragmentShader"] = shaders.WgslTemporal();
168214
+ VolumetricCloudsShaders.registered = true;
168215
+ }
168216
+ static AllSources() {
168217
+ const shaders = VolumetricCloudsShaders;
168218
+ return [
168219
+ shaders.GlslMarch(), shaders.GlslCompositeVertex(), shaders.GlslCompositeFragment(), shaders.GlslTemporal(),
168220
+ shaders.WgslMarch(), shaders.WgslCompositeVertex(), shaders.WgslCompositeFragment(), shaders.WgslTemporal()
168221
+ ];
168222
+ }
168223
+ static GlslMarchUniforms() {
168224
+ return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n";
168225
+ }
168226
+ static GlslConstants() {
168227
+ return `const float CLOUD_DENSITY_THRESHOLD = 0.001;
168228
+ const int EMPTY_STEPS_BEFORE_LARGE_STEPS = 8;
168229
+ const float FORWARD_ECCENTRICITY = 0.7;
168230
+ const float BACKWARD_ECCENTRICITY = 0.7;
168231
+ const float MIN_EROSION_DISTANCE = 3000.0;
168232
+ const float MAX_EROSION_DISTANCE = 100000.0;
168233
+ const float NOISE_NORMALIZATION = 100000.0;
168234
+ const float MAX_SKYBOX_DISTANCE = 200000.0;
168235
+ const float LIGHT_STEP_MAXIMAL_SIZE = 1000.0;
168236
+ const float CLOUD_PI = 3.14159265359;
168237
+ const int MAX_PRIMARY_STEPS = 1024;
168238
+ const int MAX_LIGHT_STEPS = 16;
168239
+ `;
168240
+ }
168241
+ static GlslGeometry() {
168242
+ return `vec2 cloudIntersectSphere(float sphereRadius, float cosChi, float radialDistance) {
168243
+ float ratio = sphereRadius / radialDistance;
168244
+ float d = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
168245
+ return (d < 0.0) ? vec2(d) : radialDistance * vec2(-cosChi - sqrt(d), -cosChi + sqrt(d));
168246
+ }
168247
+ float cloudHorizonCosine(float radialDistance) {
168248
+ float sinHorizon = cloudPlanet.w / radialDistance;
168249
+ return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
168250
+ }
168251
+ bool cloudIntersectVolume(vec3 originPS, vec3 direction, out float entry, out float exitDistance) {
168252
+ float radialDistance = length(originPS);
168253
+ float cosChi = dot(originPS, direction) / radialDistance;
168254
+ vec2 inner = cloudIntersectSphere(cloudLayer.x, cosChi, radialDistance);
168255
+ vec2 outer = cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance);
168256
+ if (inner.x < 0.0 && inner.y >= 0.0) {
168257
+ entry = inner.y;
168258
+ exitDistance = outer.y;
168259
+ return cosChi >= cloudHorizonCosine(radialDistance);
168260
+ }
168261
+ entry = max(outer.x, 0.0);
168262
+ exitDistance = (inner.x >= 0.0) ? inner.x : outer.y;
168263
+ return outer.y >= 0.0;
168264
+ }
168265
+ int cloudRaySphere(vec3 startWS, vec3 direction, float radius, out vec2 result) {
168266
+ vec3 startPS = startWS + vec3(0.0, cloudPlanet.w, 0.0);
168267
+ float a = dot(direction, direction);
168268
+ float b = 2.0 * dot(direction, startPS);
168269
+ float c = dot(startPS, startPS) - radius * radius;
168270
+ float d = b * b - 4.0 * a * c;
168271
+ result = vec2(0.0);
168272
+ int solutions = 0;
168273
+ if (d >= 0.0) {
168274
+ float q = -0.5 * (b + ((b >= 0.0) ? 1.0 : -1.0) * sqrt(d));
168275
+ result = vec2(c / q, q / a);
168276
+ solutions = 2;
168277
+ if (result.x < 0.0) { solutions--; result.x = result.y; }
168278
+ if (result.y < 0.0) solutions--;
168279
+ }
168280
+ return solutions;
168281
+ }
168282
+ bool cloudRange(vec3 originWS, vec3 direction, out float rangeStart, out float rangeEnd) {
168283
+ #ifdef CLOUD_LOCAL
168284
+ return cloudIntersectVolume(originWS - cloudPlanet.xyz, direction, rangeStart, rangeEnd);
168285
+ #else
168286
+ vec2 inner = vec2(0.0);
168287
+ vec2 outer = vec2(0.0);
168288
+ cloudRaySphere(originWS, direction, cloudLayer.x, inner);
168289
+ cloudRaySphere(originWS, direction, cloudLayer.y, outer);
168290
+ rangeStart = inner.x;
168291
+ rangeEnd = outer.x;
168292
+ return true;
168293
+ #endif
168294
+ }
168295
+ `;
168296
+ }
168297
+ static GlslDensity() {
168298
+ return `struct CloudProperties {
168299
+ float density;
168300
+ float ambientOcclusion;
168301
+ float height;
168302
+ float sigmaT;
168303
+ };
168304
+ float cloudRemap(float x, float a, float b, float c, float d) {
168305
+ return (((x - a) / (b - a)) * (d - c)) + c;
168306
+ }
168307
+ float cloudNormalizedHeight(vec3 positionPS) {
168308
+ return clamp((length(positionPS) - cloudLayer.x) / (cloudLayer.y - cloudLayer.x), 0.0, 1.0);
168309
+ }
168310
+ vec3 cloudAnimateShape(vec3 positionPS) {
168311
+ vec3 position = positionPS;
168312
+ position.y += position.x / 3.0 + position.z / 7.0;
168313
+ return position + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.x + vec3(0.0, cloudWindSpeed.z, 0.0);
168314
+ }
168315
+ vec3 cloudAnimateErosion(vec3 positionPS) {
168316
+ return positionPS + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.y + vec3(0.0, cloudWindSpeed.w, 0.0);
168317
+ }
168318
+ float cloudCoverageHash(vec2 cell) {
168319
+ vec3 p = fract(vec3(cell.x, cell.y, cell.x) * 0.1031);
168320
+ p += dot(p, p.yzx + 33.33);
168321
+ return fract((p.x + p.y) * p.z);
168322
+ }
168323
+ float cloudCoverageNoise(vec2 position) {
168324
+ vec2 cell = floor(position);
168325
+ vec2 blend = fract(position);
168326
+ blend = blend * blend * (3.0 - 2.0 * blend);
168327
+ float bottom = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2(1.0, 0.0)), blend.x);
168328
+ float top = mix(cloudCoverageHash(cell + vec2(0.0, 1.0)), cloudCoverageHash(cell + vec2(1.0, 1.0)), blend.x);
168329
+ return mix(bottom, top, blend.y);
168330
+ }
168331
+ vec2 cloudCoverageAt(vec3 positionPS) {
168332
+ float coverage = 0.9;
168333
+ float shapeFill = 0.0;
168334
+ if (cloudCoverage.x != 0.0) {
168335
+ vec2 coordinates = (positionPS.xz + cloudWind.zw * cloudWindSpeed.x + cloudCoverageOffset.xy) * cloudCoverage.y;
168336
+ float coverageNoise = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2(17.13, 43.71)) * 0.3;
168337
+ float threshold = mix(-0.15, 1.15, cloudCoverage.z);
168338
+ float mask = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
168339
+ if (cloudCoverage.x > 0.0) {
168340
+ coverage *= mix(1.0, mask, cloudCoverage.x);
168341
+ } else {
168342
+ shapeFill = clamp(-cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
168343
+ coverage = mix(coverage, 1.0, shapeFill);
168344
+ }
168345
+ }
168346
+ return vec2(coverage, shapeFill);
168347
+ }
168348
+ CloudProperties cloudPropertiesAt(vec3 samplePositionPS, float noiseMip, float erosionMip, bool cheapVersion, bool lightSampling) {
168349
+ CloudProperties properties = CloudProperties(0.0, 0.0, 0.0, 0.0);
168350
+ vec3 positionPS = samplePositionPS;
168351
+ #ifndef CLOUD_LOCAL
168352
+ if (positionPS.y < cloudPlanet.w) return properties;
168353
+ #endif
168354
+ properties.ambientOcclusion = 1.0;
168355
+ properties.height = cloudNormalizedHeight(positionPS);
168356
+ #ifndef CLOUD_LOCAL
168357
+ positionPS.xz += cloudEye.xz;
168358
+ #endif
168359
+ vec3 baseCoordinates = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * cloudShape.x - cloudShapeOffset.xyz;
168360
+ baseCoordinates += properties.height * vec3(cloudWind.x, cloudWind.y, 0.0) * cloudShapeOffset.w;
168361
+ float lowFrequencyNoise = textureLod(worleyNoise, baseCoordinates, noiseMip).r;
168362
+ vec2 coverage = cloudCoverageAt(positionPS);
168363
+ if (coverage.x <= CLOUD_DENSITY_THRESHOLD) return properties;
168364
+ vec3 curves = textureLod(cloudCurves, vec2(properties.height, 0.5), 0.0).xyz;
168365
+ float shapeFactor = mix(0.1, 1.0, cloudShape.y * (1.0 - coverage.y)) * curves.y;
168366
+ float erosionFactor = cloudShape.w * curves.y;
168367
+ float microDetailFactor = cloudDetail.z * curves.y;
168368
+ lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
168369
+ float baseCloud = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
168370
+ baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
168371
+ properties.sigmaT = 0.04;
168372
+ float occlusionBlend = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
168373
+ properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
168374
+ if (!cheapVersion) {
168375
+ vec3 erosionCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudShape.z;
168376
+ float erosionValue = 1.0 - textureLod(erosionNoise, erosionCoordinates, erosionMip).r;
168377
+ erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
168378
+ properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * cloudDetail.x), 0.0, 1.0);
168379
+ baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
168380
+ #ifdef CLOUD_MICRO_EROSION
168381
+ vec3 fineCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudDetail.y;
168382
+ float fineValue = 1.0 - textureLod(erosionNoise, fineCoordinates, erosionMip).r;
168383
+ fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
168384
+ baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
168385
+ #endif
168386
+ }
168387
+ if (lightSampling) {
168388
+ baseCloud -= erosionFactor * 0.1;
168389
+ #ifdef CLOUD_MICRO_EROSION
168390
+ baseCloud -= microDetailFactor * 0.15;
168391
+ #endif
168392
+ }
168393
+ properties.density = max(0.0, baseCloud) * cloudDetail.w;
168394
+ return properties;
168395
+ }
168396
+ `;
168397
+ }
168398
+ static GlslLighting() {
168399
+ return `float cloudPhase(float anisotropy, float cosTheta) {
168400
+ float denominator = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
168401
+ float falloff = inversesqrt(max(denominator, 1e-6));
168402
+ return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
168403
+ }
168404
+ float cloudPowder(float density, float cosAngle, float intensity) {
168405
+ float powder = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
168406
+ return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
168407
+ }
168408
+ vec3 cloudSunTransmittance(vec3 positionPS, vec3 sunDirection, vec2 phase) {
168409
+ float radialDistance = length(positionPS);
168410
+ float cosChi = dot(positionPS, sunDirection) / radialDistance;
168411
+ vec3 transmittance = vec3(0.0);
168412
+ if (cosChi < cloudHorizonCosine(radialDistance)) return transmittance;
168413
+ float lightSteps = cloudSteps.y;
168414
+ float totalLightDistance = clamp(cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
168415
+ float intervalSize = totalLightDistance / lightSteps;
168416
+ float opticalDepth = 0.0;
168417
+ for (int j = 0; j < MAX_LIGHT_STEPS; j++) {
168418
+ if (float(j) >= lightSteps) break;
168419
+ float distanceAlongLight = intervalSize * (0.25 + float(j));
168420
+ CloudProperties lightProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * float(j) / lightSteps, 0.0, true, true);
168421
+ opticalDepth += lightProperties.density * lightProperties.sigmaT;
168422
+ }
168423
+ vec3 extinction = intervalSize * opticalDepth * cloudTint.rgb;
168424
+ transmittance += exp(-extinction) * phase.x;
168425
+ transmittance += exp(-extinction * cloudScattering.x) * (phase.y * cloudScattering.x);
168426
+ return transmittance;
168427
+ }
168428
+ `;
168429
+ }
168430
+ static GlslAerial() {
168431
+ const skyShaders = HdrpPbSkyShaders;
168432
+ const fogPass = HdrpFogPass;
168433
+ if (skyShaders == null || fogPass == null)
168434
+ return "";
168435
+ return "#ifdef CLOUD_AERIAL\nprecision highp sampler2DArray;\nuniform sampler2DArray apLut;\nuniform vec4 cloudApCamera;\n"
168436
+ + skyShaders.GlslUniforms() + skyShaders.GlslCommon() + fogPass.AerialPerspectiveGlsl() + "#endif\n";
168437
+ }
168438
+ static GlslMarch() {
168439
+ const shaders = VolumetricCloudsShaders;
168440
+ return "precision highp float;\nprecision highp sampler3D;\nvarying vec2 vUV;\nuniform sampler3D worleyNoise;\nuniform sampler3D erosionNoise;\nuniform sampler2D cloudCurves;\nuniform sampler2D sceneDepth;\n"
168441
+ + shaders.GlslMarchUniforms() + shaders.GlslConstants() + shaders.GlslGeometry() + shaders.GlslDensity() + shaders.GlslLighting() + shaders.GlslAerial() + `
168442
+ uint cloudJenkins(uint value) {
168443
+ uint x = value;
168444
+ x += (x << 10u);
168445
+ x ^= (x >> 6u);
168446
+ x += (x << 3u);
168447
+ x ^= (x >> 11u);
168448
+ x += (x << 15u);
168449
+ return x;
168450
+ }
168451
+ float cloudRandom(vec2 pixel, float seed) {
168452
+ uvec3 v = uvec3(uvec2(pixel), uint(seed));
168453
+ uint hash = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
168454
+ return uintBitsToFloat((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
168455
+ }
168456
+ void main(void) {
168457
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
168458
+ vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
168459
+ vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
168460
+ float maxRayLength = MAX_SKYBOX_DISTANCE;
168461
+ if (cloudCamera.y > 0.5) {
168462
+ float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
168463
+ if (viewDepth < cloudCamera.x * 0.999) {
168464
+ #ifdef CLOUD_LOCAL
168465
+ maxRayLength = viewDepth / max(dot(direction, cloudForward.xyz), 0.0001);
168466
+ #else
168467
+ return;
168468
+ #endif
168469
+ }
168470
+ }
168471
+ #ifdef CLOUD_LOCAL
168472
+ vec3 originWS = cloudEye.xyz;
168473
+ #else
168474
+ vec3 originWS = vec3(0.0);
168475
+ #endif
168476
+ float rangeStart = 0.0;
168477
+ float rangeEnd = 0.0;
168478
+ if (!cloudRange(originWS, direction, rangeStart, rangeEnd)) return;
168479
+ if (maxRayLength < rangeStart) return;
168480
+ float integrationNoise = cloudRandom(gl_FragCoord.xy, cloudCamera.z);
168481
+ float primarySteps = cloudLayer.w;
168482
+ float totalDistance = min(rangeEnd, maxRayLength) - rangeStart;
168483
+ float stepSize = min(totalDistance / primarySteps, cloudLayer.z);
168484
+ totalDistance = stepSize * primarySteps;
168485
+ float layerThickness = max(1.0, cloudLayer.y - cloudLayer.x);
168486
+ float sampleMultiplier = min(max(1.0, totalDistance / layerThickness), max(1.0, cloudSteps.x));
168487
+ int primaryStepCount = min(int(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
168488
+ stepSize = totalDistance / float(max(1, primaryStepCount));
168489
+ vec3 sunDirection = cloudSunDirection.xyz;
168490
+ float cosAngle = dot(direction, sunDirection);
168491
+ vec2 phase = vec2(
168492
+ cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
168493
+ cloudPhase(FORWARD_ECCENTRICITY * cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * cloudScattering.x, cosAngle));
168494
+ vec3 scattering = vec3(0.0);
168495
+ float ambient = 0.0;
168496
+ float transmittance = 1.0;
168497
+ float meanDistance = 0.0;
168498
+ float meanDistanceDivider = 0.0;
168499
+ float currentDistance = integrationNoise;
168500
+ vec3 currentPositionWS = originWS + (rangeStart + currentDistance) * direction;
168501
+ bool activeSampling = true;
168502
+ int sequentialEmptySamples = 0;
168503
+ int currentIndex = 0;
168504
+ for (int iteration = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
168505
+ if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) break;
168506
+ float cameraDistance = rangeStart + currentDistance;
168507
+ float densityFade = clamp((cameraDistance - cloudSteps.z) / (cloudSteps.z + cloudSteps.w), 0.0, 1.0);
168508
+ float erosionMip = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
168509
+ vec3 currentPositionPS = currentPositionWS - cloudPlanet.xyz;
168510
+ if (activeSampling) {
168511
+ CloudProperties properties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
168512
+ properties.density *= densityFade;
168513
+ if (properties.density > CLOUD_DENSITY_THRESHOLD) {
168514
+ float transmittanceDensity = transmittance * properties.density;
168515
+ meanDistance += cameraDistance * transmittanceDensity;
168516
+ meanDistanceDivider += transmittanceDensity;
168517
+ float stepTransmittance = exp(-properties.density * properties.sigmaT * stepSize);
168518
+ float powder = cloudPowder(properties.density, cosAngle, cloudScattering.y);
168519
+ vec3 sunLuminance = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
168520
+ float absorbed = transmittance - transmittance * stepTransmittance;
168521
+ scattering += sunLuminance * absorbed;
168522
+ ambient += properties.ambientOcclusion * absorbed;
168523
+ transmittance *= stepTransmittance;
168524
+ if (transmittance < 0.003) {
168525
+ transmittance = 0.0;
168526
+ break;
168527
+ }
168528
+ sequentialEmptySamples = 0;
168529
+ } else {
168530
+ sequentialEmptySamples++;
168531
+ }
168532
+ if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) activeSampling = false;
168533
+ float relativeStepSize = mix(integrationNoise, 1.0, clamp(float(currentIndex), 0.0, 1.0));
168534
+ currentPositionWS += direction * stepSize * relativeStepSize;
168535
+ currentDistance += stepSize * relativeStepSize;
168536
+ } else {
168537
+ CloudProperties properties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
168538
+ properties.density *= densityFade;
168539
+ if (properties.density < CLOUD_DENSITY_THRESHOLD) {
168540
+ currentPositionWS += direction * stepSize * 2.0;
168541
+ currentDistance += stepSize * 2.0;
168542
+ } else {
168543
+ currentPositionWS -= direction * stepSize;
168544
+ currentDistance -= stepSize;
168545
+ currentIndex -= 1;
168546
+ activeSampling = true;
168547
+ sequentialEmptySamples = 0;
168548
+ }
168549
+ }
168550
+ currentIndex++;
168551
+ }
168552
+ if (meanDistance == 0.0) return;
168553
+ meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
168554
+ float relativeHeight = cloudNormalizedHeight(originWS - cloudPlanet.xyz + meanDistance * direction);
168555
+ vec3 ambientColor = max(vec3(0.0), mix(cloudAmbientBottom.rgb, cloudAmbientTop.rgb, relativeHeight));
168556
+ vec3 color = (cloudSunColor.rgb * scattering + ambientColor * ambient) * cloudCamera.w;
168557
+ #ifdef CLOUD_AERIAL
168558
+ if (cloudApCamera.w > 0.5) {
168559
+ vec3 apDirection = vec3(direction.x * cloudForward.w, direction.y, direction.z);
168560
+ vec3 apColor = vec3(0.0);
168561
+ vec3 apOpacity = vec3(0.0);
168562
+ fogAerialPerspective(vUV, cloudApCamera.xyz, apDirection, meanDistance, apColor, apOpacity);
168563
+ color = apColor * (1.0 - transmittance) + (vec3(1.0) - apOpacity) * color;
168564
+ }
168565
+ #endif
168566
+ gl_FragColor = vec4(color, transmittance);
168567
+ }
168568
+ `;
168569
+ }
168570
+ static GlslCompositeVertex() {
168571
+ return `precision highp float;
168572
+ attribute vec3 position;
168573
+ varying vec2 vUV;
168574
+ void main(void) {
168575
+ vUV = position.xy * 0.5 + 0.5;
168576
+ gl_Position = vec4(position.xy, 0.0, 1.0);
168577
+ }
168578
+ `;
168579
+ }
168580
+ static GlslCompositeFragment() {
168581
+ return `precision highp float;
168582
+ varying vec2 vUV;
168583
+ uniform sampler2D cloudLighting;
168584
+ uniform float cloudDisplayEncoded;
168585
+ const float CLOUD_DISPLAY_GAMMA = 2.2;
168586
+ const vec3 CLOUD_LUMINANCE = vec3(0.2126, 0.7152, 0.0722);
168587
+ void main(void) {
168588
+ vec4 cloud = texture2D(cloudLighting, vUV);
168589
+ vec3 scattering = cloud.rgb;
168590
+ float throughput = cloud.a;
168591
+ if (cloudDisplayEncoded > 0.5) {
168592
+ float encodeExponent = 1.0 / CLOUD_DISPLAY_GAMMA;
168593
+ float scatteringLuminance = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
168594
+ throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
168595
+ scattering = pow(clamp(scattering, vec3(0.0), vec3(1.0)), vec3(encodeExponent));
168596
+ }
168597
+ gl_FragColor = vec4(scattering, 1.0 - throughput);
168598
+ }
168599
+ `;
168600
+ }
168601
+ static GlslTemporal() {
168602
+ const shaders = VolumetricCloudsShaders;
168603
+ return "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D cloudCurrent;\nuniform sampler2D cloudHistory;\nuniform sampler2D sceneDepth;\n"
168604
+ + shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n"
168605
+ + shaders.GlslGeometry() + `
168606
+ float cloudReprojectionDistance(vec3 direction) {
168607
+ float distanceToCloud = 0.0;
168608
+ float exitDistance = 0.0;
168609
+ #ifdef CLOUD_LOCAL
168610
+ if (!cloudIntersectVolume(cloudEye.xyz - cloudPlanet.xyz, direction, distanceToCloud, exitDistance)) distanceToCloud = exitDistance;
168611
+ #else
168612
+ cloudRange(vec3(0.0), direction, distanceToCloud, exitDistance);
168613
+ #endif
168614
+ distanceToCloud = max(distanceToCloud, 1.0);
168615
+ if (cloudCamera.y > 0.5) {
168616
+ float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
168617
+ if (viewDepth < cloudCamera.x * 0.999) distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, cloudForward.xyz), 0.0001));
168618
+ }
168619
+ return distanceToCloud;
168620
+ }
168621
+ void main(void) {
168622
+ vec4 current = textureLod(cloudCurrent, vUV, 0.0);
168623
+ gl_FragColor = current;
168624
+ if (cloudCamera.z < 0.5 || current.a >= 1.0) return;
168625
+ vec3 boxMin = current.rgb;
168626
+ vec3 boxMax = current.rgb;
168627
+ vec3 neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, -cloudTexel.y), 0.0).rgb;
168628
+ boxMin = min(boxMin, neighbour);
168629
+ boxMax = max(boxMax, neighbour);
168630
+ neighbour = textureLod(cloudCurrent, vUV + vec2(-cloudTexel.x, 0.0), 0.0).rgb;
168631
+ boxMin = min(boxMin, neighbour);
168632
+ boxMax = max(boxMax, neighbour);
168633
+ neighbour = textureLod(cloudCurrent, vUV + vec2(cloudTexel.x, 0.0), 0.0).rgb;
168634
+ boxMin = min(boxMin, neighbour);
168635
+ boxMax = max(boxMax, neighbour);
168636
+ neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, cloudTexel.y), 0.0).rgb;
168637
+ boxMin = min(boxMin, neighbour);
168638
+ boxMax = max(boxMax, neighbour);
168639
+ vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
168640
+ vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
168641
+ vec3 worldPosition = cloudEye.xyz + direction * cloudReprojectionDistance(direction);
168642
+ vec4 previousClip = cloudPrevViewProj * vec4(worldPosition, 1.0);
168643
+ vec2 previousUV = previousClip.xy / previousClip.w * 0.5 + 0.5;
168644
+ vec2 velocity = previousUV - vUV;
168645
+ if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) return;
168646
+ vec3 previous = clamp(textureLod(cloudHistory, previousUV, 0.0).rgb, boxMin, boxMax);
168647
+ float accumulation = cloudCamera.w;
168648
+ float intensity = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
168649
+ gl_FragColor = vec4(mix(current.rgb, previous, intensity), current.a);
168650
+ }
168651
+ `;
168652
+ }
168653
+ static WgslMarchUniforms() {
168654
+ return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n";
168655
+ }
168656
+ static WgslConstants() {
168657
+ return `const CLOUD_DENSITY_THRESHOLD: f32 = 0.001;
168658
+ const EMPTY_STEPS_BEFORE_LARGE_STEPS: i32 = 8;
168659
+ const FORWARD_ECCENTRICITY: f32 = 0.7;
168660
+ const BACKWARD_ECCENTRICITY: f32 = 0.7;
168661
+ const MIN_EROSION_DISTANCE: f32 = 3000.0;
168662
+ const MAX_EROSION_DISTANCE: f32 = 100000.0;
168663
+ const NOISE_NORMALIZATION: f32 = 100000.0;
168664
+ const MAX_SKYBOX_DISTANCE: f32 = 200000.0;
168665
+ const LIGHT_STEP_MAXIMAL_SIZE: f32 = 1000.0;
168666
+ const CLOUD_PI: f32 = 3.14159265359;
168667
+ const MAX_PRIMARY_STEPS: i32 = 1024;
168668
+ const MAX_LIGHT_STEPS: i32 = 16;
168669
+ `;
168670
+ }
168671
+ static WgslGeometry() {
168672
+ return `struct CloudRange {
168673
+ hit: bool,
168674
+ entryDistance: f32,
168675
+ exitDistance: f32,
168676
+ };
168677
+ fn cloudIntersectSphere(sphereRadius: f32, cosChi: f32, radialDistance: f32) -> vec2f {
168678
+ let ratio: f32 = sphereRadius / radialDistance;
168679
+ let d: f32 = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
168680
+ if (d < 0.0) { return vec2f(d); }
168681
+ return radialDistance * vec2f(-cosChi - sqrt(d), -cosChi + sqrt(d));
168682
+ }
168683
+ fn cloudHorizonCosine(radialDistance: f32) -> f32 {
168684
+ let sinHorizon: f32 = uniforms.cloudPlanet.w / radialDistance;
168685
+ return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
168686
+ }
168687
+ fn cloudIntersectVolume(originPS: vec3f, direction: vec3f) -> CloudRange {
168688
+ let radialDistance: f32 = length(originPS);
168689
+ let cosChi: f32 = dot(originPS, direction) / radialDistance;
168690
+ let inner: vec2f = cloudIntersectSphere(uniforms.cloudLayer.x, cosChi, radialDistance);
168691
+ let outer: vec2f = cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance);
168692
+ if (inner.x < 0.0 && inner.y >= 0.0) {
168693
+ return CloudRange(cosChi >= cloudHorizonCosine(radialDistance), inner.y, outer.y);
168694
+ }
168695
+ return CloudRange(outer.y >= 0.0, max(outer.x, 0.0), select(outer.y, inner.x, inner.x >= 0.0));
168696
+ }
168697
+ fn cloudRaySphere(startWS: vec3f, direction: vec3f, radius: f32) -> vec2f {
168698
+ let startPS: vec3f = startWS + vec3f(0.0, uniforms.cloudPlanet.w, 0.0);
168699
+ let a: f32 = dot(direction, direction);
168700
+ let b: f32 = 2.0 * dot(direction, startPS);
168701
+ let c: f32 = dot(startPS, startPS) - radius * radius;
168702
+ let d: f32 = b * b - 4.0 * a * c;
168703
+ var result: vec2f = vec2f(0.0);
168704
+ if (d >= 0.0) {
168705
+ let q: f32 = -0.5 * (b + select(-1.0, 1.0, b >= 0.0) * sqrt(d));
168706
+ result = vec2f(c / q, q / a);
168707
+ if (result.x < 0.0) { result.x = result.y; }
168708
+ }
168709
+ return result;
168710
+ }
168711
+ fn cloudRange(originWS: vec3f, direction: vec3f) -> CloudRange {
168712
+ #ifdef CLOUD_LOCAL
168713
+ return cloudIntersectVolume(originWS - uniforms.cloudPlanet.xyz, direction);
168714
+ #else
168715
+ let inner: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.x);
168716
+ let outer: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.y);
168717
+ return CloudRange(true, inner.x, outer.x);
168718
+ #endif
168719
+ }
168720
+ `;
168721
+ }
168722
+ static WgslDensity() {
168723
+ return `struct CloudProperties {
168724
+ density: f32,
168725
+ ambientOcclusion: f32,
168726
+ height: f32,
168727
+ sigmaT: f32,
168728
+ };
168729
+ fn cloudRemap(x: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
168730
+ return (((x - a) / (b - a)) * (d - c)) + c;
168731
+ }
168732
+ fn cloudNormalizedHeight(positionPS: vec3f) -> f32 {
168733
+ return clamp((length(positionPS) - uniforms.cloudLayer.x) / (uniforms.cloudLayer.y - uniforms.cloudLayer.x), 0.0, 1.0);
168734
+ }
168735
+ fn cloudAnimateShape(positionPS: vec3f) -> vec3f {
168736
+ var position: vec3f = positionPS;
168737
+ position.y = position.y + position.x / 3.0 + position.z / 7.0;
168738
+ return position + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.x + vec3f(0.0, uniforms.cloudWindSpeed.z, 0.0);
168739
+ }
168740
+ fn cloudAnimateErosion(positionPS: vec3f) -> vec3f {
168741
+ return positionPS + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.y + vec3f(0.0, uniforms.cloudWindSpeed.w, 0.0);
168742
+ }
168743
+ fn cloudCoverageHash(cell: vec2f) -> f32 {
168744
+ var p: vec3f = fract(vec3f(cell.x, cell.y, cell.x) * 0.1031);
168745
+ p = p + vec3f(dot(p, p.yzx + vec3f(33.33)));
168746
+ return fract((p.x + p.y) * p.z);
168747
+ }
168748
+ fn cloudCoverageNoise(position: vec2f) -> f32 {
168749
+ let cell: vec2f = floor(position);
168750
+ var blend: vec2f = fract(position);
168751
+ blend = blend * blend * (vec2f(3.0) - 2.0 * blend);
168752
+ let bottom: f32 = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2f(1.0, 0.0)), blend.x);
168753
+ let top: f32 = mix(cloudCoverageHash(cell + vec2f(0.0, 1.0)), cloudCoverageHash(cell + vec2f(1.0, 1.0)), blend.x);
168754
+ return mix(bottom, top, blend.y);
168755
+ }
168756
+ fn cloudCoverageAt(positionPS: vec3f) -> vec2f {
168757
+ var coverage: f32 = 0.9;
168758
+ var shapeFill: f32 = 0.0;
168759
+ if (uniforms.cloudCoverage.x != 0.0) {
168760
+ let coordinates: vec2f = (positionPS.xz + uniforms.cloudWind.zw * uniforms.cloudWindSpeed.x + uniforms.cloudCoverageOffset.xy) * uniforms.cloudCoverage.y;
168761
+ let coverageNoise: f32 = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2f(17.13, 43.71)) * 0.3;
168762
+ let threshold: f32 = mix(-0.15, 1.15, uniforms.cloudCoverage.z);
168763
+ let mask: f32 = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
168764
+ if (uniforms.cloudCoverage.x > 0.0) {
168765
+ coverage = coverage * mix(1.0, mask, uniforms.cloudCoverage.x);
168766
+ } else {
168767
+ shapeFill = clamp(-uniforms.cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
168768
+ coverage = mix(coverage, 1.0, shapeFill);
168769
+ }
168770
+ }
168771
+ return vec2f(coverage, shapeFill);
168772
+ }
168773
+ fn cloudPropertiesAt(samplePositionPS: vec3f, noiseMip: f32, erosionMip: f32, cheapVersion: bool, lightSampling: bool) -> CloudProperties {
168774
+ var properties: CloudProperties = CloudProperties(0.0, 0.0, 0.0, 0.0);
168775
+ var positionPS: vec3f = samplePositionPS;
168776
+ #ifndef CLOUD_LOCAL
168777
+ if (positionPS.y < uniforms.cloudPlanet.w) { return properties; }
168778
+ #endif
168779
+ properties.ambientOcclusion = 1.0;
168780
+ properties.height = cloudNormalizedHeight(positionPS);
168781
+ #ifndef CLOUD_LOCAL
168782
+ positionPS = vec3f(positionPS.x + uniforms.cloudEye.x, positionPS.y, positionPS.z + uniforms.cloudEye.z);
168783
+ #endif
168784
+ var baseCoordinates: vec3f = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * uniforms.cloudShape.x - uniforms.cloudShapeOffset.xyz;
168785
+ baseCoordinates = baseCoordinates + properties.height * vec3f(uniforms.cloudWind.x, uniforms.cloudWind.y, 0.0) * uniforms.cloudShapeOffset.w;
168786
+ var lowFrequencyNoise: f32 = textureSampleLevel(worleyNoise, worleyNoiseSampler, baseCoordinates, noiseMip).r;
168787
+ let coverage: vec2f = cloudCoverageAt(positionPS);
168788
+ if (coverage.x <= CLOUD_DENSITY_THRESHOLD) { return properties; }
168789
+ let curves: vec3f = textureSampleLevel(cloudCurves, cloudCurvesSampler, vec2f(properties.height, 0.5), 0.0).xyz;
168790
+ let shapeFactor: f32 = mix(0.1, 1.0, uniforms.cloudShape.y * (1.0 - coverage.y)) * curves.y;
168791
+ let erosionFactor: f32 = uniforms.cloudShape.w * curves.y;
168792
+ let microDetailFactor: f32 = uniforms.cloudDetail.z * curves.y;
168793
+ lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
168794
+ var baseCloud: f32 = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
168795
+ baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
168796
+ properties.sigmaT = 0.04;
168797
+ let occlusionBlend: f32 = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
168798
+ properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
168799
+ if (!cheapVersion) {
168800
+ let erosionCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudShape.z;
168801
+ var erosionValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, erosionCoordinates, erosionMip).r;
168802
+ erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
168803
+ properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * uniforms.cloudDetail.x), 0.0, 1.0);
168804
+ baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
168805
+ #ifdef CLOUD_MICRO_EROSION
168806
+ let fineCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudDetail.y;
168807
+ var fineValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, fineCoordinates, erosionMip).r;
168808
+ fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
168809
+ baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
168810
+ #endif
168811
+ }
168812
+ if (lightSampling) {
168813
+ baseCloud = baseCloud - erosionFactor * 0.1;
168814
+ #ifdef CLOUD_MICRO_EROSION
168815
+ baseCloud = baseCloud - microDetailFactor * 0.15;
168816
+ #endif
168817
+ }
168818
+ properties.density = max(0.0, baseCloud) * uniforms.cloudDetail.w;
168819
+ return properties;
168820
+ }
168821
+ `;
168822
+ }
168823
+ static WgslLighting() {
168824
+ return `fn cloudPhase(anisotropy: f32, cosTheta: f32) -> f32 {
168825
+ let denominator: f32 = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
168826
+ let falloff: f32 = inverseSqrt(max(denominator, 1e-6));
168827
+ return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
168828
+ }
168829
+ fn cloudPowder(density: f32, cosAngle: f32, intensity: f32) -> f32 {
168830
+ let powder: f32 = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
168831
+ return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
168832
+ }
168833
+ fn cloudSunTransmittance(positionPS: vec3f, sunDirection: vec3f, phase: vec2f) -> vec3f {
168834
+ let radialDistance: f32 = length(positionPS);
168835
+ let cosChi: f32 = dot(positionPS, sunDirection) / radialDistance;
168836
+ var transmittance: vec3f = vec3f(0.0);
168837
+ if (cosChi < cloudHorizonCosine(radialDistance)) { return transmittance; }
168838
+ let lightSteps: f32 = uniforms.cloudSteps.y;
168839
+ let totalLightDistance: f32 = clamp(cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
168840
+ let intervalSize: f32 = totalLightDistance / lightSteps;
168841
+ var opticalDepth: f32 = 0.0;
168842
+ for (var j: i32 = 0; j < MAX_LIGHT_STEPS; j++) {
168843
+ if (f32(j) >= lightSteps) { break; }
168844
+ let distanceAlongLight: f32 = intervalSize * (0.25 + f32(j));
168845
+ let lightProperties: CloudProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * f32(j) / lightSteps, 0.0, true, true);
168846
+ opticalDepth = opticalDepth + lightProperties.density * lightProperties.sigmaT;
168847
+ }
168848
+ let extinction: vec3f = intervalSize * opticalDepth * uniforms.cloudTint.rgb;
168849
+ transmittance = transmittance + exp(-extinction) * phase.x;
168850
+ transmittance = transmittance + exp(-extinction * uniforms.cloudScattering.x) * (phase.y * uniforms.cloudScattering.x);
168851
+ return transmittance;
168852
+ }
168853
+ `;
168854
+ }
168855
+ static WgslAerial() {
168856
+ const skyShaders = HdrpPbSkyShaders;
168857
+ const fogPass = HdrpFogPass;
168858
+ if (skyShaders == null || fogPass == null)
168859
+ return "";
168860
+ return "#ifdef CLOUD_AERIAL\nvar apLutSampler: sampler;\nvar apLut: texture_2d_array<f32>;\nuniform cloudApCamera: vec4f;\n"
168861
+ + skyShaders.WgslUniforms() + skyShaders.WgslCommon() + fogPass.AerialPerspectiveWgsl() + "#endif\n";
168862
+ }
168863
+ static WgslMarch() {
168864
+ const shaders = VolumetricCloudsShaders;
168865
+ return "varying vUV: vec2f;\nvar worleyNoiseSampler: sampler;\nvar worleyNoise: texture_3d<f32>;\nvar erosionNoiseSampler: sampler;\nvar erosionNoise: texture_3d<f32>;\nvar cloudCurvesSampler: sampler;\nvar cloudCurves: texture_2d<f32>;\nvar sceneDepthSampler: sampler;\nvar sceneDepth: texture_2d<f32>;\n"
168866
+ + shaders.WgslMarchUniforms() + shaders.WgslConstants() + shaders.WgslGeometry() + shaders.WgslDensity() + shaders.WgslLighting() + shaders.WgslAerial() + `
168867
+ fn cloudJenkins(value: u32) -> u32 {
168868
+ var x: u32 = value;
168869
+ x = x + (x << 10u);
168870
+ x = x ^ (x >> 6u);
168871
+ x = x + (x << 3u);
168872
+ x = x ^ (x >> 11u);
168873
+ x = x + (x << 15u);
168874
+ return x;
168875
+ }
168876
+ fn cloudRandom(pixel: vec2f, seed: f32) -> f32 {
168877
+ let v: vec3u = vec3u(u32(pixel.x), u32(pixel.y), u32(seed));
168878
+ let hash: u32 = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
168879
+ return bitcast<f32>((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
168880
+ }
168881
+ @fragment
168882
+ fn main(input: FragmentInputs) -> FragmentOutputs {
168883
+ fragmentOutputs.color = vec4f(0.0, 0.0, 0.0, 1.0);
168884
+ let uv: vec2f = fragmentInputs.vUV;
168885
+ let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
168886
+ let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
168887
+ var maxRayLength: f32 = MAX_SKYBOX_DISTANCE;
168888
+ let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
168889
+ if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
168890
+ #ifdef CLOUD_LOCAL
168891
+ maxRayLength = viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001);
168892
+ #else
168893
+ return fragmentOutputs;
168894
+ #endif
168895
+ }
168896
+ #ifdef CLOUD_LOCAL
168897
+ let originWS: vec3f = uniforms.cloudEye.xyz;
168898
+ #else
168899
+ let originWS: vec3f = vec3f(0.0);
168900
+ #endif
168901
+ let marchRange: CloudRange = cloudRange(originWS, direction);
168902
+ if (!marchRange.hit || maxRayLength < marchRange.entryDistance) { return fragmentOutputs; }
168903
+ let rangeStart: f32 = marchRange.entryDistance;
168904
+ let integrationNoise: f32 = cloudRandom(fragmentInputs.position.xy, uniforms.cloudCamera.z);
168905
+ let primarySteps: f32 = uniforms.cloudLayer.w;
168906
+ var totalDistance: f32 = min(marchRange.exitDistance, maxRayLength) - rangeStart;
168907
+ var stepSize: f32 = min(totalDistance / primarySteps, uniforms.cloudLayer.z);
168908
+ totalDistance = stepSize * primarySteps;
168909
+ let layerThickness: f32 = max(1.0, uniforms.cloudLayer.y - uniforms.cloudLayer.x);
168910
+ let sampleMultiplier: f32 = min(max(1.0, totalDistance / layerThickness), max(1.0, uniforms.cloudSteps.x));
168911
+ let primaryStepCount: i32 = min(i32(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
168912
+ stepSize = totalDistance / f32(max(1, primaryStepCount));
168913
+ let sunDirection: vec3f = uniforms.cloudSunDirection.xyz;
168914
+ let cosAngle: f32 = dot(direction, sunDirection);
168915
+ let phase: vec2f = vec2f(
168916
+ cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
168917
+ cloudPhase(FORWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle));
168918
+ var scattering: vec3f = vec3f(0.0);
168919
+ var ambient: f32 = 0.0;
168920
+ var transmittance: f32 = 1.0;
168921
+ var meanDistance: f32 = 0.0;
168922
+ var meanDistanceDivider: f32 = 0.0;
168923
+ var currentDistance: f32 = integrationNoise;
168924
+ var currentPositionWS: vec3f = originWS + (rangeStart + currentDistance) * direction;
168925
+ var activeSampling: bool = true;
168926
+ var sequentialEmptySamples: i32 = 0;
168927
+ var currentIndex: i32 = 0;
168928
+ for (var iteration: i32 = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
168929
+ if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) { break; }
168930
+ let cameraDistance: f32 = rangeStart + currentDistance;
168931
+ let densityFade: f32 = clamp((cameraDistance - uniforms.cloudSteps.z) / (uniforms.cloudSteps.z + uniforms.cloudSteps.w), 0.0, 1.0);
168932
+ let erosionMip: f32 = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
168933
+ let currentPositionPS: vec3f = currentPositionWS - uniforms.cloudPlanet.xyz;
168934
+ if (activeSampling) {
168935
+ var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
168936
+ properties.density = properties.density * densityFade;
168937
+ if (properties.density > CLOUD_DENSITY_THRESHOLD) {
168938
+ let transmittanceDensity: f32 = transmittance * properties.density;
168939
+ meanDistance = meanDistance + cameraDistance * transmittanceDensity;
168940
+ meanDistanceDivider = meanDistanceDivider + transmittanceDensity;
168941
+ let stepTransmittance: f32 = exp(-properties.density * properties.sigmaT * stepSize);
168942
+ let powder: f32 = cloudPowder(properties.density, cosAngle, uniforms.cloudScattering.y);
168943
+ let sunLuminance: vec3f = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
168944
+ let absorbed: f32 = transmittance - transmittance * stepTransmittance;
168945
+ scattering = scattering + sunLuminance * absorbed;
168946
+ ambient = ambient + properties.ambientOcclusion * absorbed;
168947
+ transmittance = transmittance * stepTransmittance;
168948
+ if (transmittance < 0.003) {
168949
+ transmittance = 0.0;
168950
+ break;
168951
+ }
168952
+ sequentialEmptySamples = 0;
168953
+ } else {
168954
+ sequentialEmptySamples = sequentialEmptySamples + 1;
168955
+ }
168956
+ if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) { activeSampling = false; }
168957
+ let relativeStepSize: f32 = mix(integrationNoise, 1.0, clamp(f32(currentIndex), 0.0, 1.0));
168958
+ currentPositionWS = currentPositionWS + direction * stepSize * relativeStepSize;
168959
+ currentDistance = currentDistance + stepSize * relativeStepSize;
168960
+ } else {
168961
+ var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
168962
+ properties.density = properties.density * densityFade;
168963
+ if (properties.density < CLOUD_DENSITY_THRESHOLD) {
168964
+ currentPositionWS = currentPositionWS + direction * stepSize * 2.0;
168965
+ currentDistance = currentDistance + stepSize * 2.0;
168966
+ } else {
168967
+ currentPositionWS = currentPositionWS - direction * stepSize;
168968
+ currentDistance = currentDistance - stepSize;
168969
+ currentIndex = currentIndex - 1;
168970
+ activeSampling = true;
168971
+ sequentialEmptySamples = 0;
168972
+ }
168973
+ }
168974
+ currentIndex = currentIndex + 1;
168975
+ }
168976
+ if (meanDistance == 0.0) { return fragmentOutputs; }
168977
+ meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
168978
+ let relativeHeight: f32 = cloudNormalizedHeight(originWS - uniforms.cloudPlanet.xyz + meanDistance * direction);
168979
+ let ambientColor: vec3f = max(vec3f(0.0), mix(uniforms.cloudAmbientBottom.rgb, uniforms.cloudAmbientTop.rgb, relativeHeight));
168980
+ var color: vec3f = (uniforms.cloudSunColor.rgb * scattering + ambientColor * ambient) * uniforms.cloudCamera.w;
168981
+ #ifdef CLOUD_AERIAL
168982
+ if (uniforms.cloudApCamera.w > 0.5) {
168983
+ let apDirection: vec3f = vec3f(direction.x * uniforms.cloudForward.w, direction.y, direction.z);
168984
+ let aerial: FogAerial = fogAerialPerspective(uv, uniforms.cloudApCamera.xyz, apDirection, meanDistance);
168985
+ color = aerial.color * (1.0 - transmittance) + (vec3f(1.0) - aerial.opacity) * color;
168986
+ }
168987
+ #endif
168988
+ fragmentOutputs.color = vec4f(color, transmittance);
168989
+ }
168990
+ `;
168991
+ }
168992
+ static WgslCompositeVertex() {
168993
+ return `attribute position: vec3f;
168994
+ varying vUV: vec2f;
168995
+ @vertex
168996
+ fn main(input: VertexInputs) -> FragmentInputs {
168997
+ vertexOutputs.vUV = vertexInputs.position.xy * 0.5 + vec2f(0.5);
168998
+ vertexOutputs.position = vec4f(vertexInputs.position.xy, 0.0, 1.0);
168999
+ }
169000
+ `;
169001
+ }
169002
+ static WgslCompositeFragment() {
169003
+ return `varying vUV: vec2f;
169004
+ var cloudLightingSampler: sampler;
169005
+ var cloudLighting: texture_2d<f32>;
169006
+ uniform cloudDisplayEncoded: f32;
169007
+ const CLOUD_DISPLAY_GAMMA: f32 = 2.2;
169008
+ const CLOUD_LUMINANCE: vec3f = vec3f(0.2126, 0.7152, 0.0722);
169009
+ @fragment
169010
+ fn main(input: FragmentInputs) -> FragmentOutputs {
169011
+ let cloud: vec4f = textureSample(cloudLighting, cloudLightingSampler, fragmentInputs.vUV);
169012
+ var scattering: vec3f = cloud.rgb;
169013
+ var throughput: f32 = cloud.a;
169014
+ if (uniforms.cloudDisplayEncoded > 0.5) {
169015
+ let encodeExponent: f32 = 1.0 / CLOUD_DISPLAY_GAMMA;
169016
+ let scatteringLuminance: f32 = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
169017
+ throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
169018
+ scattering = pow(clamp(scattering, vec3f(0.0), vec3f(1.0)), vec3f(encodeExponent));
169019
+ }
169020
+ fragmentOutputs.color = vec4f(scattering, 1.0 - throughput);
169021
+ }
169022
+ `;
169023
+ }
169024
+ static WgslTemporal() {
169025
+ const shaders = VolumetricCloudsShaders;
169026
+ return "varying vUV: vec2f;\nvar cloudCurrentSampler: sampler;\nvar cloudCurrent: texture_2d<f32>;\nvar cloudHistorySampler: sampler;\nvar cloudHistory: texture_2d<f32>;\nvar sceneDepthSampler: sampler;\nvar sceneDepth: texture_2d<f32>;\n"
169027
+ + shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n"
169028
+ + shaders.WgslGeometry() + `
169029
+ fn cloudReprojectionDistance(direction: vec3f, viewDepth: f32) -> f32 {
169030
+ #ifdef CLOUD_LOCAL
169031
+ let layerRange: CloudRange = cloudIntersectVolume(uniforms.cloudEye.xyz - uniforms.cloudPlanet.xyz, direction);
169032
+ var distanceToCloud: f32 = select(layerRange.exitDistance, layerRange.entryDistance, layerRange.hit);
169033
+ #else
169034
+ let layerRange: CloudRange = cloudRange(vec3f(0.0), direction);
169035
+ var distanceToCloud: f32 = layerRange.entryDistance;
169036
+ #endif
169037
+ distanceToCloud = max(distanceToCloud, 1.0);
169038
+ if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
169039
+ distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001));
169040
+ }
169041
+ return distanceToCloud;
169042
+ }
169043
+ @fragment
169044
+ fn main(input: FragmentInputs) -> FragmentOutputs {
169045
+ let uv: vec2f = fragmentInputs.vUV;
169046
+ let current: vec4f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv, 0.0);
169047
+ let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
169048
+ var north: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, -uniforms.cloudTexel.y), 0.0).rgb;
169049
+ var west: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(-uniforms.cloudTexel.x, 0.0), 0.0).rgb;
169050
+ var east: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(uniforms.cloudTexel.x, 0.0), 0.0).rgb;
169051
+ var south: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, uniforms.cloudTexel.y), 0.0).rgb;
169052
+ fragmentOutputs.color = current;
169053
+ if (uniforms.cloudCamera.z < 0.5 || current.a >= 1.0) { return fragmentOutputs; }
169054
+ let boxMin: vec3f = min(min(min(current.rgb, north), min(west, east)), south);
169055
+ let boxMax: vec3f = max(max(max(current.rgb, north), max(west, east)), south);
169056
+ let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
169057
+ let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
169058
+ let worldPosition: vec3f = uniforms.cloudEye.xyz + direction * cloudReprojectionDistance(direction, viewDepth);
169059
+ let previousClip: vec4f = uniforms.cloudPrevViewProj * vec4f(worldPosition, 1.0);
169060
+ let previousUV: vec2f = previousClip.xy / previousClip.w * 0.5 + vec2f(0.5);
169061
+ let velocity: vec2f = previousUV - uv;
169062
+ let history: vec3f = textureSampleLevel(cloudHistory, cloudHistorySampler, clamp(previousUV, vec2f(0.0), vec2f(1.0)), 0.0).rgb;
169063
+ if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) { return fragmentOutputs; }
169064
+ let previous: vec3f = clamp(history, boxMin, boxMax);
169065
+ let accumulation: f32 = uniforms.cloudCamera.w;
169066
+ let intensity: f32 = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
169067
+ fragmentOutputs.color = vec4f(mix(current.rgb, previous, intensity), current.a);
169068
+ }
169069
+ `;
169070
+ }
169071
+ }
169072
+ VolumetricCloudsShaders.MarchName = "tkCloudMarch";
169073
+ VolumetricCloudsShaders.CompositeName = "tkCloudComposite";
169074
+ VolumetricCloudsShaders.TemporalName = "tkCloudTemporal";
169075
+ VolumetricCloudsShaders.TemporalUniforms = ["cloudInvViewProj", "cloudPrevViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudTexel"];
169076
+ VolumetricCloudsShaders.TemporalSamplers = ["cloudCurrent", "cloudHistory", "sceneDepth"];
169077
+ VolumetricCloudsShaders.MarchUniforms = [
169078
+ "cloudInvViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudSteps", "cloudShape",
169079
+ "cloudDetail", "cloudShapeOffset", "cloudWind", "cloudWindSpeed", "cloudCoverage", "cloudCoverageOffset", "cloudScattering",
169080
+ "cloudTint", "cloudSunDirection", "cloudSunColor", "cloudAmbientTop", "cloudAmbientBottom"
169081
+ ];
169082
+ VolumetricCloudsShaders.MarchSamplers = ["worleyNoise", "erosionNoise", "cloudCurves", "sceneDepth"];
169083
+ VolumetricCloudsShaders.AerialSampler = "apLut";
169084
+ VolumetricCloudsShaders.AerialCameraUniform = "cloudApCamera";
169085
+ VolumetricCloudsShaders.registered = false;
166747
169086
  export class WebVideoPlayer extends ScriptComponent {
166748
169087
  getVideoMaterial() { return this.m_videoMaterial; }
166749
169088
  getVideoTexture() { return this.m_videoTexture; }
@@ -167098,6 +169437,7 @@ SceneManager.RegisterClass("TOOLKIT.HdrpWaterDecalSurface", HdrpWaterDecalSurfac
167098
169437
  SceneManager.RegisterClass("TOOLKIT.LensFlareComponentSRP", LensFlareComponentSRP);
167099
169438
  SceneManager.RegisterClass("TOOLKIT.LegacyLensFlare", LegacyLensFlare);
167100
169439
  SceneManager.RegisterClass("TOOLKIT.LightProbeNetwork", LightProbeNetwork);
169440
+ SceneManager.RegisterClass("TOOLKIT.MeshWaterSurface", MeshWaterSurface);
167101
169441
  SceneManager.RegisterClass("TOOLKIT.NavigationAgent", NavigationAgent);
167102
169442
  Object.defineProperty(PlayerRef, "None", { value: new PlayerRef(0), writable: false });
167103
169443
  Object.defineProperty(PlayerRef, "MasterClient", { value: new PlayerRef(-1), writable: false });
@@ -167129,11 +169469,14 @@ SceneManager.RegisterClass("TOOLKIT.ShurikenParticles", ShurikenParticles);
167129
169469
  SceneManager.RegisterClass("TOOLKIT.SkinTextureArray", SkinTextureArray);
167130
169470
  SceneManager.RegisterClass(SplineAnimate.ClassName, SplineAnimate);
167131
169471
  SceneManager.RegisterClass(SplineContainer.ClassName, SplineContainer);
169472
+ const TERRAIN_EDGE_TOLERANCE_METERS = 0.01;
167132
169473
  SceneManager.RegisterClass("TOOLKIT.TerrainBuilder", TerrainBuilder);
167133
169474
  SceneManager.RegisterClass("TOOLKIT.TreeBranchMaterial", TreeBranchMaterial);
167134
169475
  SceneManager.RegisterClass("TOOLKIT.GrassStandardMaterial", GrassStandardMaterial);
167135
169476
  SceneManager.RegisterClass("TOOLKIT.GrassBillboardMaterial", GrassBillboardMaterial);
167136
169477
  SceneManager.RegisterClass("TOOLKIT.TerrainGrassPlugin", TerrainGrassPlugin);
169478
+ const TREE_INITIAL_CAPACITY = 64;
169479
+ const TREE_GROWTH_FACTOR = 1.5;
167137
169480
  SceneManager.RegisterClass("TOOLKIT.TextureAtlasSkin", TextureAtlasSkin);
167138
169481
  SceneManager.RegisterClass(ThirdPersonController.ClassName, ThirdPersonController);
167139
169482
  SceneManager.RegisterClass("BABYLON.GUI.UnitySlider", UnitySlider);
@@ -167143,4 +169486,36 @@ SceneManager.RegisterClass("TOOLKIT.UserInterface", UserInterface);
167143
169486
  SceneManager.RegisterClass("TOOLKIT.VisualEffect", VisualEffect);
167144
169487
  const VFX_EPSILON = 1e-5;
167145
169488
  const VFX_FLT_MIN = 1.175494351e-38;
169489
+ const CLOUD_ALPHA_INDEX = -1000000;
169490
+ const CLOUD_TIER_WINDOW_FRAMES = 120;
169491
+ const CLOUD_TIER_BUDGET_MS = 33;
169492
+ const CLOUD_TIER_MINIMUM_SAVING = 0.1;
169493
+ const CLOUD_MAX_STEP_STRETCH = 4;
169494
+ const CLOUD_CAMERA_CUT_METERS = 50;
169495
+ const CLOUD_CURVE_SAMPLES = 64;
169496
+ const CLOUD_EARTH_RADIUS_METERS = 6378100;
169497
+ const CLOUD_MINIMUM_CURVATURE_SCALE = 0.025;
169498
+ const CLOUD_WIND_KMH_TO_MS = -0.277778;
169499
+ const CLOUD_WIND_WRAP_METERS = 100000;
169500
+ const CLOUD_MAX_STEP_DIVISOR = 8;
169501
+ const CLOUD_PHONE_WIDTH_CSS_PX = 800;
169502
+ const CLOUD_WEAK_GPU_TEXTURE_SIZE = 8192;
169503
+ const CLOUD_READY_POLL_MS = 16;
169504
+ const CLOUD_MINIMUM_ACCUMULATION = 0.01;
169505
+ const CLOUD_AUTO_FADE_START_FRACTION = 0.2;
169506
+ const CLOUD_AUTO_FADE_DISTANCE_FRACTION = 0.3;
169507
+ const CLOUD_FADE_MODE_MANUAL = 1;
169508
+ const CLOUD_DENSITY_SCALE = 2;
169509
+ const CLOUD_ALTITUDE_DISTORTION_SCALE = 0.25;
169510
+ const CLOUD_MULTI_SCATTERING_SCALE = 0.95;
169511
+ const CLOUD_SCATTERING_TINT_SCALE = 0.75;
169512
+ const CLOUD_MINIMUM_COVERAGE_PATTERN_METERS = 100;
169513
+ const CLOUD_DEFAULT_COVERAGE_PATTERN_METERS = 10000;
169514
+ const CLOUD_DEFAULT_COVERAGE_GAP = 0.45;
169515
+ const CLOUD_DEFAULT_SETTINGS = { resolutionScale: 0.5, primarySteps: 32, longRayMultiplier: 1, lightSteps: 2 };
169516
+ const CLOUD_TIER_CAPS = [
169517
+ { resolutionScale: Infinity, primarySteps: 64, longRayMultiplier: 2, lightSteps: Infinity },
169518
+ { resolutionScale: 0.35, primarySteps: 64, longRayMultiplier: 1, lightSteps: 2 },
169519
+ { resolutionScale: 0.25, primarySteps: 64, longRayMultiplier: 1, lightSteps: 1 }
169520
+ ];
167146
169521
  SceneManager.RegisterClass("TOOLKIT.WebVideoPlayer", WebVideoPlayer);