@babylonjs-toolkit/next 9.34.1 → 9.34.100

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.
@@ -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.1 - R1"; }
146
+ static get Version() { return "9.34.100 - R1"; }
147
147
  static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
148
148
  static IsPrecompilingShaders() {
149
149
  return SceneManager.PrecompileShaders !== false;
@@ -338,6 +338,11 @@ export class SceneManager {
338
338
  await assetsManager.loadAsync();
339
339
  }
340
340
  static ShowLoadingScreen(engine, hideLoadingUIWithEngine = true, defaultLoadingUIMarginTop = "150px") {
341
+ const pageCover = (LoadingScreenStages != null) ? LoadingScreenStages.FindPageCover() : null;
342
+ if (pageCover != null) {
343
+ LoadingScreenStages.AttachScreen(pageCover, defaultLoadingUIMarginTop);
344
+ return;
345
+ }
341
346
  if (engine.loadingScreen != null) {
342
347
  SceneManager._HideLoadingScreen = engine.loadingScreen.hideLoadingUI.bind(engine.loadingScreen);
343
348
  if (hideLoadingUIWithEngine === false)
@@ -348,9 +353,20 @@ export class SceneManager {
348
353
  if (anyLoadingScreen._loadingTextDiv != null && anyLoadingScreen._loadingTextDiv.style != null) {
349
354
  anyLoadingScreen._loadingTextDiv.style.marginTop = defaultLoadingUIMarginTop;
350
355
  }
356
+ if (LoadingScreenStages != null)
357
+ LoadingScreenStages.Attach(anyLoadingScreen, defaultLoadingUIMarginTop);
351
358
  }
352
359
  }
353
360
  static HideLoadingScreen(engine, fade = true) {
361
+ if (LoadingScreenStages != null) {
362
+ const unrevealed = LoadingScreenStages.SceneToReveal(engine);
363
+ if (unrevealed != null) {
364
+ SceneManager._FadeLoadingScreen = fade;
365
+ SceneManager.HideSplashScreen(unrevealed);
366
+ return;
367
+ }
368
+ LoadingScreenStages.Detach();
369
+ }
354
370
  if (engine.loadingScreen != null) {
355
371
  if (fade === false) {
356
372
  const anyLoadingScreen = engine.loadingScreen;
@@ -470,9 +486,11 @@ export class SceneManager {
470
486
  });
471
487
  }
472
488
  static RevealSplashScreen(scene) {
489
+ const fade = SceneManager._FadeLoadingScreen;
490
+ SceneManager._FadeLoadingScreen = true;
473
491
  if (scene != null && !scene.isDisposed) {
474
492
  try {
475
- SceneManager.HideLoadingScreen(scene.getEngine());
493
+ SceneManager.HideLoadingScreen(scene.getEngine(), fade);
476
494
  }
477
495
  catch (e) {
478
496
  console.warn(e);
@@ -490,6 +508,16 @@ export class SceneManager {
490
508
  console.warn(e);
491
509
  }
492
510
  }
511
+ else if (LoadingScreenStages != null && LoadingScreenStages.IsShowing()) {
512
+ const engine = (scene != null) ? scene.getEngine() : EngineStore.LastCreatedEngine;
513
+ try {
514
+ if (engine != null)
515
+ SceneManager.HideLoadingScreen(engine, fade);
516
+ }
517
+ catch (e) {
518
+ console.warn(e);
519
+ }
520
+ }
493
521
  if (typeof document === "undefined")
494
522
  return;
495
523
  try {
@@ -630,7 +658,7 @@ export class SceneManager {
630
658
  if (splash == null)
631
659
  splash = document.getElementById("xbabylonjsLoadingDiv");
632
660
  if (splash == null)
633
- return false;
661
+ return (LoadingScreenStages != null && LoadingScreenStages.IsShowing());
634
662
  const style = window.getComputedStyle(splash);
635
663
  return style.display !== "none" && style.visibility !== "hidden" && splash.style.opacity !== "0";
636
664
  }
@@ -5511,6 +5539,7 @@ SceneManager.AllLayerMask = 0xFFFFFFFF;
5511
5539
  SceneManager.DefaultLayerMask = 0x0FFFFFFF;
5512
5540
  SceneManager.HiddenLayerMask = 0x10000000;
5513
5541
  SceneManager._HideLoadingScreen = null;
5542
+ SceneManager._FadeLoadingScreen = true;
5514
5543
  SceneManager.CVTOOLS_NAME = "CVTOOLS_unity_metadata";
5515
5544
  SceneManager.CVTOOLS_MESH = "CVTOOLS_babylon_mesh";
5516
5545
  SceneManager.CVTOOLS_HAND = "CVTOOLS_left_handed";
@@ -9169,7 +9198,6 @@ export class CVTOOLS_unity_metadata {
9169
9198
  return;
9170
9199
  const levels = [];
9171
9200
  const rendererCounts = [];
9172
- let multiRenderer = false;
9173
9201
  let totalMeshes = 0;
9174
9202
  for (let index = 0; index < levelCount; index++) {
9175
9203
  const entry = lodentries[index];
@@ -9183,8 +9211,6 @@ export class CVTOOLS_unity_metadata {
9183
9211
  meshes.push(lodnode);
9184
9212
  }
9185
9213
  });
9186
- if (meshes.length > 1)
9187
- multiRenderer = true;
9188
9214
  totalMeshes += meshes.length;
9189
9215
  rendererCounts.push(meshes.length);
9190
9216
  levels.push({ distance: thresholds[index], meshes: meshes });
@@ -9192,14 +9218,15 @@ export class CVTOOLS_unity_metadata {
9192
9218
  if (totalMeshes === 0)
9193
9219
  return;
9194
9220
  const fadeMode = UnityLodGroups.ParseFadeMode(metadata.fademode);
9195
- 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
9196
9223
  && levels[0].meshes.length === 1 && levels[0].meshes[0] instanceof Mesh && !(levels[0].meshes[0] instanceof InstancedMesh));
9197
9224
  if (nativeCandidate === true && this._setupNativeCoverageLevelOfDetail(scene, transform, levels, lodBias, metadata) === true) {
9198
9225
  }
9199
9226
  else if (useDistances === false) {
9200
9227
  this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_COVERAGE, lodBias, metadata);
9201
9228
  }
9202
- else if (multiRenderer === true || fadeMode !== UnityLodGroups.FADE_NONE) {
9229
+ else if (requiresSwitcher === true) {
9203
9230
  this._setupLevelOfDetailSwitcher(scene, transform, levels, rendererCounts, UnityLodGroups.MODE_DISTANCE, lodBias, metadata);
9204
9231
  }
9205
9232
  else {
@@ -9706,10 +9733,14 @@ export class CVTOOLS_unity_metadata {
9706
9733
  return { mesh: skyboxMesh, texture: skyboxTexture };
9707
9734
  };
9708
9735
  const pbSkyClass = HdrpPhysicallyBasedSky;
9709
- 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;
9710
9738
  const liveSky = (pbSkyClass != null && hdrpSkyType === "physicallybased")
9711
9739
  ? pbSkyClass.TryCreate(babylonScene, metadata, root, { rendergroup: rendergroup, bakedSky: hasBakedSkyFaces ? (() => buildBakedSky().mesh) : null })
9712
9740
  : null;
9741
+ const volumetricClouds = VolumetricClouds;
9742
+ if (volumetricClouds != null)
9743
+ volumetricClouds.TryCreate(babylonScene, metadata, root);
9713
9744
  const graphSky = (liveSky == null && hdrpSkyType !== "none" && typeof skybox.customMaterial === "string" && skybox.bags != null && ShaderGraphSky != null)
9714
9745
  ? ShaderGraphSky.CreateGraphSkybox(babylonScene, skybox.bags, { size: skysize, rendergroup: rendergroup, skyfog: skyfog, tags: skytags, environment: (skybox.environment == null && babylonScene.environmentTexture == null) })
9715
9746
  : null;
@@ -9816,8 +9847,10 @@ export class CVTOOLS_unity_metadata {
9816
9847
  const hasDiffuseIbl = Array.isArray(environment.sh) && environment.sh.length === 27;
9817
9848
  const useUnitySH = (mode === 0 && hasDiffuseIbl === true);
9818
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));
9819
9852
  if (useUnitySH)
9820
- this.generateSphericalHarmonics(babylonScene, environment.sh, diffuseIbl, (envinfo != null && envinfo !== "") ? environmentLevel : null);
9853
+ this.generateSphericalHarmonics(babylonScene, environment.sh, ambientScale, hasEnvironmentFile ? environmentLevel : null);
9821
9854
  if (envinfo != null && envinfo !== "") {
9822
9855
  try {
9823
9856
  const root = (this._rootUrl != null) ? this._rootUrl : "/";
@@ -9925,6 +9958,8 @@ export class CVTOOLS_unity_metadata {
9925
9958
  }
9926
9959
  if (metadata.shaderglobals != null)
9927
9960
  ShaderGlobals.LoadSceneGlobals(babylonScene, metadata.shaderglobals);
9961
+ ShaderGlobals.LoadSceneDrivers(babylonScene, metadata.shaderglobaldrivers);
9962
+ ShaderGraphRuntime.RecordExportReport(babylonScene, metadata.exportreport);
9928
9963
  if (metadata.lights2d != null && Light2DTexture != null)
9929
9964
  Light2DTexture.Load(babylonScene, metadata.lights2d);
9930
9965
  if (metadata.renderfeatures != null && ShaderGraphPass != null)
@@ -15402,6 +15437,8 @@ export class LightingConversions {
15402
15437
  static UnityLinearLightColor(light) {
15403
15438
  if (light == null || light.diffuse == null)
15404
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();
15405
15442
  const rider = (light.metadata != null && light.metadata.toolkit != null) ? light.metadata.toolkit.colorTemperature : null;
15406
15443
  const d = light.diffuse;
15407
15444
  const channel = (value, factor) => {
@@ -15414,6 +15451,18 @@ export class LightingConversions {
15414
15451
  const tb = (rider != null) ? rider.b : 1.0;
15415
15452
  return new Color3(channel(d.r, tr), channel(d.g, tg), channel(d.b, tb));
15416
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
+ }
15417
15466
  static SetShadowFade(scene, fade) {
15418
15467
  if (scene == null || !Array.isArray(fade) || fade.length < 2)
15419
15468
  return;
@@ -15869,6 +15918,10 @@ export class LightingConversions {
15869
15918
  return Math.max(0.0, reflection);
15870
15919
  return Math.min(levelMax, Math.max(0.0, reflection));
15871
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
+ }
15872
15925
  static WarnBakedSkyWithoutEnvironment(skybox) {
15873
15926
  if (skybox == null || skybox.bakedsky !== true || skybox.environment != null)
15874
15927
  return false;
@@ -16290,6 +16343,167 @@ LoadingProgress.Clock = () => ((typeof performance !== "undefined") ? performanc
16290
16343
  LoadingProgress.Jobs = new Map();
16291
16344
  LoadingProgress.PostStates = new Map();
16292
16345
  LoadingProgress.DisposeHooks = new Set();
16346
+ export class LoadingScreenStages {
16347
+ static Attach(loadingScreen, marginTop) {
16348
+ if (typeof document === "undefined" || loadingScreen == null)
16349
+ return;
16350
+ const screens = loadingScreen._loadingDivToRenderingCanvasMap;
16351
+ if (screens == null || typeof screens.forEach !== "function")
16352
+ return;
16353
+ const covers = [];
16354
+ screens.forEach((_canvas, screen) => { covers.push(screen); });
16355
+ LoadingScreenStages.Begin(covers, marginTop);
16356
+ }
16357
+ static FindPageCover() {
16358
+ if (typeof document === "undefined" || document.getElementById("xbabylonjsSplashScreen") != null)
16359
+ return null;
16360
+ return document.getElementById("xbabylonjsLoadingDiv");
16361
+ }
16362
+ static AttachScreen(screen, marginTop) {
16363
+ if (typeof document === "undefined" || screen == null)
16364
+ return;
16365
+ LoadingScreenStages.Begin([screen], marginTop);
16366
+ }
16367
+ static Begin(covers, marginTop) {
16368
+ LoadingScreenStages.Detach(true);
16369
+ if (covers.length === 0)
16370
+ return;
16371
+ covers.forEach((screen) => {
16372
+ screen.style.opacity = "0.995";
16373
+ screen.style.willChange = "opacity";
16374
+ LoadingScreenStages.Views.push(LoadingScreenStages.CreateView(screen, marginTop));
16375
+ });
16376
+ LoadingScreenStages.BarState = -1;
16377
+ LoadingScreenStages.BarProgress = null;
16378
+ LoadingScreenStages.PreviousHostDefers = SceneManager.HostDefersSceneStatus;
16379
+ SceneManager.HostDefersSceneStatus = true;
16380
+ LoadingScreenStages.Observer = SceneManager.OnLoaderStatusObservable.add(LoadingScreenStages.OnLoaderStatus);
16381
+ }
16382
+ static IsShowing() {
16383
+ return LoadingScreenStages.Views.some((view) => view.screen.isConnected !== false && view.screen.style.opacity !== "0");
16384
+ }
16385
+ static SceneToReveal(engine) {
16386
+ if (SceneShaderWarmup == null || LoadingScreenStages.IsShowing() !== true)
16387
+ return null;
16388
+ const scene = (engine.scenes != null && engine.scenes.length > 0) ? engine.scenes[engine.scenes.length - 1] : null;
16389
+ if (scene == null || scene.isDisposed || SceneShaderWarmup.IsSceneRevealed(scene))
16390
+ return null;
16391
+ return scene;
16392
+ }
16393
+ static FadeScreen(screen) {
16394
+ if (screen == null)
16395
+ return;
16396
+ const onFadeEnd = (event) => {
16397
+ if (event.target !== screen || event.propertyName !== "opacity")
16398
+ return;
16399
+ screen.removeEventListener("transitionend", onFadeEnd);
16400
+ screen.remove();
16401
+ };
16402
+ screen.addEventListener("transitionend", onFadeEnd);
16403
+ screen.style.opacity = "0";
16404
+ LoadingScreenStages.Detach();
16405
+ }
16406
+ static GetBarState() {
16407
+ return LoadingScreenStages.BarState;
16408
+ }
16409
+ static ShowStatus(status, details, progress, state) {
16410
+ const barProgress = LoadingScreenStages.NextBarProgress(progress, state);
16411
+ LoadingScreenStages.Views.forEach((view) => {
16412
+ if (status != null && status !== "")
16413
+ view.status.textContent = LoadingScreenStages.ToSentenceCase(status);
16414
+ if (details != null)
16415
+ view.details.textContent = LoadingScreenStages.ToSentenceCase(details);
16416
+ if (barProgress != null) {
16417
+ if (view.slide != null) {
16418
+ view.slide.cancel();
16419
+ view.slide = null;
16420
+ }
16421
+ view.fill.style.opacity = "";
16422
+ view.fill.style.width = Math.round(barProgress * 100) + "%";
16423
+ view.stage.setAttribute("aria-valuenow", "" + Math.round(barProgress * 100));
16424
+ }
16425
+ });
16426
+ }
16427
+ static Detach(removeStages = false) {
16428
+ if (LoadingScreenStages.Observer != null) {
16429
+ SceneManager.OnLoaderStatusObservable.remove(LoadingScreenStages.Observer);
16430
+ LoadingScreenStages.Observer = null;
16431
+ SceneManager.HostDefersSceneStatus = LoadingScreenStages.PreviousHostDefers;
16432
+ }
16433
+ LoadingScreenStages.Views.forEach((view) => {
16434
+ if (view.slide != null)
16435
+ view.slide.cancel();
16436
+ if (removeStages === true) {
16437
+ view.stage.remove();
16438
+ view.details.remove();
16439
+ }
16440
+ });
16441
+ LoadingScreenStages.Views = [];
16442
+ }
16443
+ static CreateView(screen, marginTop) {
16444
+ const details = document.createElement("div");
16445
+ details.style.cssText = "position:absolute;right:18px;bottom:12px;font-family:Arial;font-size:12px;color:white;text-align:right;z-index:2;opacity:0.9;letter-spacing:0.3px";
16446
+ const stage = document.createElement("div");
16447
+ stage.setAttribute("role", "progressbar");
16448
+ stage.setAttribute("aria-valuemin", "0");
16449
+ stage.setAttribute("aria-valuemax", "100");
16450
+ stage.style.cssText = "position:absolute;left:50%;top:50%;transform:translateX(-50%);width:min(320px, calc(100% - 32px));font-family:Arial;font-size:14px;color:white;text-align:center;z-index:1";
16451
+ stage.style.marginTop = marginTop;
16452
+ const status = document.createElement("div");
16453
+ status.style.cssText = "min-height:18px;margin-bottom:10px;letter-spacing:0.3px";
16454
+ status.textContent = LoadingScreenStages.START_TEXT;
16455
+ const track = document.createElement("div");
16456
+ track.style.cssText = "position:relative;width:100%;height:4px;overflow:hidden;border-radius:2px;background-color:rgba(255, 255, 255, 0.15)";
16457
+ const fill = document.createElement("div");
16458
+ fill.style.cssText = "width:30%;height:100%;border-radius:2px;transition:width 0.25s ease-out";
16459
+ fill.style.backgroundColor = LoadingScreenStages.BAR_COLOR;
16460
+ track.appendChild(fill);
16461
+ stage.appendChild(status);
16462
+ stage.appendChild(track);
16463
+ screen.appendChild(details);
16464
+ screen.appendChild(stage);
16465
+ return { screen: screen, stage: stage, status: status, details: details, fill: fill, slide: LoadingScreenStages.StartSlide(fill) };
16466
+ }
16467
+ static StartSlide(fill) {
16468
+ const reduced = (typeof window.matchMedia === "function") && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
16469
+ if (reduced || typeof fill.animate !== "function") {
16470
+ fill.style.width = "100%";
16471
+ fill.style.opacity = "0.4";
16472
+ return null;
16473
+ }
16474
+ return fill.animate([{ transform: "translateX(-100%)" }, { transform: "translateX(340%)" }], { duration: LoadingScreenStages.SLIDE_DURATION_MS, iterations: Infinity, easing: "ease-in-out" });
16475
+ }
16476
+ static OnLoaderStatus(update) {
16477
+ if (LoadingScreenStages.IsShowing() !== true) {
16478
+ LoadingScreenStages.Detach();
16479
+ return;
16480
+ }
16481
+ LoadingScreenStages.ShowStatus(update.status, update.details, update.progress, update.state);
16482
+ }
16483
+ static NextBarProgress(progress, state) {
16484
+ if (typeof progress !== "number" || !isFinite(progress))
16485
+ return null;
16486
+ const value = Math.max(0, Math.min(1, progress));
16487
+ const restart = (state !== LoadingScreenStages.BarState || !(state >= 0));
16488
+ LoadingScreenStages.BarState = state;
16489
+ LoadingScreenStages.BarProgress = (restart || LoadingScreenStages.BarProgress == null) ? value : Math.max(LoadingScreenStages.BarProgress, value);
16490
+ return LoadingScreenStages.BarProgress;
16491
+ }
16492
+ static ToSentenceCase(text) {
16493
+ if (text === "" || text !== text.toUpperCase())
16494
+ return text;
16495
+ const lower = text.toLowerCase();
16496
+ return lower.charAt(0).toUpperCase() + lower.slice(1);
16497
+ }
16498
+ }
16499
+ LoadingScreenStages.START_TEXT = "Loading Scene ...";
16500
+ LoadingScreenStages.BAR_COLOR = "#E0684B";
16501
+ LoadingScreenStages.SLIDE_DURATION_MS = 1200;
16502
+ LoadingScreenStages.Views = [];
16503
+ LoadingScreenStages.Observer = null;
16504
+ LoadingScreenStages.PreviousHostDefers = false;
16505
+ LoadingScreenStages.BarState = -1;
16506
+ LoadingScreenStages.BarProgress = null;
16293
16507
  export class MaterialSamplerBudget {
16294
16508
  static InstallForScene(scene) {
16295
16509
  if (scene == null)
@@ -17420,6 +17634,7 @@ export class MetadataParser {
17420
17634
  babylonLight.metadata.toolkit.hdrpSky = component.hdrpsky;
17421
17635
  babylonLight.metadata.toolkit.intensityFactor = intensityFactor;
17422
17636
  babylonLight.metadata.toolkit.colorTemperature = (component.colortemperature != null) ? Utilities.ParseColor3(component.colortemperature, Color3.White()) : null;
17637
+ LightingConversions.LinearizeLightColor(babylonLight, scene);
17423
17638
  if (component.cookie != null)
17424
17639
  UnityLightCookies.Apply(scene, babylonLight, entity, component.cookie);
17425
17640
  babylonLight.shadowEnabled = false;
@@ -17908,6 +18123,10 @@ export class OpaqueSceneColor {
17908
18123
  }
17909
18124
  return all;
17910
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
+ }
17911
18130
  static QueueSamples(subMeshes, materials) {
17912
18131
  if (subMeshes == null || materials == null || materials.size === 0)
17913
18132
  return false;
@@ -17978,7 +18197,7 @@ export class OpaqueSceneColor {
17978
18197
  if (!copier.isReady())
17979
18198
  return;
17980
18199
  engine.unBindFramebuffer(current, true);
17981
- copier.copy(current.texture, destination);
18200
+ copier.copy(current.texture, destination, OpaqueSceneColor.CopyConversionFor(scene.imageProcessingConfiguration));
17982
18201
  if (target.__tkMips === true && target.getInternalTexture() != null && typeof engine.generateMipmaps === "function")
17983
18202
  engine.generateMipmaps(target.getInternalTexture());
17984
18203
  engine.bindFramebuffer(current, undefined, undefined, undefined, true);
@@ -17989,6 +18208,27 @@ export class OpaqueSceneColor {
17989
18208
  OpaqueSceneColor.Ratio = null;
17990
18209
  OpaqueSceneColor.StoreKey = "__tkOpaqueSceneColor";
17991
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;
17992
18232
  export class FixedStepClock {
17993
18233
  constructor(fixedDeltaTime = FixedStepClock.DEFAULT_FIXED_DELTA_TIME, maximumDeltaTime = FixedStepClock.DEFAULT_MAXIMUM_DELTA_TIME) {
17994
18234
  this.accumulatedTime = 0;
@@ -19257,7 +19497,9 @@ export class SceneShaderWarmup {
19257
19497
  SceneShaderWarmup.SkipFrustumClipping(scene, frustumState);
19258
19498
  const drawHidden = drawWholeScene && ownPipelines && options.includeHiddenMeshes === true && SceneShaderWarmup.DrawHiddenMeshesOnReveal === true;
19259
19499
  const shownHiddenMeshes = drawHidden ? SceneShaderWarmup.ShowHiddenMeshesForDraws(scene) : [];
19260
- 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);
19261
19503
  const shadowListWraps = SceneShaderWarmup.KeepHiddenCastersInShadowMaps(scene, shownHiddenMeshes);
19262
19504
  const scaleVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateScaleVariants(scene) : null;
19263
19505
  const materialVariantDraw = (drawWholeScene && ownPipelines) ? SceneShaderWarmup.AlternateMaterialVariants(scene) : null;
@@ -19268,10 +19510,11 @@ export class SceneShaderWarmup {
19268
19510
  const stageFraction = (requested > startRequested) ? (compiled - startCompiled) / (requested - startRequested) : 0;
19269
19511
  const pipelineStep = { step: "pipelines", ready: compiled - startCompiled, total: requested - startRequested };
19270
19512
  report(effects.ready + compiled - startCompiled, effects.total + requested - startRequested + 1, effectShare + (1 - effectShare) * stageFraction * 0.99, pipelineStep);
19271
- }).then((timedOut) => {
19513
+ }, settleFrames).then((timedOut) => {
19272
19514
  SceneShaderWarmup.RestoreFrustumClipping(scene, frustumState);
19273
19515
  SceneShaderWarmup.RestoreShadowCasterLists(shadowListWraps);
19274
- SceneShaderWarmup.RestorePunctualShadowMaps(punctualShadowDraws);
19516
+ SceneShaderWarmup.RestoreFullShadowMaps(fullShadowDraws);
19517
+ SceneShaderWarmup.RestoreNativeLodLevels(scene, lodCycle);
19275
19518
  SceneShaderWarmup.RestoreHiddenMeshes(scene, shownHiddenMeshes);
19276
19519
  SceneShaderWarmup.RestoreScaleVariants(scene, scaleVariantDraw);
19277
19520
  SceneShaderWarmup.RestoreMaterialVariants(scene, materialVariantDraw);
@@ -20991,13 +21234,15 @@ export class SceneShaderWarmup {
20991
21234
  wrap.shadowMap.getCustomRenderList = wrap.customList;
20992
21235
  }
20993
21236
  }
20994
- static DrawPunctualShadowMapsFully(scene) {
21237
+ static DrawShadowMapsFully(scene) {
20995
21238
  const draws = [];
20996
21239
  for (const shadowMap of SceneShaderWarmup.CollectShadowMaps(scene)) {
20997
21240
  const renderGate = shadowMap;
20998
- if (renderGate._tkSkipInvisible !== true)
21241
+ const isGated = renderGate._tkSkipInvisible === true;
21242
+ if (!isGated && !SceneShaderWarmup.HasCasterCullingList(shadowMap))
20999
21243
  continue;
21000
- renderGate._tkPrewarmDrawAll = true;
21244
+ if (isGated)
21245
+ renderGate._tkPrewarmDrawAll = true;
21001
21246
  draws.push({ shadowMap: shadowMap, customList: shadowMap.getCustomRenderList });
21002
21247
  shadowMap.getCustomRenderList = null;
21003
21248
  try {
@@ -21007,7 +21252,11 @@ export class SceneShaderWarmup {
21007
21252
  }
21008
21253
  return draws;
21009
21254
  }
21010
- 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) {
21011
21260
  for (const draw of draws) {
21012
21261
  draw.shadowMap._tkPrewarmDrawAll = false;
21013
21262
  if (draw.shadowMap.getCustomRenderList == null)
@@ -21239,6 +21488,53 @@ export class SceneShaderWarmup {
21239
21488
  draw.frameObserver = null;
21240
21489
  SceneShaderWarmup.SetScaleVariantForced(draw.entries, false);
21241
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
+ }
21242
21538
  static SetScaleVariantForced(entries, forced) {
21243
21539
  const prototypeUpdate = AbstractMesh.prototype._updateNonUniformScalingState;
21244
21540
  for (const entry of entries) {
@@ -21382,8 +21678,8 @@ export class SceneShaderWarmup {
21382
21678
  }
21383
21679
  return phases;
21384
21680
  }
21385
- static WaitForPipelinesPerCamera(scene, pipelines, deadline, phases, onProgress) {
21386
- 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);
21387
21683
  }
21388
21684
  static RunCameraPhasesAsync(scene, phases, noPhaseRun, runStep, isTimedOut) {
21389
21685
  const runPhase = (phaseIndex, previous) => {
@@ -21401,7 +21697,7 @@ export class SceneShaderWarmup {
21401
21697
  };
21402
21698
  return runPhase(0, noPhaseRun);
21403
21699
  }
21404
- static WaitForPipelines(scene, pipelines, deadline, onProgress) {
21700
+ static WaitForPipelines(scene, pipelines, deadline, onProgress, settleFrames = SceneShaderWarmup.PIPELINE_SETTLE_FRAMES) {
21405
21701
  return new Promise((resolve) => {
21406
21702
  let quietFrames = 0;
21407
21703
  let lastRequested = -1;
@@ -21432,7 +21728,7 @@ export class SceneShaderWarmup {
21432
21728
  quietFrames = quiet ? quietFrames + 1 : 0;
21433
21729
  if (onProgress != null)
21434
21730
  onProgress(pipelines.compiled, pipelines.requested);
21435
- if (quietFrames >= SceneShaderWarmup.PIPELINE_SETTLE_FRAMES)
21731
+ if (quietFrames >= settleFrames)
21436
21732
  finish(false);
21437
21733
  });
21438
21734
  timer = setTimeout(() => finish(true), Math.max(0, deadline - SceneShaderWarmup.NowMs()));
@@ -21989,6 +22285,7 @@ SceneShaderWarmup.BuildScenePipelinesOnReveal = true;
21989
22285
  SceneShaderWarmup.DrawHiddenMeshesOnReveal = true;
21990
22286
  SceneShaderWarmup.WarmEmptyThinInstancesOnReveal = true;
21991
22287
  SceneShaderWarmup.WarmScaleVariantsOnReveal = true;
22288
+ SceneShaderWarmup.WarmNativeLodLevelsOnReveal = true;
21992
22289
  SceneShaderWarmup.OPTIONAL_UNIT_GRACE_MS = 3000;
21993
22290
  SceneShaderWarmup.GIVEN_UP_NAMES_SHOWN = 10;
21994
22291
  SceneShaderWarmup.LateCompileReporting = null;
@@ -23625,6 +23922,27 @@ export class ShaderGlobals {
23625
23922
  ShaderGlobals._keywords.add(k);
23626
23923
  ShaderGlobals._version++;
23627
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
+ }
23628
23946
  static GetClock(scene) {
23629
23947
  if (scene == null)
23630
23948
  return { time: 0, deltaTime: 0, smoothDeltaTime: 0, frame: 0 };
@@ -23659,6 +23977,7 @@ export class ShaderGlobals {
23659
23977
  return clock;
23660
23978
  }
23661
23979
  }
23980
+ ShaderGlobals.DriverComponentNames = ["x", "y", "z", "w"];
23662
23981
  ShaderGlobals._version = 0;
23663
23982
  ShaderGlobals._floats = new Map();
23664
23983
  ShaderGlobals._vectors = new Map();
@@ -24883,6 +25202,23 @@ export class ShaderGraphRuntime {
24883
25202
  loadTexture(textures[k], (texture) => { clone.setTextureValue(k, texture); });
24884
25203
  return clone;
24885
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
+ }
24886
25222
  static FlushSceneSummary(scene) {
24887
25223
  const record = ShaderGraphRuntime._records.get(scene != null ? scene.uniqueId : -1);
24888
25224
  if (record == null || record.scene !== scene)
@@ -24902,6 +25238,8 @@ export class ShaderGraphRuntime {
24902
25238
  const report = (ShaderGraphRuntime.ReportDeviations != null) ? ShaderGraphRuntime.ReportDeviations
24903
25239
  : (SceneManager != null && typeof SceneManager.IsDebugMode === "function" ? SceneManager.IsDebugMode() : true);
24904
25240
  const n = record.materials.size, m = record.lastResorts.size;
25241
+ if (report && record.exportReport != null)
25242
+ ShaderGraphRuntime.PrintExportReport(record);
24905
25243
  if (report && (n > 0 || m > 0)) {
24906
25244
  console.groupCollapsed("Shader Graph: " + n + " materials with deviations, " + m + " last resort" + (record.flushed ? " (built after the scene summary)" : ""));
24907
25245
  record.materials.forEach((entry, name) => {
@@ -29628,8 +29966,9 @@ export class UnityLodGroups {
29628
29966
  static ExportedSize(root, metadata) {
29629
29967
  if (root == null || metadata == null || !(metadata.lodsize > 0))
29630
29968
  return null;
29631
- const c = metadata.lodcenter;
29632
- 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();
29633
29972
  const scale = new Vector3();
29634
29973
  root.computeWorldMatrix(true).decompose(scale, null, null);
29635
29974
  const largest = Math.max(Math.abs(scale.x), Math.abs(scale.y), Math.abs(scale.z));
@@ -29640,6 +29979,35 @@ export class UnityLodGroups {
29640
29979
  const useDistances = !hasCoverages && (distances != null && distances.length > 0);
29641
29980
  return { useDistances: useDistances, thresholds: useDistances ? distances : (hasCoverages ? coverages : null) };
29642
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
+ }
29643
30011
  static ParseFadeMode(mode) {
29644
30012
  const m = (mode != null) ? String(mode).toLowerCase() : "";
29645
30013
  if (m === "crossfade")
@@ -29802,21 +30170,25 @@ export class UnityLodGroups {
29802
30170
  addNativeGroup(master, details, coverages, worldSize) {
29803
30171
  if (!(master instanceof Mesh) || master instanceof InstancedMesh || coverages == null)
29804
30172
  return false;
29805
- const n = Math.min(coverages.length, details.length + 1);
29806
- if (n <= 0)
30173
+ if (master.instances.length > 0)
29807
30174
  return false;
29808
- for (let k = 0; k < n - 1; k++) {
29809
- const d = details[k];
29810
- if (!(d instanceof Mesh) || d instanceof InstancedMesh || d === master || d._masterMesh != null || details.indexOf(d) !== k)
30175
+ const levelCount = Math.min(coverages.length, details.length + 1);
30176
+ if (levelCount <= 0)
30177
+ return false;
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)
29811
30181
  return false;
29812
30182
  }
29813
- 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++)
29814
30186
  master.addLODLevel(1e6 + k, details[k - 1]);
29815
- master.addLODLevel(1e6 + n, null);
30187
+ master.addLODLevel(1e6 + levelCount, null);
29816
30188
  const all = master.getLODLevels();
29817
30189
  const entries = [];
29818
- for (let i = 0; i < n; i++) {
29819
- 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;
29820
30192
  let found = null;
29821
30193
  for (const level of all) {
29822
30194
  if (level.mesh === mesh && entries.indexOf(level) === -1) {
@@ -29826,7 +30198,7 @@ export class UnityLodGroups {
29826
30198
  }
29827
30199
  entries.push(found);
29828
30200
  }
29829
- 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 });
29830
30202
  this._nativeKey[0] = NaN;
29831
30203
  return true;
29832
30204
  }
@@ -30076,6 +30448,9 @@ UnityLodGroups.FADE_NONE = 0;
30076
30448
  UnityLodGroups.FADE_CROSSFADE = 1;
30077
30449
  UnityLodGroups.FADE_SPEEDTREE = 2;
30078
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];
30079
30454
  UnityLodGroups.DeferLoadFadeClones = true;
30080
30455
  UnityLodGroups.FadeCloneSliceMs = 8;
30081
30456
  UnityLodGroups._cloneQueues = new Map();
@@ -69707,16 +70082,36 @@ export class HdrpFogPass {
69707
70082
  return HdrpFogPass.Uniforms.concat((S != null && Array.isArray(S.AtmosphereUniforms)) ? S.AtmosphereUniforms : [], ["scale"]);
69708
70083
  }
69709
70084
  static FogBlock(scene) {
69710
- 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);
69711
70091
  return (block != null && block.fog != null && typeof block.fog === "object") ? block.fog : null;
69712
70092
  }
69713
70093
  static SkyOf(scene) {
69714
70094
  const live = (HdrpPhysicallyBasedSky != null) ? HdrpPhysicallyBasedSky.Get(scene) : null;
69715
70095
  if (live != null)
69716
70096
  return live;
69717
- 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);
69718
70103
  return (block != null) ? block.sky : null;
69719
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
+ }
69720
70115
  static Frame(scene, fog) {
69721
70116
  const s = scene;
69722
70117
  const renderId = scene.getRenderId();
@@ -70016,76 +70411,8 @@ fn hdrpFogAmbient(dir: vec3f) -> vec3f {
70016
70411
  }
70017
70412
  `;
70018
70413
  }
70019
- static GlslHelpers(prefix) {
70414
+ static AerialPerspectiveGlsl() {
70020
70415
  return `
70021
- float ${prefix}OpticalDepth(float ext, float baseH, vec2 exps, float cosZ, float startH, float len) {
70022
- float absZ = max(abs(cosZ), 0.001);
70023
- float rcpAbsZ = 1.0 / absZ;
70024
- float endH = startH + len * cosZ;
70025
- float minH = min(startH, endH);
70026
- float h = max(minH - baseH, 0.0);
70027
- float homDist = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
70028
- float expDist = len - homDist;
70029
- float expMult = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
70030
- return ext * (homDist + expMult);
70031
- }
70032
- float ${prefix}PhaseHG(float g, float c) {
70033
- float g2 = g * g;
70034
- return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
70035
- }
70036
- `;
70037
- }
70038
- static WgslHelpers(prefix) {
70039
- return `
70040
- fn ${prefix}OpticalDepth(ext: f32, baseH: f32, exps: vec2f, cosZ: f32, startH: f32, len: f32) -> f32 {
70041
- let absZ: f32 = max(abs(cosZ), 0.001);
70042
- let rcpAbsZ: f32 = 1.0 / absZ;
70043
- let endH: f32 = startH + len * cosZ;
70044
- let minH: f32 = min(startH, endH);
70045
- let h: f32 = max(minH - baseH, 0.0);
70046
- let homDist: f32 = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
70047
- let expDist: f32 = len - homDist;
70048
- let expMult: f32 = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
70049
- return ext * (homDist + expMult);
70050
- }
70051
- fn ${prefix}PhaseHG(g: f32, c: f32) -> f32 {
70052
- let g2: f32 = g * g;
70053
- return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
70054
- }
70055
- `;
70056
- }
70057
- static Glsl() {
70058
- const S = HdrpPbSkyShaders;
70059
- const ap = (S != null) ? (S.GlslUniforms() + S.GlslCommon()) : "";
70060
- return `#ifdef HDRP_FOG_AP
70061
- precision highp sampler2DArray;
70062
- #endif
70063
- precision highp float;
70064
- varying vec2 vUV;
70065
- uniform sampler2D textureSampler;
70066
- uniform sampler2D depthSampler;
70067
- #ifdef HDRP_FOG_ENV
70068
- uniform samplerCube fogEnvSampler;
70069
- #endif
70070
- uniform vec4 fogA;
70071
- uniform vec4 fogB;
70072
- uniform vec4 fogC;
70073
- uniform vec4 fogD;
70074
- uniform vec4 fogE;
70075
- uniform vec4 fogSunDir;
70076
- uniform vec4 fogSun;
70077
- uniform vec4 fogEnv;
70078
- uniform vec4 fogSky;
70079
- ${HdrpFogPass.AmbientDeclarations(false)}uniform vec4 fogEye;
70080
- uniform vec4 fogForward;
70081
- uniform vec4 fogViewport;
70082
- uniform vec4 fogInput;
70083
- uniform mat4 fogInvViewProj;
70084
- uniform mat4 fogEnvMatrix;
70085
- #ifdef HDRP_FOG_AP
70086
- uniform sampler2DArray apLut;
70087
- uniform vec4 fogApCamera;
70088
- ${ap}
70089
70416
  vec3 fogRescaledChapman(vec2 z, vec2 Z, float c) {
70090
70417
  float sinTheta = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
70091
70418
  vec2 ch = vec2(pbChapmanUpper(z.x, abs(c)) * exp(Z.x - z.x), pbChapmanUpper(z.y, abs(c)) * exp(Z.y - z.y));
@@ -70114,116 +70441,28 @@ vec3 fogApOpticalDepth(vec3 O, vec3 V, float t) {
70114
70441
  float ozone = pbOzoneOpticalDepth(r, cosChi, t);
70115
70442
  return od.x * pbAirExt.xyz + od.y * pbAirExt.w + ozone * pbOzone0.xyz;
70116
70443
  }
70117
- #endif
70118
- ${HdrpFogPass.AmbientGlsl()}${HdrpFogPass.GlslHelpers("hdrpFog")}${HdrpFogPass.LocalGlsl()}
70119
- vec3 hdrpFogColor(vec3 dir, float dist) {
70120
- vec3 c = fogC.rgb;
70121
- if (fogB.z > 0.5) {
70122
- #ifdef HDRP_FOG_ENV
70123
- float t = clamp((dist - fogD.x) / max(fogD.y - fogD.x, 0.0001), 0.0, 1.0);
70124
- float lod = (1.0 - fogD.z * t) * fogD.w;
70125
- vec3 d = (fogEnvMatrix * vec4(dir, 0.0)).xyz;
70126
- vec4 e = textureLod(fogEnvSampler, d, lod);
70127
- vec3 rgb = (fogEnv.y > 0.5) ? pow(e.rgb, vec3(2.2)) / max(e.a, 0.0001) : e.rgb;
70128
- c *= rgb * fogEnv.x;
70129
- #else
70130
- c *= fogSky.rgb;
70131
- #endif
70132
- }
70133
- return c * fogC.w;
70134
- }
70135
- void main(void) {
70136
- vec4 src = texture2D(textureSampler, fogInput.xy + vUV * fogInput.zw);
70137
- float raw = abs(texture2D(depthSampler, fogViewport.xy + vUV * fogViewport.zw).r);
70138
- vec4 farP = fogInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
70139
- vec3 dir = normalize(farP.xyz / farP.w - fogEye.xyz);
70140
- bool isSky = (raw <= 0.0) || (raw >= fogForward.w);
70141
- float tFrag = isSky ? fogA.w : raw / max(dot(dir, fogForward.xyz), 0.0001);
70142
- vec3 color = vec3(0.0);
70143
- vec3 opacity = vec3(0.0);
70144
- if (fogA.x > 0.5 && fogEye.w > 0.5) {
70145
- vec3 fc = hdrpFogColor(dir, tFrag);
70146
- float volEnd = (fogB.w > 0.5) ? min(tFrag, fogEnv.w) : 0.0;
70147
- float trCamera = 1.0;
70148
- if (volEnd > 0.0) {
70149
- trCamera = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y, volEnd));
70150
- vec3 lit = hdrpFogAmbient(dir) * fogC.w * fogSun.w + fogSun.rgb * hdrpFogPhaseHG(fogE.w, dot(dir, fogSunDir.xyz)) * fogSunDir.w;
70151
- color = lit * fogE.rgb * (1.0 - trCamera);
70152
- }
70153
- float delta = tFrag - volEnd;
70154
- if (delta > 0.0) {
70155
- float tr = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y + volEnd * dir.y, delta));
70156
- color += trCamera * fc * fogE.rgb * (1.0 - tr);
70157
- trCamera *= tr;
70158
- }
70159
- #if HDRP_FOG_LOCALS > 0
70160
- if (fogB.w > 0.5) {
70161
- vec4 lf = hdrpFogLocals(dir, min(tFrag, fogEnv.w));
70162
- color = color * lf.a + lf.rgb;
70163
- trCamera *= lf.a;
70164
- }
70165
- #endif
70166
- opacity = vec3(1.0 - trCamera);
70167
- }
70168
- #ifdef HDRP_FOG_AP
70169
- if (!isSky && fogApCamera.w > 0.5 && fogEye.w > 0.5) {
70170
- vec3 O = fogApCamera.xyz;
70171
- float r = length(O);
70172
- float cosChi = dot(O / r, dir);
70173
- float tAp = min(tFrag, 128000.0);
70174
- float off = 1.0 / 128.0;
70175
- float s = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
70176
- float layer = clamp(s * 64.0 - 0.5, 0.0, 63.0);
70177
- float l0 = floor(layer);
70178
- float l1 = min(l0 + 1.0, 63.0);
70179
- vec3 apColor = mix(textureLod(apLut, vec3(vUV, l0), 0.0).rgb, textureLod(apLut, vec3(vUV, l1), 0.0).rgb, layer - l0);
70180
- vec3 apOpacity = vec3(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
70181
- apOpacity = pbDesaturate(apOpacity, pbZenith.w) * pbArt.x;
70182
- float chi = acos(clamp(cosChi, -1.0, 1.0));
70183
- float hor = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
70184
- float nrm = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
70185
- float te = exp(pbArt.y * nrm) * pbArt.z - pbArt.z;
70186
- apColor *= mix(pbHorizon.xyz, pbZenith.xyz, te);
70187
- color = color + (vec3(1.0) - opacity) * apColor;
70188
- opacity = opacity + (vec3(1.0) - opacity) * apOpacity;
70189
- }
70190
- #endif
70191
- gl_FragColor = vec4(src.rgb * (vec3(1.0) - opacity) + color, src.a);
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);
70192
70461
  }
70193
70462
  `;
70194
70463
  }
70195
- static Wgsl() {
70196
- const S = HdrpPbSkyShaders;
70197
- const ap = (S != null) ? (S.WgslUniforms() + S.WgslCommon()) : "";
70198
- return `varying vUV: vec2f;
70199
- var textureSamplerSampler: sampler;
70200
- var textureSampler: texture_2d<f32>;
70201
- var depthSamplerSampler: sampler;
70202
- var depthSampler: texture_2d<f32>;
70203
- #ifdef HDRP_FOG_ENV
70204
- var fogEnvSamplerSampler: sampler;
70205
- var fogEnvSampler: texture_cube<f32>;
70206
- #endif
70207
- uniform fogA: vec4f;
70208
- uniform fogB: vec4f;
70209
- uniform fogC: vec4f;
70210
- uniform fogD: vec4f;
70211
- uniform fogE: vec4f;
70212
- uniform fogSunDir: vec4f;
70213
- uniform fogSun: vec4f;
70214
- uniform fogEnv: vec4f;
70215
- uniform fogSky: vec4f;
70216
- ${HdrpFogPass.AmbientDeclarations(true)}uniform fogEye: vec4f;
70217
- uniform fogForward: vec4f;
70218
- uniform fogViewport: vec4f;
70219
- uniform fogInput: vec4f;
70220
- uniform fogInvViewProj: mat4x4f;
70221
- uniform fogEnvMatrix: mat4x4f;
70222
- #ifdef HDRP_FOG_AP
70223
- var apLutSampler: sampler;
70224
- var apLut: texture_2d_array<f32>;
70225
- uniform fogApCamera: vec4f;
70226
- ${ap}
70464
+ static AerialPerspectiveWgsl() {
70465
+ return `
70227
70466
  fn fogRescaledChapman(z: vec2f, Z: vec2f, c: f32) -> vec2f {
70228
70467
  let sinTheta: f32 = sqrt(clamp(1.0 - c * c, 0.0, 1.0));
70229
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));
@@ -70252,6 +70491,218 @@ fn fogApOpticalDepth(O: vec3f, V: vec3f, t: f32) -> vec3f {
70252
70491
  let ozone: f32 = pbOzoneOpticalDepth(r, cosChi, t);
70253
70492
  return od.x * uniforms.pbAirExt.xyz + od.y * uniforms.pbAirExt.w + ozone * uniforms.pbOzone0.xyz;
70254
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
+ }
70519
+ `;
70520
+ }
70521
+ static GlslHelpers(prefix) {
70522
+ return `
70523
+ float ${prefix}OpticalDepth(float ext, float baseH, vec2 exps, float cosZ, float startH, float len) {
70524
+ float absZ = max(abs(cosZ), 0.001);
70525
+ float rcpAbsZ = 1.0 / absZ;
70526
+ float endH = startH + len * cosZ;
70527
+ float minH = min(startH, endH);
70528
+ float h = max(minH - baseH, 0.0);
70529
+ float homDist = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
70530
+ float expDist = len - homDist;
70531
+ float expMult = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
70532
+ return ext * (homDist + expMult);
70533
+ }
70534
+ float ${prefix}PhaseHG(float g, float c) {
70535
+ float g2 = g * g;
70536
+ return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
70537
+ }
70538
+ `;
70539
+ }
70540
+ static WgslHelpers(prefix) {
70541
+ return `
70542
+ fn ${prefix}OpticalDepth(ext: f32, baseH: f32, exps: vec2f, cosZ: f32, startH: f32, len: f32) -> f32 {
70543
+ let absZ: f32 = max(abs(cosZ), 0.001);
70544
+ let rcpAbsZ: f32 = 1.0 / absZ;
70545
+ let endH: f32 = startH + len * cosZ;
70546
+ let minH: f32 = min(startH, endH);
70547
+ let h: f32 = max(minH - baseH, 0.0);
70548
+ let homDist: f32 = clamp((baseH - minH) * rcpAbsZ, 0.0, len);
70549
+ let expDist: f32 = len - homDist;
70550
+ let expMult: f32 = exp(-h * exps.x) * (1.0 - exp(-expDist * absZ * exps.x)) * (rcpAbsZ * exps.y);
70551
+ return ext * (homDist + expMult);
70552
+ }
70553
+ fn ${prefix}PhaseHG(g: f32, c: f32) -> f32 {
70554
+ let g2: f32 = g * g;
70555
+ return (1.0 - g2) / (12.566370614 * pow(max(1.0 + g2 - 2.0 * g * c, 0.0001), 1.5));
70556
+ }
70557
+ `;
70558
+ }
70559
+ static Glsl() {
70560
+ const S = HdrpPbSkyShaders;
70561
+ const ap = (S != null) ? (S.GlslUniforms() + S.GlslCommon()) : "";
70562
+ return `#ifdef HDRP_FOG_AP
70563
+ precision highp sampler2DArray;
70564
+ #endif
70565
+ precision highp float;
70566
+ #define FOG_DISPLAY_GAMMA 2.2
70567
+ varying vec2 vUV;
70568
+ uniform sampler2D textureSampler;
70569
+ uniform sampler2D depthSampler;
70570
+ #ifdef HDRP_FOG_ENV
70571
+ uniform samplerCube fogEnvSampler;
70572
+ #endif
70573
+ uniform vec4 fogA;
70574
+ uniform vec4 fogB;
70575
+ uniform vec4 fogC;
70576
+ uniform vec4 fogD;
70577
+ uniform vec4 fogE;
70578
+ uniform vec4 fogSunDir;
70579
+ uniform vec4 fogSun;
70580
+ uniform vec4 fogEnv;
70581
+ uniform vec4 fogSky;
70582
+ ${HdrpFogPass.AmbientDeclarations(false)}uniform vec4 fogEye;
70583
+ uniform vec4 fogForward;
70584
+ uniform vec4 fogViewport;
70585
+ uniform vec4 fogInput;
70586
+ uniform vec4 fogChain;
70587
+ uniform mat4 fogInvViewProj;
70588
+ uniform mat4 fogEnvMatrix;
70589
+ #ifdef HDRP_FOG_AP
70590
+ uniform sampler2DArray apLut;
70591
+ uniform vec4 fogApCamera;
70592
+ ${ap}
70593
+ ${HdrpFogPass.AerialPerspectiveGlsl()}
70594
+ #endif
70595
+ ${HdrpFogPass.AmbientGlsl()}${HdrpFogPass.GlslHelpers("hdrpFog")}${HdrpFogPass.LocalGlsl()}
70596
+ vec3 hdrpFogColor(vec3 dir, float dist) {
70597
+ vec3 c = fogC.rgb;
70598
+ if (fogB.z > 0.5) {
70599
+ #ifdef HDRP_FOG_ENV
70600
+ float t = clamp((dist - fogD.x) / max(fogD.y - fogD.x, 0.0001), 0.0, 1.0);
70601
+ float lod = (1.0 - fogD.z * t) * fogD.w;
70602
+ vec3 d = (fogEnvMatrix * vec4(dir, 0.0)).xyz;
70603
+ vec4 e = textureLod(fogEnvSampler, d, lod);
70604
+ vec3 rgb = (fogEnv.y > 0.5) ? pow(e.rgb, vec3(2.2)) / max(e.a, 0.0001) : e.rgb;
70605
+ c *= rgb * fogEnv.x;
70606
+ #else
70607
+ c *= fogSky.rgb;
70608
+ #endif
70609
+ }
70610
+ return c * fogC.w;
70611
+ }
70612
+ void main(void) {
70613
+ vec4 src = texture2D(textureSampler, fogInput.xy + vUV * fogInput.zw);
70614
+ float raw = abs(texture2D(depthSampler, fogViewport.xy + vUV * fogViewport.zw).r);
70615
+ vec4 farP = fogInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
70616
+ vec3 dir = normalize(farP.xyz / farP.w - fogEye.xyz);
70617
+ bool isSky = (raw <= 0.0) || (raw >= fogForward.w);
70618
+ float tFrag = isSky ? fogA.w : raw / max(dot(dir, fogForward.xyz), 0.0001);
70619
+ vec3 color = vec3(0.0);
70620
+ vec3 opacity = vec3(0.0);
70621
+ if (fogA.x > 0.5 && fogEye.w > 0.5) {
70622
+ vec3 fc = hdrpFogColor(dir, tFrag);
70623
+ float volEnd = (fogB.w > 0.5) ? min(tFrag, fogEnv.w) : 0.0;
70624
+ float trCamera = 1.0;
70625
+ if (volEnd > 0.0) {
70626
+ trCamera = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y, volEnd));
70627
+ vec3 lit = hdrpFogAmbient(dir) * fogC.w * fogSun.w + fogSun.rgb * hdrpFogPhaseHG(fogE.w, dot(dir, fogSunDir.xyz)) * fogSunDir.w;
70628
+ color = lit * fogE.rgb * (1.0 - trCamera);
70629
+ }
70630
+ float delta = tFrag - volEnd;
70631
+ if (delta > 0.0) {
70632
+ float tr = exp(-hdrpFogOpticalDepth(fogA.y, fogA.z, fogB.xy, dir.y, fogEye.y + volEnd * dir.y, delta));
70633
+ color += trCamera * fc * fogE.rgb * (1.0 - tr);
70634
+ trCamera *= tr;
70635
+ }
70636
+ #if HDRP_FOG_LOCALS > 0
70637
+ if (fogB.w > 0.5) {
70638
+ vec4 lf = hdrpFogLocals(dir, min(tFrag, fogEnv.w));
70639
+ color = color * lf.a + lf.rgb;
70640
+ trCamera *= lf.a;
70641
+ }
70642
+ #endif
70643
+ opacity = vec3(1.0 - trCamera);
70644
+ }
70645
+ #ifdef HDRP_FOG_AP
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);
70651
+ color = color + (vec3(1.0) - opacity) * apColor;
70652
+ opacity = opacity + (vec3(1.0) - opacity) * apOpacity;
70653
+ }
70654
+ #endif
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);
70668
+ }
70669
+ `;
70670
+ }
70671
+ static Wgsl() {
70672
+ const S = HdrpPbSkyShaders;
70673
+ const ap = (S != null) ? (S.WgslUniforms() + S.WgslCommon()) : "";
70674
+ return `varying vUV: vec2f;
70675
+ const FOG_DISPLAY_GAMMA: f32 = 2.2;
70676
+ var textureSamplerSampler: sampler;
70677
+ var textureSampler: texture_2d<f32>;
70678
+ var depthSamplerSampler: sampler;
70679
+ var depthSampler: texture_2d<f32>;
70680
+ #ifdef HDRP_FOG_ENV
70681
+ var fogEnvSamplerSampler: sampler;
70682
+ var fogEnvSampler: texture_cube<f32>;
70683
+ #endif
70684
+ uniform fogA: vec4f;
70685
+ uniform fogB: vec4f;
70686
+ uniform fogC: vec4f;
70687
+ uniform fogD: vec4f;
70688
+ uniform fogE: vec4f;
70689
+ uniform fogSunDir: vec4f;
70690
+ uniform fogSun: vec4f;
70691
+ uniform fogEnv: vec4f;
70692
+ uniform fogSky: vec4f;
70693
+ ${HdrpFogPass.AmbientDeclarations(true)}uniform fogEye: vec4f;
70694
+ uniform fogForward: vec4f;
70695
+ uniform fogViewport: vec4f;
70696
+ uniform fogInput: vec4f;
70697
+ uniform fogChain: vec4f;
70698
+ uniform fogInvViewProj: mat4x4f;
70699
+ uniform fogEnvMatrix: mat4x4f;
70700
+ #ifdef HDRP_FOG_AP
70701
+ var apLutSampler: sampler;
70702
+ var apLut: texture_2d_array<f32>;
70703
+ uniform fogApCamera: vec4f;
70704
+ ${ap}
70705
+ ${HdrpFogPass.AerialPerspectiveWgsl()}
70255
70706
  #endif
70256
70707
  ${HdrpFogPass.AmbientWgsl()}${HdrpFogPass.WgslHelpers("hdrpFog")}${HdrpFogPass.LocalWgsl()}
70257
70708
  fn hdrpFogColor(dir: vec3f, dist: f32) -> vec3f {
@@ -70307,31 +70758,29 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
70307
70758
  opacity = vec3f(1.0 - trCamera);
70308
70759
  }
70309
70760
  #ifdef HDRP_FOG_AP
70310
- if (!isSky && uniforms.fogApCamera.w > 0.5 && uniforms.fogEye.w > 0.5) {
70311
- let O: vec3f = uniforms.fogApCamera.xyz;
70312
- let r: f32 = length(O);
70313
- let cosChi: f32 = dot(O / r, dir);
70314
- let tAp: f32 = min(tFrag, 128000.0);
70315
- let off: f32 = 1.0 / 128.0;
70316
- let s: f32 = sqrt(max(tAp / 128000.0 - off * off, 0.0)) - off;
70317
- let layer: f32 = clamp(s * 64.0 - 0.5, 0.0, 63.0);
70318
- let l0: f32 = floor(layer);
70319
- let l1: f32 = min(l0 + 1.0, 63.0);
70320
- let c0: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l0), 0.0).rgb;
70321
- let c1: vec3f = textureSampleLevel(apLut, apLutSampler, uv, i32(l1), 0.0).rgb;
70322
- var apColor: vec3f = mix(c0, c1, vec3f(layer - l0));
70323
- var apOpacity: vec3f = vec3f(1.0) - exp(-fogApOpticalDepth(O, dir, tAp));
70324
- apOpacity = pbDesaturate(apOpacity, uniforms.pbZenith.w) * uniforms.pbArt.x;
70325
- let chi: f32 = acos(clamp(cosChi, -1.0, 1.0));
70326
- let hor: f32 = acos(clamp(pbCosHorizon(r), -1.0, 1.0));
70327
- let nrm: f32 = clamp(1.0 - chi / max(hor, 0.0001), 0.0, 1.0);
70328
- let te: f32 = exp(uniforms.pbArt.y * nrm) * uniforms.pbArt.z - uniforms.pbArt.z;
70329
- 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;
70330
70766
  color = color + (vec3f(1.0) - opacity) * apColor;
70331
70767
  opacity = opacity + (vec3f(1.0) - opacity) * apOpacity;
70332
70768
  }
70333
70769
  #endif
70334
- 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);
70335
70784
  }
70336
70785
  `;
70337
70786
  }
@@ -70412,6 +70861,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
70412
70861
  effect.setFloat4("fogE", frame.e[0], frame.e[1], frame.e[2], frame.e[3]);
70413
70862
  effect.setFloat4("fogSunDir", frame.sunDir[0], frame.sunDir[1], frame.sunDir[2], frame.sunDir[3]);
70414
70863
  effect.setFloat4("fogSun", frame.sun[0], frame.sun[1], frame.sun[2], frame.sun[3]);
70864
+ HdrpFogPass.BindChain(effect, scene, state.fog);
70415
70865
  effect.setFloat4("fogEnv", frame.env[0], frame.env[1], frame.env[2], frame.env[3]);
70416
70866
  effect.setFloat4("fogSky", frame.sky[0], frame.sky[1], frame.sky[2], frame.sky[3]);
70417
70867
  for (let i = 0; i < HdrpFogPass.AmbientVectors; i++)
@@ -70443,7 +70893,7 @@ fn main(input: FragmentInputs) -> FragmentOutputs {
70443
70893
  }
70444
70894
  }
70445
70895
  HdrpFogPass.ShaderName = "toolkitHdrpFog";
70446
- 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"];
70447
70897
  HdrpFogPass.Samplers = ["depthSampler", "fogEnvSampler", "apLut"];
70448
70898
  HdrpFogPass.AmbientVectors = 9;
70449
70899
  HdrpFogPass.registered = false;
@@ -71068,6 +71518,8 @@ export class HdrpPbSkyShaders {
71068
71518
  #define PB_TWO_PI 6.28318530718
71069
71519
  #define PB_HALF_PI 1.57079632679
71070
71520
  #define PB_INV_PI 0.31830988618
71521
+ #define PB_DISPLAY_GAMMA 0.45454545454
71522
+ #define PB_NO_BODY_HIT 3.0e38
71071
71523
  #define PB_FLT_MIN 1.175494351e-38
71072
71524
  #define PB_FLT_EPS 5.960464478e-8
71073
71525
  float pbSat(float x) { return clamp(x, 0.0, 1.0); }
@@ -71497,6 +71949,10 @@ vec2 pbCloudLighting${X}(vec2 uv) {
71497
71949
  ` + HdrpPbSkyShaders.GlslCloudUniforms() + `#ifdef PB_CLOUDS
71498
71950
  uniform sampler2D cloudTex;
71499
71951
  #endif
71952
+ #ifdef PBSKY_SPACE_EMISSION
71953
+ uniform samplerCube spaceCube;
71954
+ uniform vec4 pbSpace;
71955
+ #endif
71500
71956
  ` + HdrpPbSkyShaders.GlslCommon() + `
71501
71957
  vec2 pbMapSkyView(float cosChi, vec3 V) {
71502
71958
  float coord = pbFastACos(cosChi) / PB_HALF_PI;
@@ -71591,6 +72047,12 @@ vec3 pbRenderSky(vec3 d) {
71591
72047
  }
71592
72048
  }
71593
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
71594
72056
  vec3 skyColor = vec3(0.0);
71595
72057
  vec3 skyOpacity = vec3(0.0);
71596
72058
  if (lookAbove) {
@@ -71613,7 +72075,12 @@ vec3 pbRenderSky(vec3 d) {
71613
72075
  }
71614
72076
  void main(void) {
71615
72077
  vec3 d = normalize(vDir);
71616
- 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);
71617
72084
  }
71618
72085
  `;
71619
72086
  }
@@ -71640,6 +72107,8 @@ const PB_PI: f32 = 3.14159265359;
71640
72107
  const PB_TWO_PI: f32 = 6.28318530718;
71641
72108
  const PB_HALF_PI: f32 = 1.57079632679;
71642
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;
71643
72112
  const PB_FLT_MIN: f32 = 1.175494351e-38;
71644
72113
  const PB_FLT_EPS: f32 = 5.960464478e-8;
71645
72114
  fn pbSat(x: f32) -> f32 { return clamp(x, 0.0, 1.0); }
@@ -72082,6 +72551,11 @@ fn pbCloudLighting${X}(uv: vec2f) -> vec2f {
72082
72551
  var cloudTexSampler: sampler;
72083
72552
  var cloudTex: texture_2d<f32>;
72084
72553
  #endif
72554
+ #ifdef PBSKY_SPACE_EMISSION
72555
+ var spaceCubeSampler: sampler;
72556
+ var spaceCube: texture_cube<f32>;
72557
+ uniform pbSpace: vec4f;
72558
+ #endif
72085
72559
  ` + HdrpPbSkyShaders.WgslCommon() + `
72086
72560
  fn pbMapSkyView(cosChi: f32, V: vec3f) -> vec2f {
72087
72561
  let coord: f32 = pbFastACos(cosChi) / PB_HALF_PI;
@@ -72177,6 +72651,12 @@ fn pbRenderSky(d: vec3f) -> vec3f {
72177
72651
  }
72178
72652
  }
72179
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
72180
72660
  var skyColor: vec3f = vec3f(0.0);
72181
72661
  var skyOpacity: vec3f = vec3f(0.0);
72182
72662
  if (lookAbove) {
@@ -72199,8 +72679,13 @@ fn pbRenderSky(d: vec3f) -> vec3f {
72199
72679
  }
72200
72680
  @fragment
72201
72681
  fn main(input: FragmentInputs) -> FragmentOutputs {
72202
- let d: vec3f = normalize(fragmentInputs.vDir);
72203
- 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);
72204
72689
  }
72205
72690
  `;
72206
72691
  }
@@ -72453,17 +72938,16 @@ export class HdrpPhysicallyBasedSky {
72453
72938
  return (sky != null && sky.disposed !== true) ? sky : null;
72454
72939
  }
72455
72940
  static Decide(metadata, engine) {
72456
- if (HdrpRendering.IsParityExport(metadata) === false)
72457
- return { live: false, key: null, reason: "not an HDRP parity export" };
72458
- const sky = metadata.hdrp.sky;
72941
+ const sky = PhysicalSkyBlock.SkyOf(metadata);
72942
+ const isUrp = PhysicalSkyBlock.UrpBlock(metadata) != null && !HdrpRendering.IsParityExport(metadata);
72459
72943
  if (sky == null)
72460
- 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" };
72461
72945
  const type = (typeof sky.type === "string") ? sky.type.toLowerCase() : "";
72462
72946
  if (type !== "physicallybased")
72463
72947
  return { live: false, key: null, reason: "sky type " + (type || "unknown") };
72464
72948
  if (HdrpPhysicallyBasedSky.IsSupported(engine) === false)
72465
72949
  return { live: false, key: "hdrp:pbsky:unsupported", reason: "no WebGL2 / WebGPU half-float render target" };
72466
- if (sky.volumetricclouds === true)
72950
+ if (isUrp === false && sky.volumetricclouds === true)
72467
72951
  return { live: false, key: "hdrp:pbsky:volumetricclouds", reason: "an active VolumetricClouds is not ported (the bake carries it)" };
72468
72952
  return { live: true, key: null, reason: "live" };
72469
72953
  }
@@ -72475,11 +72959,12 @@ export class HdrpPhysicallyBasedSky {
72475
72959
  if (decision.key != null)
72476
72960
  LightingConversions.WarnOnce(decision.key, "HDRP PhysicallyBasedSky: " + decision.reason + " - the baked sky is used");
72477
72961
  if (decision.key != null)
72478
- HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, metadata.hdrp.sky);
72962
+ HdrpPhysicallyBasedSky.AttachBakedSkySunAttenuation(scene, PhysicalSkyBlock.SkyOf(metadata));
72479
72963
  return null;
72480
72964
  }
72481
72965
  try {
72482
- 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 });
72483
72968
  }
72484
72969
  catch (e) {
72485
72970
  LightingConversions.WarnOnce("hdrp:pbsky:create", "HDRP PhysicallyBasedSky could not be created (" + ((e && e.message) ? e.message : e) + ") - the baked sky is used");
@@ -72565,6 +73050,13 @@ export class HdrpPhysicallyBasedSky {
72565
73050
  }
72566
73051
  return precomputationHash !== state.lastPrecompute || lightHash !== state.lastLight || state.requested === true;
72567
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
+ }
72568
73060
  static SelectAmbientSh(skyAmbientMode, readback, exported) {
72569
73061
  if (skyAmbientMode === 1)
72570
73062
  return (readback != null) ? readback : exported;
@@ -72653,6 +73145,7 @@ export class HdrpPhysicallyBasedSky {
72653
73145
  this.filtering = null;
72654
73146
  this.pendingFilter = false;
72655
73147
  this.capturing = false;
73148
+ this.probeDrawsSky = false;
72656
73149
  this.shReading = false;
72657
73150
  this.readbackHarmonics = null;
72658
73151
  this.exportedHarmonics = null;
@@ -72667,6 +73160,7 @@ export class HdrpPhysicallyBasedSky {
72667
73160
  this.startTime = Date.now();
72668
73161
  this.scene = scene;
72669
73162
  this.sky = sky || {};
73163
+ this.sunIntensityScale = (typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
72670
73164
  this.options = options;
72671
73165
  this.fixedLights = lights;
72672
73166
  this.coefficients = HdrpPbSkyMath.Coefficients(this.sky);
@@ -72717,16 +73211,23 @@ export class HdrpPhysicallyBasedSky {
72717
73211
  defines.push("#define PB_CLOUDS");
72718
73212
  if (layers.length >= 2)
72719
73213
  defines.push("#define PB_CLOUDS2");
73214
+ this.spaceCube = this.loadSpaceEmission();
73215
+ if (this.spaceCube != null)
73216
+ defines.push("#define PBSKY_SPACE_EMISSION");
72720
73217
  const material = new ShaderMaterial("HDRP PBSky Material", scene, { vertex: HdrpPbSkyShaders.SkyName, fragment: HdrpPbSkyShaders.SkyName }, {
72721
73218
  attributes: ["position"],
72722
- uniforms: ["world", "viewProjection"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
72723
- samplers: ["skyViewLut", "cloudTex"],
73219
+ uniforms: ["world", "viewProjection", "pbSpace"].concat(HdrpPbSkyShaders.AtmosphereUniforms, HdrpPbSkyShaders.CloudUniforms),
73220
+ samplers: ["skyViewLut", "cloudTex", "spaceCube"],
72724
73221
  defines: defines,
72725
73222
  shaderLanguage: language
72726
73223
  });
72727
73224
  material.backFaceCulling = false;
72728
73225
  material.disableDepthWrite = true;
72729
73226
  material.setTexture("skyViewLut", this.skyViewLut);
73227
+ if (this.spaceCube != null) {
73228
+ material.setTexture("spaceCube", this.spaceCube);
73229
+ material.setVector4("pbSpace", this.spaceEmissionUniform());
73230
+ }
72730
73231
  if (layers.length > 0) {
72731
73232
  for (let i = 0; i < layers.length; i++)
72732
73233
  this.cloudMaps.push(this.loadCloudMap(layers[i]));
@@ -72865,7 +73366,7 @@ export class HdrpPhysicallyBasedSky {
72865
73366
  let exposure = 1;
72866
73367
  const body = new Array(HdrpPbSkyShaders.BodyVec4s * HdrpPbSkyShaders.MaxBodies * 4).fill(0);
72867
73368
  const peSafe = (pe > 0) ? pe : 1;
72868
- const lux = (l) => l.intensity / peSafe;
73369
+ const lux = (l) => (l.intensity * this.sunIntensityScale) / peSafe;
72869
73370
  const sunFor = (self) => {
72870
73371
  let best = null, max = 0;
72871
73372
  for (const l of this.scene.lights)
@@ -72938,7 +73439,7 @@ export class HdrpPhysicallyBasedSky {
72938
73439
  U.pbHorizon = [horizon[0], horizon[1], horizon[2], (typeof p.colorsaturation === "number") ? p.colorsaturation : 1];
72939
73440
  U.pbZenith = [zenith[0], zenith[1], zenith[2], (typeof p.alphasaturation === "number") ? p.alphasaturation : 1];
72940
73441
  U.pbArt = [(typeof p.alphamultiplier === "number") ? p.alphamultiplier : 1, exp[0], exp[1], pe];
72941
- U.pbCounts = [lightCount, lights.length, 1, 0];
73442
+ U.pbCounts = [lightCount, lights.length, 1, this.encodesForDisplay() ? 1 : 0];
72942
73443
  U.pbBody = body;
72943
73444
  this.writeCloudUniforms(lights);
72944
73445
  }
@@ -72973,14 +73474,16 @@ export class HdrpPhysicallyBasedSky {
72973
73474
  }
72974
73475
  }
72975
73476
  cloudAmbient() {
72976
- const down = new Vector3(0, -1, 0);
73477
+ return this.ambientAt(new Vector3(0, -1, 0));
73478
+ }
73479
+ ambientAt(direction) {
72977
73480
  if (this.ambientPolynomial != null)
72978
- return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial, down);
73481
+ return HdrpPbSkyMath.EvaluatePolynomial(this.ambientPolynomial, direction);
72979
73482
  if (this.exportedHarmonics == null)
72980
73483
  this.exportedHarmonics = this.parseExportedAmbient();
72981
73484
  if (this.exportedHarmonics == null)
72982
73485
  return new Color3(0, 0, 0);
72983
- return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1), down);
73486
+ return HdrpPbSkyMath.EvaluatePolynomial(LightingConversions.PolynomialFromPreScaled(this.exportedHarmonics, 1), direction);
72984
73487
  }
72985
73488
  cloudLayers() {
72986
73489
  const cl = this.sky.cloudlayer;
@@ -72988,6 +73491,22 @@ export class HdrpPhysicallyBasedSky {
72988
73491
  return [];
72989
73492
  return cl.layerlist.filter((l) => l != null && l.cloudmap != null && typeof l.cloudmap.url === "string").slice(0, 2);
72990
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
+ }
72991
73510
  loadCloudMap(layer) {
72992
73511
  const url = ((typeof this.options.root === "string") ? this.options.root : "") + layer.cloudmap.url;
72993
73512
  const tex = new Texture(url, this.scene, true, true, Texture.BILINEAR_SAMPLINGMODE);
@@ -73019,7 +73538,10 @@ export class HdrpPhysicallyBasedSky {
73019
73538
  this.applyMaterialUniforms();
73020
73539
  return;
73021
73540
  }
73022
- 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;
73023
73545
  }
73024
73546
  applyMaterialUniforms() {
73025
73547
  const m = this.material;
@@ -73048,7 +73570,7 @@ export class HdrpPhysicallyBasedSky {
73048
73570
  const p = this.sky.params || {};
73049
73571
  if (p.renderingmode === 1)
73050
73572
  LightingConversions.WarnOnce("hdrp:pbsky:material", "HDRP PhysicallyBasedSky: the Material rendering mode (a Shader Graph sky) renders the default live atmosphere");
73051
- if (p.hasgroundtextures === true || p.hasspaceemission === true)
73573
+ if (p.hasgroundtextures === true || (p.hasspaceemission === true && typeof p.spaceemissionurl !== "string"))
73052
73574
  LightingConversions.WarnOnce("hdrp:pbsky:textures", "HDRP PhysicallyBasedSky: ground / space emission textures are not rendered");
73053
73575
  for (const layer of this.cloudLayers())
73054
73576
  if (typeof layer.distortion === "number" && layer.distortion !== 0)
@@ -73081,20 +73603,28 @@ export class HdrpPhysicallyBasedSky {
73081
73603
  scene.customRenderTargets.push(probe.cubeTexture);
73082
73604
  const p = this.sky.params || {};
73083
73605
  probe.cubeTexture.onBeforeRenderObservable.add(() => {
73084
- if (this.capturing !== true)
73085
- 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);
73086
73608
  const art = this.uniforms.pbArt, counts = this.uniforms.pbCounts;
73087
73609
  if (art != null)
73088
73610
  this.material.setVector4("pbArt", new Vector4(art[0], art[1], art[2], 1));
73089
73611
  if (counts != null)
73090
- 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));
73091
73613
  });
73092
73614
  probe.cubeTexture.onAfterRenderObservable.add((faceIndex) => {
73093
- if (this.capturing !== true || (typeof faceIndex === "number" && faceIndex < 5))
73615
+ if (typeof faceIndex === "number" && faceIndex < 5)
73094
73616
  return;
73095
- this.capturing = false;
73096
73617
  this.applyMaterialUniforms();
73097
- 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
+ }
73098
73628
  });
73099
73629
  this.exportedHarmonics = this.parseExportedAmbient();
73100
73630
  }
@@ -73297,6 +73827,8 @@ export class HdrpPhysicallyBasedSky {
73297
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" });
73298
73828
  this.apTexture = new BaseTexture(scene, this.apTarget.texture);
73299
73829
  this.apTexture.name = "HDRP PBSky Aerial Perspective";
73830
+ this.apTexture.wrapU = Constants.TEXTURE_CLAMP_ADDRESSMODE;
73831
+ this.apTexture.wrapV = Constants.TEXTURE_CLAMP_ADDRESSMODE;
73300
73832
  LightingConversions.WarnOnce("hdrp:pbsky:aerialshadow", "HDRP PhysicallyBasedSky: the aerial perspective has no shadowed in-scatter (the sun shadow map is not sampled)");
73301
73833
  }
73302
73834
  if (!this.apPass.isReady())
@@ -73385,7 +73917,7 @@ export class HdrpPhysicallyBasedSky {
73385
73917
  catch (e) { }
73386
73918
  for (const light of this.attenuated)
73387
73919
  HdrpPhysicallyBasedSky.RestoreLight(light);
73388
- 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);
73389
73921
  for (const d of disposables) {
73390
73922
  try {
73391
73923
  if (d != null)
@@ -73404,6 +73936,9 @@ HdrpPhysicallyBasedSky.AerialPerspectiveSize = [32, 32, 64];
73404
73936
  HdrpPhysicallyBasedSky.AerialMaxDistance = 128000;
73405
73937
  HdrpPhysicallyBasedSky.MeshName = "HDRP Physically Based Sky";
73406
73938
  HdrpPhysicallyBasedSky.OnEnvironmentUpdated = new Observable();
73939
+ HdrpPhysicallyBasedSky.ProbeRenderKept = 0;
73940
+ HdrpPhysicallyBasedSky.ProbeCaptureAccepted = 1;
73941
+ HdrpPhysicallyBasedSky.ProbeCaptureRetry = 2;
73407
73942
  HdrpPhysicallyBasedSky.RealtimeMinFrames = 9;
73408
73943
  HdrpPhysicallyBasedSky.ShProjectionFaceSize = 64;
73409
73944
  HdrpPhysicallyBasedSky.CaptureFaceCount = 6;
@@ -91714,6 +92249,238 @@ LightProbeNetwork.DebugProbeSize = 0.1;
91714
92249
  LightProbeNetwork.InspectorSection = true;
91715
92250
  LightProbeNetwork.OnNetworkChangedObservable = new Observable();
91716
92251
  LightProbeNetwork.Sections = ["positions", "probesh", "tetraindices", "tetraadjacency", "renderersh", "rendererocclusion", "rendereranchor"];
92252
+ export class MeshWaterSurface extends ScriptComponent {
92253
+ constructor(transform, scene, properties = {}, alias = "TOOLKIT.MeshWaterSurface") {
92254
+ super(transform, scene, properties, alias);
92255
+ this.waterProperties = null;
92256
+ this.isUnderwater = false;
92257
+ this.fogColorChannels = [0, 0, 0];
92258
+ this.surfaceFogColor = new Color3(0, 0, 0);
92259
+ this.surfaceFogDensity = 0;
92260
+ this.surfaceFogStart = 0;
92261
+ this.surfaceFogEnd = 0;
92262
+ this.shownNodes = [];
92263
+ this.shownParticles = [];
92264
+ this.volumeNodes = [];
92265
+ this.flippedNodes = [];
92266
+ this.followers = [];
92267
+ this.followPosition = new Vector3(0, 0, 0);
92268
+ }
92269
+ static Get(scene) {
92270
+ if (scene == null)
92271
+ return null;
92272
+ return MeshWaterSurface.Registry.get(scene.uniqueId) ?? null;
92273
+ }
92274
+ awake() {
92275
+ this.waterProperties = this.getProperties();
92276
+ if (MeshWaterSurface.Registry.has(this.scene.uniqueId)) {
92277
+ Tools.Warn("MeshWaterSurface: '" + this.transform.name + "' replaces the scene's first water surface");
92278
+ }
92279
+ MeshWaterSurface.Registry.set(this.scene.uniqueId, this);
92280
+ }
92281
+ ready() {
92282
+ const properties = this.getProperties();
92283
+ if (properties.underwater == null)
92284
+ return;
92285
+ for (const node of this.findNodes(properties.underwater.shownodes)) {
92286
+ for (const component of SceneManager.GetComponents(node) ?? [])
92287
+ ScriptComponent.AwakeInstance(component);
92288
+ }
92289
+ }
92290
+ start() {
92291
+ const underwater = this.waterProperties.underwater;
92292
+ if (underwater == null)
92293
+ return;
92294
+ this.shownNodes = this.findNodes(underwater.shownodes);
92295
+ this.shownParticles = this.findParticles(this.shownNodes);
92296
+ this.volumeNodes = this.findNodes(underwater.volumenodes);
92297
+ this.flippedNodes = this.findNodes(underwater.flipnodes).map((node) => this.prepareFlip(node));
92298
+ this.followers = this.prepareFollowers(underwater.follownodes);
92299
+ this.update();
92300
+ }
92301
+ update() {
92302
+ if (this.waterProperties == null || this.waterProperties.underwater == null)
92303
+ return;
92304
+ const camera = this.scene.activeCamera;
92305
+ if (camera == null)
92306
+ return;
92307
+ const isCameraUnder = this.isCameraUnderwater(camera);
92308
+ if (isCameraUnder !== this.isUnderwater) {
92309
+ this.isUnderwater = isCameraUnder;
92310
+ if (isCameraUnder)
92311
+ this.enterUnderwater();
92312
+ else
92313
+ this.leaveUnderwater();
92314
+ }
92315
+ if (!isCameraUnder)
92316
+ return;
92317
+ this.updateFog(this.waterProperties.underwater.sealevel - camera.globalPosition.y);
92318
+ this.updateFollowers(camera);
92319
+ }
92320
+ destroy() {
92321
+ if (this.isUnderwater)
92322
+ this.leaveUnderwater();
92323
+ this.isUnderwater = false;
92324
+ if (MeshWaterSurface.Registry.get(this.scene.uniqueId) === this)
92325
+ MeshWaterSurface.Registry.delete(this.scene.uniqueId);
92326
+ }
92327
+ getSeaLevel() {
92328
+ return this.waterProperties.sealevel;
92329
+ }
92330
+ getSurfaceHeight() {
92331
+ const properties = this.waterProperties;
92332
+ const clockSeconds = ShaderGlobals.GetClock(this.scene).time;
92333
+ return properties.sealevel + Math.sin(clockSeconds * properties.wavespeed) * properties.waveamplitude;
92334
+ }
92335
+ getCameraHeight(camera) {
92336
+ if (camera == null)
92337
+ return 0;
92338
+ return camera.globalPosition.y - this.getSurfaceHeight();
92339
+ }
92340
+ isCameraUnderwater(camera) {
92341
+ if (camera == null)
92342
+ return false;
92343
+ const underwater = this.waterProperties.underwater;
92344
+ const crossingHeight = (underwater != null) ? underwater.sealevel : this.getSurfaceHeight();
92345
+ return camera.globalPosition.y <= crossingHeight;
92346
+ }
92347
+ ProjectPointOnWaterSurface(searchParameters) {
92348
+ if (searchParameters == null || searchParameters.targetPositionWS == null)
92349
+ return null;
92350
+ const target = searchParameters.targetPositionWS;
92351
+ const targetX = Array.isArray(target) ? target[0] : target.x;
92352
+ const targetZ = Array.isArray(target) ? target[2] : target.z;
92353
+ const projectedPosition = new Vector3(targetX, this.getSurfaceHeight(), targetZ);
92354
+ return {
92355
+ projectedPositionWS: projectedPosition,
92356
+ normalWS: Vector3.Up(),
92357
+ candidateLocationWS: projectedPosition.clone(),
92358
+ currentDirectionWS: Vector3.Zero(),
92359
+ numIterations: 1,
92360
+ error: 0
92361
+ };
92362
+ }
92363
+ FindWaterSurfaceHeight(searchParameters) {
92364
+ return this.ProjectPointOnWaterSurface(searchParameters);
92365
+ }
92366
+ findNodes(nodeIds) {
92367
+ const nodes = [];
92368
+ for (const nodeId of nodeIds ?? []) {
92369
+ const node = SceneManager.GetTransformNodeByID(this.scene, nodeId);
92370
+ if (node != null)
92371
+ nodes.push(node);
92372
+ }
92373
+ return nodes;
92374
+ }
92375
+ findParticles(nodes) {
92376
+ const particles = [];
92377
+ for (const node of nodes) {
92378
+ const system = SceneManager.GetComponent(node, MeshWaterSurface.PARTICLES_CLASS);
92379
+ if (system != null)
92380
+ particles.push(system);
92381
+ }
92382
+ return particles;
92383
+ }
92384
+ prepareFlip(node) {
92385
+ if (node.isWorldMatrixFrozen)
92386
+ node.unfreezeWorldMatrix();
92387
+ if (node.rotationQuaternion == null)
92388
+ node.rotationQuaternion = Quaternion.FromEulerVector(node.rotation);
92389
+ const surfaceRotation = node.rotationQuaternion.clone();
92390
+ const flipRotation = Quaternion.FromEulerAngles(Math.PI, 0, 0);
92391
+ return { node: node, surfaceRotation: surfaceRotation, underwaterRotation: flipRotation.multiply(surfaceRotation) };
92392
+ }
92393
+ prepareFollowers(follownodes) {
92394
+ const followers = [];
92395
+ for (const follower of follownodes ?? []) {
92396
+ const node = SceneManager.GetTransformNodeByID(this.scene, follower.node);
92397
+ if (node == null)
92398
+ continue;
92399
+ if (follower.scale != null)
92400
+ node.scaling.set(follower.scale[0], follower.scale[1], follower.scale[2]);
92401
+ followers.push({ node: node, offset: Vector3.FromArray(follower.offset) });
92402
+ }
92403
+ return followers;
92404
+ }
92405
+ enterUnderwater() {
92406
+ const underwater = this.waterProperties.underwater;
92407
+ if (underwater.fogcolors.length > 0)
92408
+ this.writeUnderwaterFog(underwater);
92409
+ for (const node of this.shownNodes)
92410
+ node.setEnabled(true);
92411
+ for (const particles of this.shownParticles)
92412
+ particles.play();
92413
+ this.setVolumesEnabled(true);
92414
+ for (const flipped of this.flippedNodes)
92415
+ flipped.node.rotationQuaternion.copyFrom(flipped.underwaterRotation);
92416
+ }
92417
+ leaveUnderwater() {
92418
+ if (this.waterProperties.underwater.fogcolors.length > 0)
92419
+ this.restoreSurfaceFog();
92420
+ for (const particles of this.shownParticles)
92421
+ particles.stop(true, MeshWaterSurface.STOP_AND_CLEAR);
92422
+ for (const node of this.shownNodes)
92423
+ node.setEnabled(false);
92424
+ this.setVolumesEnabled(false);
92425
+ for (const flipped of this.flippedNodes)
92426
+ flipped.node.rotationQuaternion.copyFrom(flipped.surfaceRotation);
92427
+ }
92428
+ writeUnderwaterFog(underwater) {
92429
+ const scene = this.scene;
92430
+ this.surfaceFogColor.copyFrom(scene.fogColor);
92431
+ this.surfaceFogDensity = scene.fogDensity;
92432
+ this.surfaceFogStart = scene.fogStart;
92433
+ this.surfaceFogEnd = scene.fogEnd;
92434
+ const isExponentialSquared = scene.fogMode === Scene.FOGMODE_EXP2;
92435
+ const densityScale = isExponentialSquared ? SceneManager.FogExp2DensityScale : SceneManager.FogExpDensityScale;
92436
+ scene.fogDensity = underwater.fogdensity * densityScale;
92437
+ scene.fogStart = underwater.fogstart;
92438
+ scene.fogEnd = underwater.fogend * MeshWaterSurface.LINEAR_FOG_END_SCALE;
92439
+ }
92440
+ restoreSurfaceFog() {
92441
+ const scene = this.scene;
92442
+ scene.fogColor.copyFrom(this.surfaceFogColor);
92443
+ scene.fogDensity = this.surfaceFogDensity;
92444
+ scene.fogStart = this.surfaceFogStart;
92445
+ scene.fogEnd = this.surfaceFogEnd;
92446
+ }
92447
+ setVolumesEnabled(enabled) {
92448
+ for (const node of this.volumeNodes) {
92449
+ node.setEnabled(enabled);
92450
+ const volume = SceneManager.GetComponent(node, MeshWaterSurface.POST_PROCESSOR_CLASS);
92451
+ if (volume != null)
92452
+ volume.SetVolumeEnabled(enabled);
92453
+ }
92454
+ }
92455
+ updateFog(depthMeters) {
92456
+ const underwater = this.waterProperties.underwater;
92457
+ if (underwater.fogcolors.length === 0)
92458
+ return;
92459
+ const depthFraction = Scalar.Clamp(depthMeters / underwater.fogdepth, 0, 1);
92460
+ const keyPosition = depthFraction * (underwater.fogcolors.length - 1);
92461
+ const lowerKey = Math.floor(keyPosition);
92462
+ const upperKey = Math.min(lowerKey + 1, underwater.fogcolors.length - 1);
92463
+ const keyBlend = keyPosition - lowerKey;
92464
+ for (let channel = 0; channel < 3; channel++) {
92465
+ const gradientValue = Scalar.Lerp(underwater.fogcolors[lowerKey][channel], underwater.fogcolors[upperKey][channel], keyBlend);
92466
+ const litValue = gradientValue * underwater.suncolor[channel];
92467
+ const addedValue = underwater.fogcolormultiplier[channel];
92468
+ this.fogColorChannels[channel] = (addedValue >= 0) ? Scalar.Clamp(litValue + addedValue, 0, 1) : litValue;
92469
+ }
92470
+ this.scene.fogColor.set(this.fogColorChannels[0], this.fogColorChannels[1], this.fogColorChannels[2]);
92471
+ }
92472
+ updateFollowers(camera) {
92473
+ for (const follower of this.followers) {
92474
+ camera.globalPosition.addToRef(follower.offset, this.followPosition);
92475
+ follower.node.setAbsolutePosition(this.followPosition);
92476
+ }
92477
+ }
92478
+ }
92479
+ MeshWaterSurface.POST_PROCESSOR_CLASS = "TOOLKIT.PostProcessor";
92480
+ MeshWaterSurface.PARTICLES_CLASS = "TOOLKIT.ShurikenParticles";
92481
+ MeshWaterSurface.STOP_AND_CLEAR = 1;
92482
+ MeshWaterSurface.LINEAR_FOG_END_SCALE = 2;
92483
+ MeshWaterSurface.Registry = new Map();
91717
92484
  export class NavigationAgent extends ScriptComponent {
91718
92485
  isReady() { return this.m_agentReady; }
91719
92486
  isNavigating() { return (this.m_navStatus === NavMoveStatus.Moving); }
@@ -108513,6 +109280,7 @@ export class PostProcessor extends ScriptComponent {
108513
109280
  const props = this.getProperties();
108514
109281
  this.cameraOwner = PostProcessingContract.isCameraOwner(props);
108515
109282
  this.volumeProperties = this.cameraOwner ? null : PostProcessingContract.parseVolume(props);
109283
+ this.volumeEnabled = !this.isSwitchedVolume();
108516
109284
  this.volumeLayer = SceneManager.GetTransformLayer(this.transform);
108517
109285
  PostProcessor.Registry.push(this);
108518
109286
  if (PostProcessor.GlobalInstance == null)
@@ -108749,7 +109517,8 @@ export class PostProcessor extends ScriptComponent {
108749
109517
  PostProcessor.warnOnce("cameras:" + selection.mode, selection.warning);
108750
109518
  const hasLocal = entries.some(e => e.properties.isglobal === false && e.properties.bounds != null);
108751
109519
  const hasCinemachine = volumes.some((volume) => volume.isCinemachineVolume());
108752
- const liveBlend = hasLocal || hasCinemachine;
109520
+ const hasSwitched = volumes.some((volume) => volume.isSwitchedVolume());
109521
+ const liveBlend = hasLocal || hasCinemachine || hasSwitched;
108753
109522
  const localLuts = liveBlend ? entries.map((e) => {
108754
109523
  const lut = PostProcessingContract.normalise(e.properties).lut;
108755
109524
  return (lut != null && lut.identity !== true && typeof lut.url === "string" && lut.url !== "") ? lut : null;
@@ -108844,6 +109613,9 @@ export class PostProcessor extends ScriptComponent {
108844
109613
  isCinemachineVolume() {
108845
109614
  return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.CINEMACHINE_VOLUME_ROLE;
108846
109615
  }
109616
+ isSwitchedVolume() {
109617
+ return this.volumeProperties != null && this.volumeProperties.role === PostProcessingContract.SWITCHED_VOLUME_ROLE;
109618
+ }
108847
109619
  IsVolumeEnabled() {
108848
109620
  if (this.volumeEnabled === false)
108849
109621
  return false;
@@ -109458,7 +110230,7 @@ export class PostProcessor extends ScriptComponent {
109458
110230
  applyHdrpFog(camera, textureType) {
109459
110231
  if (camera == null || this.scene == null || HdrpRendering == null || HdrpFogPass == null)
109460
110232
  return;
109461
- if (HdrpRendering.IsParity(this.scene) !== true)
110233
+ if (HdrpRendering.IsParity(this.scene) !== true && PhysicalSkyBlock.UrpBlock(HdrpRendering.SceneMetadata(this.scene)) == null)
109462
110234
  return;
109463
110235
  const fog = HdrpFogPass.FogBlock(this.scene);
109464
110236
  if (!HdrpFogMath.PassNeeded(fog, HdrpFogPass.SkyOf(this.scene)))
@@ -114890,6 +115662,7 @@ export class PostProcessingContract {
114890
115662
  }
114891
115663
  PostProcessingContract.VOLUME_TYPES = ["BuiltIn", "URP", "HDRP"];
114892
115664
  PostProcessingContract.CINEMACHINE_VOLUME_ROLE = "cinemachine";
115665
+ PostProcessingContract.SWITCHED_VOLUME_ROLE = "switched";
114893
115666
  PostProcessingContract.GRADING_MODES = ["LowDefinitionRange", "HighDefinitionRange", "External"];
114894
115667
  PostProcessingContract.RENDERING_PATH_DEFERRED = "DeferredShading";
114895
115668
  PostProcessingContract.RENDERING_PATH_FORWARD = "Forward";
@@ -130653,6 +131426,20 @@ export class RigidbodyPhysics extends ScriptComponent {
130653
131426
  RigidbodyPhysics.ConfigRigidbodyPhysics(scene, entity, false, false, physics, mass, centerOfMass);
130654
131427
  return body;
130655
131428
  }
131429
+ static MarkCompoundChildForDisposal(entity) {
131430
+ if (RigidbodyPhysics.RendersItself(entity) === false) {
131431
+ Utilities.AddLoaderItemMarkedForDisposal(entity);
131432
+ return;
131433
+ }
131434
+ const colliderMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
131435
+ if (colliderMesh != null)
131436
+ Utilities.AddLoaderItemMarkedForDisposal(colliderMesh);
131437
+ }
131438
+ static RendersItself(node) {
131439
+ if (!(node instanceof AbstractMesh) || node.getTotalVertices() <= 0 || node.name.endsWith(".Collider"))
131440
+ return false;
131441
+ return node.metadata != null && node.metadata.toolkit != null && node.metadata.toolkit.renderer != null;
131442
+ }
130656
131443
  static CreateCompoundPhysicsShapeAndBody(scene, root, entity, element, impostortype, staticfriction, dynamicfriction, restitution, fcombine, rcombine, sitems, item, center, complex, trigger, persist, layer, filter) {
130657
131444
  let center_xx = 0;
130658
131445
  let center_yy = 0;
@@ -130663,7 +131450,7 @@ export class RigidbodyPhysics extends ScriptComponent {
130663
131450
  const rootScale = RigidbodyPhysics.GetColliderScale(root);
130664
131451
  const entityScale = (entity === root) ? rootScale : RigidbodyPhysics.RoundColliderScale(entity.scaling);
130665
131452
  if (persist === false && complex === true)
130666
- Utilities.AddLoaderItemMarkedForDisposal(entity);
131453
+ RigidbodyPhysics.MarkCompoundChildForDisposal(entity);
130667
131454
  if (impostortype == RigidbodyPhysics.ConvexHullImpostor) {
130668
131455
  let convexHullMesh = SceneManager.FindChildTransformNode(entity, (entity.name + ".Collider"), SearchType.ExactMatch, true);
130669
131456
  if (convexHullMesh != null) {
@@ -133501,7 +134288,7 @@ export class ShurikenParticles extends ScriptComponent {
133501
134288
  }
133502
134289
  canBeginWarmupDraw() {
133503
134290
  const particleSystem = this._prtSystem;
133504
- if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null || this._prtProps.enabled === false)
134291
+ if (particleSystem == null || particleSystem.isDisposed || this._prtState == null || this._prtProps == null)
133505
134292
  return false;
133506
134293
  if (this._prtWarming || this._prtPrewarmJob != null)
133507
134294
  return false;
@@ -141333,6 +142120,7 @@ export class TerrainBuilder extends ScriptComponent {
141333
142120
  if (heights == null)
141334
142121
  throw new Error("heights block has no data");
141335
142122
  hf = new TerrainHeightfield(props, heights);
142123
+ hf.setEdgeNeighbors(this.resolveEdgeNeighbors());
141336
142124
  this._heightfield = hf;
141337
142125
  }
141338
142126
  catch (e) {
@@ -141525,6 +142313,45 @@ export class TerrainBuilder extends ScriptComponent {
141525
142313
  this._isBuilt = true;
141526
142314
  this.onBuiltObservable.notifyObservers(this);
141527
142315
  }
142316
+ resolveEdgeNeighbors() {
142317
+ const contract = this._contract;
142318
+ const tiles = TerrainBuilder.GetTerrains(this.scene);
142319
+ const ids = contract.neighbors;
142320
+ const hasIds = ids != null && (ids.left != null || ids.right != null || ids.top != null || ids.bottom != null);
142321
+ const neighbors = {
142322
+ left: this.edgeNeighborFor(tiles, hasIds ? ids.left : null, hasIds, -1, 0),
142323
+ right: this.edgeNeighborFor(tiles, hasIds ? ids.right : null, hasIds, 1, 0),
142324
+ top: this.edgeNeighborFor(tiles, hasIds ? ids.top : null, hasIds, 0, 1),
142325
+ bottom: this.edgeNeighborFor(tiles, hasIds ? ids.bottom : null, hasIds, 0, -1),
142326
+ };
142327
+ const anyNeighbor = neighbors.left != null || neighbors.right != null || neighbors.top != null || neighbors.bottom != null;
142328
+ return anyNeighbor ? neighbors : null;
142329
+ }
142330
+ edgeNeighborFor(tiles, neighborId, useIds, offsetX, offsetZ) {
142331
+ for (const other of tiles) {
142332
+ if (other === this || !this.canShareEdgeWith(other))
142333
+ continue;
142334
+ const isNeighbor = useIds ? other.transform.id === neighborId : this.isAdjacentTile(other, offsetX, offsetZ);
142335
+ if (!isNeighbor)
142336
+ continue;
142337
+ const heights = TerrainContract.GetBlock(other.contract, "heights", "u16");
142338
+ return (heights != null) ? { heights: heights, heightScale: other.contract.size[1] } : null;
142339
+ }
142340
+ return null;
142341
+ }
142342
+ canShareEdgeWith(other) {
142343
+ const contract = this._contract, otherContract = other.contract;
142344
+ return otherContract.heightresolution === contract.heightresolution && otherContract.size[0] === contract.size[0] && otherContract.size[2] === contract.size[2];
142345
+ }
142346
+ isAdjacentTile(other, offsetX, offsetZ) {
142347
+ const contract = this._contract, otherContract = other.contract;
142348
+ if (contract.allowautoconnect !== true || otherContract.allowautoconnect !== true || otherContract.groupingid !== contract.groupingid)
142349
+ return false;
142350
+ this.transform.computeWorldMatrix(true);
142351
+ const offset = this.worldToUnityUV(other.transform.getAbsolutePosition());
142352
+ const toleranceU = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[0], toleranceV = TERRAIN_EDGE_TOLERANCE_METERS / contract.size[2];
142353
+ return Math.abs(offset[0] - offsetX) <= toleranceU && Math.abs(offset[1] - offsetZ) <= toleranceV;
142354
+ }
141528
142355
  applyTreesAndFoliageVisibility() {
141529
142356
  if (this._trees != null)
141530
142357
  this._trees.setVisible(this._drawTreesAndFoliage);
@@ -142109,12 +142936,21 @@ export class TerrainBuilder extends ScriptComponent {
142109
142936
  worldToUnityUV(worldPosition) {
142110
142937
  if (this._contract == null || worldPosition == null)
142111
142938
  return null;
142112
- const inv = Matrix.Invert(this.transform.getWorldMatrix());
142113
- const local = Vector3.TransformCoordinates(worldPosition, inv);
142939
+ this.transform.getWorldMatrix().invertToRef(TerrainBuilder.ScratchInverse);
142940
+ const local = TerrainBuilder.ScratchLocal;
142941
+ Vector3.TransformCoordinatesToRef(worldPosition, TerrainBuilder.ScratchInverse, local);
142114
142942
  const size = this._contract.size;
142115
142943
  const ux = TerrainMath.UnityToLocalX(local.x, this._contract.handedness);
142116
142944
  return [ux / size[0], local.z / size[2]];
142117
142945
  }
142946
+ containsWorldPosition(worldPosition, tolerance) {
142947
+ const uv = this.worldToUnityUV(worldPosition);
142948
+ if (uv == null)
142949
+ return false;
142950
+ const size = this._contract.size;
142951
+ const slackU = tolerance / size[0], slackV = tolerance / size[2];
142952
+ return uv[0] >= -slackU && uv[0] <= 1 + slackU && uv[1] >= -slackV && uv[1] <= 1 + slackV;
142953
+ }
142118
142954
  SampleHeight(worldPosition) {
142119
142955
  if (this._heightfield == null)
142120
142956
  return 0;
@@ -142183,6 +143019,10 @@ export class TerrainBuilder extends ScriptComponent {
142183
143019
  if (list[i]._heightfield != null && list[i].WorldToTerrainUV(worldPosition) != null)
142184
143020
  return list[i];
142185
143021
  }
143022
+ for (let i = list.length - 1; i >= 0; i--) {
143023
+ if (list[i]._heightfield != null && list[i].containsWorldPosition(worldPosition, TERRAIN_EDGE_TOLERANCE_METERS))
143024
+ return list[i];
143025
+ }
142186
143026
  return null;
142187
143027
  }
142188
143028
  static GetWorldHeightAt(scene, worldPosition) {
@@ -142200,6 +143040,8 @@ export class TerrainBuilder extends ScriptComponent {
142200
143040
  }
142201
143041
  }
142202
143042
  TerrainBuilder._registry = new Map();
143043
+ TerrainBuilder.ScratchInverse = new Matrix();
143044
+ TerrainBuilder.ScratchLocal = new Vector3();
142203
143045
  TerrainBuilder.OnLoadProgressObservable = new Observable();
142204
143046
  TerrainBuilder.PRELOAD_TIMEOUT_MS = 120000;
142205
143047
  TerrainBuilder.SETTLE_MS = 30000;
@@ -142620,7 +143462,7 @@ export class TerrainDetails {
142620
143462
  }
142621
143463
  const F = TerrainFoliagePlugin;
142622
143464
  const layer = rt.data;
142623
- const sway = layer.sway;
143465
+ const sway = F.GraphOwnsMotion(source) ? null : layer.sway;
142624
143466
  const urp = ((this._owner.contract != null && this._owner.contract.flavour) || "urp") === "urp";
142625
143467
  const modes = F.MODE_FADE | ((sway != null) ? F.MODE_SWAY : 0) | ((layer.rendermode === "Grass") ? F.MODE_GRASSWAVE : 0) | (urp ? F.MODE_URPSPECULAR : 0);
142626
143468
  const swayKey = (sway != null) ? (sway.amount + "," + sway.speed + "," + sway.wavelength) : "-";
@@ -142897,19 +143739,12 @@ export class TerrainDetails {
142897
143739
  rt.mainMatrices = matrices;
142898
143740
  rt.mainColors = colors;
142899
143741
  rt.main.thinInstanceSetBuffer("matrix", matrices, 16, false);
142900
- TerrainDetails.ResizePreviousMatrices(rt.main, matrices);
143742
+ TerrainTrees.ResizePreviousMatrices(rt.main, matrices);
142901
143743
  if (colors != null)
142902
143744
  rt.main.thinInstanceSetBuffer(TerrainDetails.TintBuffer(rt), colors, 4, false);
142903
143745
  if (ShaderGraphRuntime != null)
142904
143746
  ShaderGraphRuntime.EnsureThinInstanceBuffers(rt.main);
142905
143747
  }
142906
- static ResizePreviousMatrices(mesh, matrices) {
142907
- const storage = mesh._thinInstanceDataStorage;
142908
- if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
142909
- return;
142910
- storage.previousMatrixBuffer.dispose();
142911
- storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
142912
- }
142913
143748
  static TintBuffer(rt) { return (rt.grass != null) ? "tkGrassTint" : "color"; }
142914
143749
  chunkDistance(c, x, z, size) {
142915
143750
  const x0 = c.cx * this._cw, x1 = Math.min((c.cx + 1) * this._cw, size[0]);
@@ -143503,6 +144338,9 @@ export class TerrainFoliagePlugin extends MaterialPluginBase {
143503
144338
  const shader = m.getShaderName();
143504
144339
  return shader != null && shader !== "UniversalShaderMaterial" && shader !== "CustomShaderMaterial";
143505
144340
  }
144341
+ static GraphOwnsMotion(material) {
144342
+ return TerrainFoliagePlugin.IsGraphMaterial(material) && material.__speedTreeWind == null;
144343
+ }
143506
144344
  getClassName() {
143507
144345
  return "TerrainFoliagePlugin";
143508
144346
  }
@@ -144675,6 +145513,7 @@ export class TerrainHeightfield {
144675
145513
  this._errors = null;
144676
145514
  this._minH = null;
144677
145515
  this._maxH = null;
145516
+ this.edgeNeighbors = null;
144678
145517
  this.R = contract.heightresolution;
144679
145518
  this.size = [contract.size[0], contract.size[1], contract.size[2]];
144680
145519
  this.handedness = (contract.handedness != null) ? contract.handedness : 1;
@@ -144703,9 +145542,12 @@ export class TerrainHeightfield {
144703
145542
  return this.heights[z * this.R + x] / 65535 * this.size[1];
144704
145543
  }
144705
145544
  normalAt(u, v, out) {
144706
- TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out);
145545
+ TerrainMath.SampleNormal(this.heights, this.R, this.size, u, v, out, this.edgeNeighbors);
144707
145546
  out[0] *= (this.handedness === 0 ? -1 : 1);
144708
145547
  }
145548
+ setEdgeNeighbors(neighbors) {
145549
+ this.edgeNeighbors = neighbors;
145550
+ }
144709
145551
  setHoles(pixels, width) {
144710
145552
  const n = this.R - 1;
144711
145553
  const height = (width > 0) ? Math.floor(pixels.length / 4 / width) : 0;
@@ -144984,15 +145826,31 @@ export class TerrainMath {
144984
145826
  const b = h01 + (h11 - h01) * tx;
144985
145827
  return TerrainMath.DecodeHeight(a + (b - a) * tz, sizeY);
144986
145828
  }
144987
- static SampleNormal(h, R, size, u, v, out) {
145829
+ static SampleNormal(h, R, size, u, v, out, neighbors = null) {
144988
145830
  const i = Math.round(TerrainMath.Clamp01(u) * (R - 1));
144989
145831
  const k = Math.round(TerrainMath.Clamp01(v) * (R - 1));
144990
145832
  const iL = Math.max(i - 1, 0), iR = Math.min(i + 1, R - 1);
144991
145833
  const kD = Math.max(k - 1, 0), kU = Math.min(k + 1, R - 1);
144992
145834
  const cellX = size[0] / (R - 1), cellZ = size[2] / (R - 1);
144993
145835
  const sy = size[1];
144994
- const dhdx = (iR > iL) ? (TerrainMath.DecodeHeight(h[k * R + iR], sy) - TerrainMath.DecodeHeight(h[k * R + iL], sy)) / ((iR - iL) * cellX) : 0;
144995
- const dhdz = (kU > kD) ? (TerrainMath.DecodeHeight(h[kU * R + i], sy) - TerrainMath.DecodeHeight(h[kD * R + i], sy)) / ((kU - kD) * cellZ) : 0;
145836
+ let dhdx = (iR > iL) ? (TerrainMath.DecodeHeight(h[k * R + iR], sy) - TerrainMath.DecodeHeight(h[k * R + iL], sy)) / ((iR - iL) * cellX) : 0;
145837
+ let dhdz = (kU > kD) ? (TerrainMath.DecodeHeight(h[kU * R + i], sy) - TerrainMath.DecodeHeight(h[kD * R + i], sy)) / ((kU - kD) * cellZ) : 0;
145838
+ if (i === 0 && neighbors?.left != null) {
145839
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.left.heights[k * R + (R - 2)], neighbors.left.heightScale);
145840
+ dhdx = (TerrainMath.DecodeHeight(h[k * R + 1], sy) - outsideHeight) / (2 * cellX);
145841
+ }
145842
+ else if (i === R - 1 && neighbors?.right != null) {
145843
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.right.heights[k * R + 1], neighbors.right.heightScale);
145844
+ dhdx = (outsideHeight - TerrainMath.DecodeHeight(h[k * R + (R - 2)], sy)) / (2 * cellX);
145845
+ }
145846
+ if (k === 0 && neighbors?.bottom != null) {
145847
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.bottom.heights[(R - 2) * R + i], neighbors.bottom.heightScale);
145848
+ dhdz = (TerrainMath.DecodeHeight(h[1 * R + i], sy) - outsideHeight) / (2 * cellZ);
145849
+ }
145850
+ else if (k === R - 1 && neighbors?.top != null) {
145851
+ const outsideHeight = TerrainMath.DecodeHeight(neighbors.top.heights[1 * R + i], neighbors.top.heightScale);
145852
+ dhdz = (outsideHeight - TerrainMath.DecodeHeight(h[(R - 2) * R + i], sy)) / (2 * cellZ);
145853
+ }
144996
145854
  const nx = -dhdx, ny = 1, nz = -dhdz;
144997
145855
  const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
144998
145856
  out[0] = nx / len;
@@ -146251,6 +147109,7 @@ export class TerrainTrees {
146251
147109
  this._colliderShapes = [];
146252
147110
  this._distance = 2000;
146253
147111
  this._built = false;
147112
+ this._warnedUniqueHost = false;
146254
147113
  this._n = 0;
146255
147114
  this._proto = null;
146256
147115
  this._local = null;
@@ -146343,13 +147202,21 @@ export class TerrainTrees {
146343
147202
  return casters;
146344
147203
  }
146345
147204
  getStats() {
146346
- let drawn = 0, visibleHosts = 0;
147205
+ let drawn = 0, visibleHosts = 0, capacity = 0;
146347
147206
  for (let i = 0; i < this._hosts.length; i++) {
146348
- drawn += this._hosts[i].count;
146349
- if (this._hosts[i].count > 0)
147207
+ const host = this._hosts[i];
147208
+ drawn += host.count;
147209
+ if (host.count > 0)
146350
147210
  visibleHosts++;
147211
+ capacity += host.matrices.length / 16;
147212
+ for (let slotIndex = 0; slotIndex < host.slots.length; slotIndex++)
147213
+ capacity += host.slots[slotIndex].matrices.length / 16;
146351
147214
  }
146352
- return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null };
147215
+ return { hosts: this._hosts.length, instances: this._n, drawn: drawn, visibleHosts: visibleHosts, colliders: this._colliderBody != null, capacity: capacity };
147216
+ }
147217
+ static PreparedHostCount(scene) {
147218
+ const prepared = TerrainTrees.PreparedHosts.get(scene.uniqueId);
147219
+ return (prepared != null) ? prepared.size : 0;
146353
147220
  }
146354
147221
  build() {
146355
147222
  const owner = this._owner;
@@ -146407,43 +147274,44 @@ export class TerrainTrees {
146407
147274
  this._built = true;
146408
147275
  this._dirty = true;
146409
147276
  }
146410
- buildPrototype(p, tpl, cap) {
146411
- const lods = Array.isArray(p.lods) ? p.lods : [];
146412
- const runtime = { data: p, heights: lods.map((l) => (l != null && l.height != null) ? l.height : 0), lodHosts: [], fade: (p.fademode > 0 && p.animatecrossfade === true) };
146413
- const tplWorldInv = Matrix.Invert(tpl.computeWorldMatrix(true));
146414
- let any = false;
146415
- for (let l = 0; l < lods.length; l++) {
146416
- const list = [];
146417
- runtime.lodHosts.push(list);
146418
- const renderers = (lods[l] != null && Array.isArray(lods[l].renderers)) ? lods[l].renderers : [];
146419
- for (let r = 0; r < renderers.length; r++) {
146420
- const node = this._scene.getNodeById(renderers[r]);
147277
+ buildPrototype(prototype, template, instanceCount) {
147278
+ const prototypeLods = Array.isArray(prototype.lods) ? prototype.lods : [];
147279
+ const runtime = { data: prototype, heights: prototypeLods.map((prototypeLod) => (prototypeLod != null && prototypeLod.height != null) ? prototypeLod.height : 0), lodHosts: [], fade: (prototype.fademode > 0 && prototype.animatecrossfade === true) };
147280
+ const templateWorldInverse = Matrix.Invert(template.computeWorldMatrix(true));
147281
+ const initialCapacity = Math.min(instanceCount, TREE_INITIAL_CAPACITY);
147282
+ let hasHost = false;
147283
+ for (let lodIndex = 0; lodIndex < prototypeLods.length; lodIndex++) {
147284
+ const lodHostIndices = [];
147285
+ runtime.lodHosts.push(lodHostIndices);
147286
+ const renderers = (prototypeLods[lodIndex] != null && Array.isArray(prototypeLods[lodIndex].renderers)) ? prototypeLods[lodIndex].renderers : [];
147287
+ for (let rendererIndex = 0; rendererIndex < renderers.length; rendererIndex++) {
147288
+ const node = this._scene.getNodeById(renderers[rendererIndex]);
146421
147289
  if (node == null)
146422
147290
  continue;
146423
147291
  let sources = [];
146424
147292
  if (node instanceof AbstractMesh && TerrainTrees.HasGeometry(node))
146425
147293
  sources = [node];
146426
147294
  else
146427
- sources = node.getChildMeshes(true).filter((m) => TerrainTrees.HasGeometry(m));
146428
- const flags = (lods[l] != null) ? lods[l].lightprobes : null;
146429
- const probeLit = TerrainTrees.RendererUsesProbes(flags, r) && TerrainProbePlugin != null;
146430
- for (let s = 0; s < sources.length; s++) {
146431
- const matrices = new Float32Array(16 * cap), fades = new Float32Array(cap);
146432
- const cast = !(lods[l] != null && lods[l].billboard === true) && TerrainTrees.RendererCastsShadows(sources[s], node);
146433
- const host = this.createHost(p, l, sources[s], tplWorldInv, matrices, fades);
147295
+ sources = node.getChildMeshes(true).filter((mesh) => TerrainTrees.HasGeometry(mesh));
147296
+ const flags = (prototypeLods[lodIndex] != null) ? prototypeLods[lodIndex].lightprobes : null;
147297
+ const probeLit = TerrainTrees.RendererUsesProbes(flags, rendererIndex) && TerrainProbePlugin != null;
147298
+ for (let sourceIndex = 0; sourceIndex < sources.length; sourceIndex++) {
147299
+ const matrices = new Float32Array(16 * initialCapacity), fades = new Float32Array(initialCapacity);
147300
+ const cast = !(prototypeLods[lodIndex] != null && prototypeLods[lodIndex].billboard === true) && TerrainTrees.RendererCastsShadows(sources[sourceIndex], node);
147301
+ const host = this.createHost(prototype, lodIndex, sources[sourceIndex], templateWorldInverse, matrices, fades);
146434
147302
  if (host == null)
146435
147303
  continue;
146436
147304
  if (probeLit)
146437
147305
  this._probeHosts++;
146438
- list.push(this._hosts.length);
146439
- 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: [] });
147306
+ lodHostIndices.push(this._hosts.length);
147307
+ 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 });
146440
147308
  if (cast)
146441
- this.createCasterSlots(this._hosts[this._hosts.length - 1], cap);
146442
- any = true;
147309
+ this.createCasterSlots(this._hosts[this._hosts.length - 1], initialCapacity);
147310
+ hasHost = true;
146443
147311
  }
146444
147312
  }
146445
147313
  }
146446
- return any ? runtime : null;
147314
+ return hasHost ? runtime : null;
146447
147315
  }
146448
147316
  static RendererUsesProbes(flags, index) {
146449
147317
  if (!Array.isArray(flags) || index < 0 || index >= flags.length)
@@ -146451,20 +147319,32 @@ export class TerrainTrees {
146451
147319
  return flags[index] !== false;
146452
147320
  }
146453
147321
  static AttachProbeBuffer(host, probes) {
146454
- const P = TerrainProbePlugin;
146455
- for (let o = 0; o < probes.length; o += P.STRIDE)
146456
- probes[o] = -1;
146457
- const buffer = new Buffer(host.getEngine(), probes, true, P.STRIDE, false, true);
146458
- for (let k = 0; k < 3; k++)
146459
- host.setVerticesBuffer(buffer.createVertexBuffer(P.ATTRIBUTES[k], 4 * k, 4, P.STRIDE, true));
146460
- host.onDisposeObservable.addOnce(() => { buffer.dispose(); });
147322
+ const probePlugin = TerrainProbePlugin;
147323
+ for (let recordOffset = 0; recordOffset < probes.length; recordOffset += probePlugin.STRIDE)
147324
+ probes[recordOffset] = -1;
147325
+ const buffer = TerrainTrees.CreateProbeBuffer(host, probes);
147326
+ TerrainTrees.ProbeBuffers.set(host, buffer);
147327
+ host.onDisposeObservable.addOnce(() => { TerrainTrees.ProbeBuffers.get(host).dispose(); });
146461
147328
  const material = host.material;
146462
- const subs = (material != null && material.subMaterials != null) ? material.subMaterials : [material];
146463
- for (let i = 0; i < subs.length; i++)
146464
- if (subs[i] != null)
146465
- P.Attach(subs[i]);
147329
+ const subMaterials = (material != null && material.subMaterials != null) ? material.subMaterials : [material];
147330
+ for (let i = 0; i < subMaterials.length; i++)
147331
+ if (subMaterials[i] != null)
147332
+ probePlugin.Attach(subMaterials[i]);
146466
147333
  return buffer;
146467
147334
  }
147335
+ static CreateProbeBuffer(host, probes) {
147336
+ const probePlugin = TerrainProbePlugin;
147337
+ const buffer = new Buffer(host.getEngine(), probes, true, probePlugin.STRIDE, false, true);
147338
+ for (let viewIndex = 0; viewIndex < 3; viewIndex++)
147339
+ host.setVerticesBuffer(buffer.createVertexBuffer(probePlugin.ATTRIBUTES[viewIndex], 4 * viewIndex, 4, probePlugin.STRIDE, true));
147340
+ return buffer;
147341
+ }
147342
+ static ResizeProbeBuffer(host) {
147343
+ const buffer = TerrainTrees.CreateProbeBuffer(host.mesh, host.probes);
147344
+ host.probeBuffer.dispose();
147345
+ host.probeBuffer = buffer;
147346
+ TerrainTrees.ProbeBuffers.set(host.mesh, buffer);
147347
+ }
146468
147348
  sampleProbes(network) {
146469
147349
  if (network == null || typeof network.sampleInto !== "function" || !TerrainTrees.NetworkLightsProbes(network))
146470
147350
  return false;
@@ -146620,31 +147500,85 @@ export class TerrainTrees {
146620
147500
  throw error;
146621
147501
  }
146622
147502
  }
146623
- createHost(p, lod, src, tplWorldInv, matrices, fades) {
146624
- const geo = (src instanceof InstancedMesh) ? src.sourceMesh : src;
146625
- const host = geo.clone(p.name + ".L" + lod + "." + src.name, null, true);
146626
- if (host == null)
147503
+ static BakeHostGeometry(source, templateWorldInverse, name) {
147504
+ const geometrySource = (source instanceof InstancedMesh) ? source.sourceMesh : source;
147505
+ const mesh = geometrySource.clone(name, null, true);
147506
+ if (mesh == null)
147507
+ return null;
147508
+ mesh.metadata = null;
147509
+ mesh.makeGeometryUnique();
147510
+ const rendererToTemplate = source.computeWorldMatrix(true).multiply(templateWorldInverse);
147511
+ mesh.position.setAll(0);
147512
+ mesh.rotationQuaternion = null;
147513
+ mesh.rotation.setAll(0);
147514
+ mesh.scaling.setAll(1);
147515
+ mesh.bakeTransformIntoVertices(rendererToTemplate);
147516
+ TerrainTrees.InterleaveVertexData(mesh);
147517
+ return mesh;
147518
+ }
147519
+ prepareHostGeometry(prototype, lod, source, templateWorldInverse) {
147520
+ const scene = this._scene;
147521
+ const sceneKey = scene.uniqueId;
147522
+ let prepared = TerrainTrees.PreparedHosts.get(sceneKey);
147523
+ if (prepared == null) {
147524
+ const hosts = new Map();
147525
+ prepared = hosts;
147526
+ TerrainTrees.PreparedHosts.set(sceneKey, hosts);
147527
+ scene.onDisposeObservable.addOnce(() => {
147528
+ TerrainTrees.PreparedHosts.delete(sceneKey);
147529
+ hosts.forEach((mesh) => { mesh.dispose(false, false); });
147530
+ });
147531
+ }
147532
+ const key = prototype.templateid + "|" + lod + "|" + source.uniqueId;
147533
+ const existing = prepared.get(key);
147534
+ if (existing != null)
147535
+ return existing;
147536
+ const mesh = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, prototype.name + ".L" + lod + "." + source.name + ".Prepared");
147537
+ if (mesh == null)
146627
147538
  return null;
146628
- host.metadata = null;
146629
- host.makeGeometryUnique();
146630
- const rel = src.computeWorldMatrix(true).multiply(tplWorldInv);
146631
- host.position.setAll(0);
146632
- host.rotationQuaternion = null;
146633
- host.rotation.setAll(0);
146634
- host.scaling.setAll(1);
146635
- host.bakeTransformIntoVertices(rel);
147539
+ mesh.setEnabled(false);
147540
+ mesh.isVisible = false;
147541
+ mesh.isPickable = false;
147542
+ prepared.set(key, mesh);
147543
+ return mesh;
147544
+ }
147545
+ createUniqueHost(source, templateWorldInverse, name, reason) {
147546
+ if (!this._warnedUniqueHost) {
147547
+ this._warnedUniqueHost = true;
147548
+ Tools.Warn("[TerrainBuilder] " + this._owner.transform.name + ": tree hosts cannot share gpu buffers, copying geometry per tile — " + reason);
147549
+ }
147550
+ const host = TerrainTrees.BakeHostGeometry(source, templateWorldInverse, name);
147551
+ if (host != null)
147552
+ host.setEnabled(true);
147553
+ return host;
147554
+ }
147555
+ createHost(prototype, lod, source, templateWorldInverse, matrices, fades) {
147556
+ const prepared = this.prepareHostGeometry(prototype, lod, source, templateWorldInverse);
147557
+ if (prepared == null)
147558
+ return null;
147559
+ const name = prototype.name + ".L" + lod + "." + source.name;
147560
+ let host = null;
147561
+ try {
147562
+ host = TerrainTrees.CreateSharedHost(this._scene, prepared, name);
147563
+ for (const key of TerrainTrees.HOST_DRAW_STATE)
147564
+ host[key] = prepared[key];
147565
+ }
147566
+ catch (error) {
147567
+ host = this.createUniqueHost(source, templateWorldInverse, name, String(error));
147568
+ if (host == null)
147569
+ return null;
147570
+ }
147571
+ const sourceMaterial = prepared.material;
146636
147572
  host.parent = this._root;
146637
- host.setEnabled(true);
146638
147573
  host.isVisible = false;
146639
147574
  host.isPickable = false;
146640
147575
  host.receiveShadows = true;
146641
147576
  host.alwaysSelectAsActiveMesh = true;
146642
147577
  host.doNotSyncBoundingInfo = true;
146643
- TerrainTrees.InterleaveVertexData(host);
146644
147578
  host.thinInstanceSetBuffer("matrix", matrices, 16, false);
146645
147579
  host.thinInstanceSetBuffer("tkLodFade", fades, 1, false);
146646
147580
  host.thinInstanceCount = 0;
146647
- host.material = this.hostMaterial(p, lod, geo.material, TerrainTrees.HasSpeedTreeWindVertexData(host));
147581
+ host.material = this.hostMaterial(prototype, lod, sourceMaterial, TerrainTrees.HasSpeedTreeWindVertexData(prepared));
146648
147582
  if (ShaderGraphRuntime != null)
146649
147583
  ShaderGraphRuntime.EnsureThinInstanceBuffers(host);
146650
147584
  return host;
@@ -146672,7 +147606,7 @@ export class TerrainTrees {
146672
147606
  ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
146673
147607
  if (TerrainBuilder != null)
146674
147608
  TerrainBuilder.SetCasterSlot(mesh, slotIndex);
146675
- slots.push({ mesh: mesh, matrices: matrices, fades: fades, probes: null, count: 0, lastCount: -1, box: new Float32Array(6), boxMin: new Vector3(), boxMax: new Vector3() });
147609
+ 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 });
146676
147610
  }
146677
147611
  host.slots = slots;
146678
147612
  }
@@ -146713,7 +147647,8 @@ export class TerrainTrees {
146713
147647
  const hue = (info != null && Array.isArray(info.hue) && info.hue.length >= 4) ? info.hue : null;
146714
147648
  const F = TerrainFoliagePlugin;
146715
147649
  const urp = (this._owner.contract != null && (this._owner.contract.flavour || "urp") === "urp");
146716
- const bend = ownWind ? 0 : F.MODE_TREE;
147650
+ const graphMoves = p.speedtree !== true && F.GraphOwnsMotion(source);
147651
+ const bend = (ownWind || graphMoves) ? 0 : F.MODE_TREE;
146717
147652
  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));
146718
147653
  if (plugin != null) {
146719
147654
  plugin.params.bendFactor = (p.bendfactor != null) ? p.bendfactor : 0;
@@ -147418,7 +148353,7 @@ export class TerrainTrees {
147418
148353
  }
147419
148354
  appendInstance(target, instanceIndex, fade) {
147420
148355
  const count = target.count;
147421
- if (16 * (count + 1) > target.matrices.length)
148356
+ if (16 * (count + 1) > target.matrices.length && !TerrainTrees.GrowTarget(target, target.capacityCeiling))
147422
148357
  return;
147423
148358
  const sourceOffset = 16 * instanceIndex;
147424
148359
  const targetOffset = 16 * count;
@@ -147456,6 +148391,26 @@ export class TerrainTrees {
147456
148391
  if (z + radius > box[5])
147457
148392
  box[5] = z + radius;
147458
148393
  }
148394
+ static GrowTarget(target, ceiling) {
148395
+ const capacity = target.matrices.length / 16;
148396
+ if (capacity >= ceiling)
148397
+ return false;
148398
+ const grownCapacity = Math.min(ceiling, Math.max(target.count + 1, Math.ceil(capacity * TREE_GROWTH_FACTOR)));
148399
+ const matrices = new Float32Array(16 * grownCapacity);
148400
+ matrices.set(target.matrices.subarray(0, 16 * target.count));
148401
+ target.matrices = matrices;
148402
+ const fades = new Float32Array(grownCapacity);
148403
+ fades.set(target.fades.subarray(0, target.count));
148404
+ target.fades = fades;
148405
+ if (target.probes != null) {
148406
+ const stride = TerrainProbePlugin.STRIDE;
148407
+ const probes = new Float32Array(stride * grownCapacity);
148408
+ probes.set(target.probes.subarray(0, stride * target.count));
148409
+ target.probes = probes;
148410
+ }
148411
+ target.grown = true;
148412
+ return true;
148413
+ }
147459
148414
  static LodCasts(hosts, proto, lod) {
147460
148415
  if (lod < 0 || lod >= proto.lodHosts.length)
147461
148416
  return false;
@@ -147493,16 +148448,39 @@ export class TerrainTrees {
147493
148448
  }
147494
148449
  }
147495
148450
  }
148451
+ static RebindGrownTarget(target) {
148452
+ const mesh = target.mesh;
148453
+ mesh.thinInstanceSetBuffer("matrix", target.matrices, 16, false);
148454
+ TerrainTrees.ResizePreviousMatrices(mesh, target.matrices);
148455
+ mesh.thinInstanceSetBuffer("tkLodFade", target.fades, 1, false);
148456
+ if (target.probes != null)
148457
+ TerrainTrees.ResizeProbeBuffer(target);
148458
+ if (ShaderGraphRuntime != null)
148459
+ ShaderGraphRuntime.EnsureThinInstanceBuffers(mesh);
148460
+ target.grown = false;
148461
+ }
148462
+ static ResizePreviousMatrices(mesh, matrices) {
148463
+ const storage = mesh._thinInstanceDataStorage;
148464
+ if (storage == null || storage.previousMatrixBuffer == null || storage.previousMatrixData != null)
148465
+ return;
148466
+ storage.previousMatrixBuffer.dispose();
148467
+ storage.previousMatrixBuffer = mesh._thinInstanceCreateMatrixBuffer("previousWorld", matrices, false);
148468
+ }
147496
148469
  uploadTarget(target, probeBuffer, isSlot) {
147497
148470
  const mesh = target.mesh;
147498
148471
  if (target.count > 0) {
147499
148472
  if (isSlot && target.lastCount <= 0)
147500
148473
  mesh.setEnabled(true);
148474
+ const grown = target.grown;
148475
+ if (grown)
148476
+ TerrainTrees.RebindGrownTarget(target);
147501
148477
  mesh.thinInstanceCount = target.count;
147502
- mesh.thinInstancePartialBufferUpdate("matrix", target.count, 0);
147503
- mesh.thinInstancePartialBufferUpdate("tkLodFade", target.count, 0);
147504
- if (probeBuffer != null)
147505
- probeBuffer.updateDirectly(target.probes, 0, target.count);
148478
+ if (!grown) {
148479
+ mesh.thinInstancePartialBufferUpdate("matrix", target.count, 0);
148480
+ mesh.thinInstancePartialBufferUpdate("tkLodFade", target.count, 0);
148481
+ if (probeBuffer != null)
148482
+ probeBuffer.updateDirectly(target.probes, 0, target.count);
148483
+ }
147506
148484
  target.boxMin.set(target.box[0], target.box[1], target.box[2]);
147507
148485
  target.boxMax.set(target.box[3], target.box[4], target.box[5]);
147508
148486
  mesh.getBoundingInfo().reConstruct(target.boxMin, target.boxMax, mesh.computeWorldMatrix(isSlot));
@@ -147626,6 +148604,9 @@ TerrainTrees.CASTER_SLOTS = 4;
147626
148604
  TerrainTrees.SLOT_MARGIN = 4;
147627
148605
  TerrainTrees.NO_PROBE_RECORD = -1;
147628
148606
  TerrainTrees.LIGHT_TURN_DOT = 0.99996192;
148607
+ TerrainTrees.HOST_DRAW_STATE = ["sideOrientation", "renderingGroupId", "layerMask", "alphaIndex", "hasVertexAlpha", "useVertexColors", "numBoneInfluencers", "skeleton"];
148608
+ TerrainTrees.PreparedHosts = new Map();
148609
+ TerrainTrees.ProbeBuffers = new WeakMap();
147629
148610
  TerrainTrees._materials = new Map();
147630
148611
  TerrainTrees.ProbeSampleHeight = 0;
147631
148612
  TerrainTrees.SUBSURFACE_FIELDS = ["isTranslucencyEnabled", "isScatteringEnabled", "translucencyIntensity", "legacyTranslucency", "useGltfStyleTextures",
@@ -166554,6 +167535,1531 @@ export class VisualEffectSpawner {
166554
167535
  }
166555
167536
  VisualEffectSpawner.COUNT_EPSILON = 1e-6;
166556
167537
  VisualEffectSpawner.MAX_PHASE_CHANGES_PER_STEP = 64;
167538
+ export class VolumetricCloudsMath {
167539
+ static ShellInterval(rayOrigin, rayDirection, innerRadius, outerRadius, planetRadius) {
167540
+ const radialDistance = Math.hypot(rayOrigin[0], rayOrigin[1], rayOrigin[2]);
167541
+ if (radialDistance <= 0)
167542
+ return null;
167543
+ const cosChi = (rayOrigin[0] * rayDirection[0] + rayOrigin[1] * rayDirection[1] + rayOrigin[2] * rayDirection[2]) / radialDistance;
167544
+ const inner = VolumetricCloudsMath.IntersectSphere(innerRadius, cosChi, radialDistance);
167545
+ const outer = VolumetricCloudsMath.IntersectSphere(outerRadius, cosChi, radialDistance);
167546
+ if (inner[0] < 0 && inner[1] >= 0) {
167547
+ const radius = (typeof planetRadius === "number") ? planetRadius : innerRadius;
167548
+ const sinHorizon = Math.min(1, radius / radialDistance);
167549
+ const horizonCosine = -Math.sqrt(Math.max(0, 1 - sinHorizon * sinHorizon));
167550
+ return (cosChi >= horizonCosine) ? [inner[1], outer[1]] : null;
167551
+ }
167552
+ if (outer[1] < 0)
167553
+ return null;
167554
+ return [Math.max(outer[0], 0), (inner[0] >= 0) ? inner[0] : outer[1]];
167555
+ }
167556
+ static StepSize(shellLength, steps, maximumStep) {
167557
+ return Math.min(shellLength / Math.max(1, steps), maximumStep);
167558
+ }
167559
+ static MaximumStep(altitudeRange, authoredSteps, tierSteps) {
167560
+ const stretch = (typeof authoredSteps === "number" && tierSteps > 0) ? Math.min(Math.max(1, authoredSteps / tierSteps), CLOUD_MAX_STEP_STRETCH) : 1;
167561
+ return altitudeRange / CLOUD_MAX_STEP_DIVISOR * stretch;
167562
+ }
167563
+ static TierSettings(tier, authored) {
167564
+ const block = authored || {};
167565
+ const positiveOr = (value, fallback) => (typeof value === "number" && value > 0) ? value : fallback;
167566
+ const caps = CLOUD_TIER_CAPS[Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, Math.floor(tier)))];
167567
+ return {
167568
+ resolutionScale: Math.min(positiveOr(block.resolutionscale, CLOUD_DEFAULT_SETTINGS.resolutionScale), caps.resolutionScale),
167569
+ primarySteps: Math.min(positiveOr(block.numprimarysteps, CLOUD_DEFAULT_SETTINGS.primarySteps), caps.primarySteps),
167570
+ longRayMultiplier: Math.min(positiveOr(block.longraysamplemultiplier, CLOUD_DEFAULT_SETTINGS.longRayMultiplier), caps.longRayMultiplier),
167571
+ lightSteps: Math.min(positiveOr(block.numlightsteps, CLOUD_DEFAULT_SETTINGS.lightSteps), caps.lightSteps)
167572
+ };
167573
+ }
167574
+ static StartTier(maximumTextureSize, viewportCssWidth) {
167575
+ if (maximumTextureSize < CLOUD_WEAK_GPU_TEXTURE_SIZE || viewportCssWidth < CLOUD_PHONE_WIDTH_CSS_PX)
167576
+ return 1;
167577
+ return 0;
167578
+ }
167579
+ static IsCameraCut(previousPosition, currentPosition) {
167580
+ if (previousPosition == null)
167581
+ return true;
167582
+ const distance = Math.hypot(currentPosition[0] - previousPosition[0], currentPosition[1] - previousPosition[1], currentPosition[2] - previousPosition[2]);
167583
+ return distance > CLOUD_CAMERA_CUT_METERS;
167584
+ }
167585
+ static PackCurves(density, erosion, ambientOcclusion) {
167586
+ const texels = new Float32Array(CLOUD_CURVE_SAMPLES * 4);
167587
+ const hasCurves = Array.isArray(density) && density.length >= CLOUD_CURVE_SAMPLES;
167588
+ const clamp01 = (value) => Math.max(0, Math.min(1, (typeof value === "number") ? value : 0));
167589
+ for (let i = 0; i < CLOUD_CURVE_SAMPLES; i++) {
167590
+ const isEnd = (i === 0 || i === CLOUD_CURVE_SAMPLES - 1);
167591
+ texels[i * 4] = !hasCurves ? 1 : (isEnd ? 0 : clamp01(density[i]));
167592
+ texels[i * 4 + 1] = (!hasCurves || !Array.isArray(erosion)) ? 1 : clamp01(erosion[i]);
167593
+ texels[i * 4 + 2] = (!hasCurves || !Array.isArray(ambientOcclusion)) ? 1 : clamp01(1 - ambientOcclusion[i]);
167594
+ texels[i * 4 + 3] = 1;
167595
+ }
167596
+ return texels;
167597
+ }
167598
+ static IntersectSphere(sphereRadius, cosChi, radialDistance) {
167599
+ const ratio = sphereRadius / radialDistance;
167600
+ const discriminant = ratio * ratio - Math.max(0, Math.min(1, 1 - cosChi * cosChi));
167601
+ if (discriminant < 0)
167602
+ return [discriminant, discriminant];
167603
+ const root = Math.sqrt(discriminant);
167604
+ return [radialDistance * (-cosChi - root), radialDistance * (-cosChi + root)];
167605
+ }
167606
+ }
167607
+ VolumetricCloudsMath.LowestTier = 2;
167608
+ export class VolumetricCloudsTier {
167609
+ constructor(startTier) {
167610
+ this.isSettled = false;
167611
+ this.windowMilliseconds = 0;
167612
+ this.windowFrames = 0;
167613
+ this.meanBeforeStep = 0;
167614
+ this.previousMean = 0;
167615
+ this.tier = Math.max(0, Math.min(VolumetricCloudsMath.LowestTier, startTier));
167616
+ }
167617
+ addFrame(frameMilliseconds) {
167618
+ this.windowMilliseconds += frameMilliseconds;
167619
+ this.windowFrames++;
167620
+ if (this.windowFrames < CLOUD_TIER_WINDOW_FRAMES)
167621
+ return false;
167622
+ const meanMilliseconds = this.windowMilliseconds / this.windowFrames;
167623
+ this.windowMilliseconds = 0;
167624
+ this.windowFrames = 0;
167625
+ if (this.meanBeforeStep > 0) {
167626
+ const savedEnough = meanMilliseconds <= this.meanBeforeStep * (1 - CLOUD_TIER_MINIMUM_SAVING);
167627
+ this.meanBeforeStep = 0;
167628
+ if (!savedEnough) {
167629
+ this.tier--;
167630
+ this.isSettled = true;
167631
+ return true;
167632
+ }
167633
+ }
167634
+ const previousMean = this.previousMean;
167635
+ this.previousMean = meanMilliseconds;
167636
+ const isSteady = previousMean > 0 && meanMilliseconds > previousMean * (1 - CLOUD_TIER_MINIMUM_SAVING);
167637
+ if (this.isSettled || !isSteady || meanMilliseconds <= CLOUD_TIER_BUDGET_MS || this.tier >= VolumetricCloudsMath.LowestTier)
167638
+ return false;
167639
+ this.tier++;
167640
+ this.meanBeforeStep = meanMilliseconds;
167641
+ return true;
167642
+ }
167643
+ }
167644
+ export class VolumetricClouds {
167645
+ static TryCreate(scene, metadata, root) {
167646
+ const block = PhysicalSkyBlock.UrpBlock(metadata);
167647
+ const clouds = (block != null) ? block.clouds : null;
167648
+ if (clouds == null || clouds.state === false || scene == null)
167649
+ return null;
167650
+ if (HdrpPhysicallyBasedSky == null || !HdrpPhysicallyBasedSky.IsSupported(scene.getEngine()))
167651
+ return null;
167652
+ const existing = VolumetricClouds.Get(scene);
167653
+ if (existing != null)
167654
+ return existing;
167655
+ return new VolumetricClouds(scene, clouds, block.sky, (typeof root === "string") ? root : "");
167656
+ }
167657
+ static Get(scene) {
167658
+ if (scene == null)
167659
+ return null;
167660
+ const clouds = scene._tkVolumetricClouds;
167661
+ return (clouds != null && clouds.disposed !== true) ? clouds : null;
167662
+ }
167663
+ constructor(scene, block, sky, root) {
167664
+ this.disposed = false;
167665
+ this.tier = 0;
167666
+ this.hasAerial = false;
167667
+ this.placeholderAerial = null;
167668
+ this.worleyTexture = null;
167669
+ this.erosionTexture = null;
167670
+ this.curveTexture = null;
167671
+ this.placeholderDepth = null;
167672
+ this.marchTarget = null;
167673
+ this.renderer = null;
167674
+ this.marchPass = null;
167675
+ this.temporalPass = null;
167676
+ this.historyTargets = [];
167677
+ this.historyIndex = 0;
167678
+ this.hasHistory = false;
167679
+ this.compositeMesh = null;
167680
+ this.compositeMaterial = null;
167681
+ this.isReady = false;
167682
+ this.isPending = false;
167683
+ this.preExposure = 1;
167684
+ this.preExposureBinding = null;
167685
+ this.frameSeed = 0;
167686
+ this.windOffset = [0, 0];
167687
+ this.verticalShapeOffset = 0;
167688
+ this.verticalErosionOffset = 0;
167689
+ this.sceneDepth = null;
167690
+ this.beforeRenderObserver = null;
167691
+ this.afterRenderObserver = null;
167692
+ this.tierTracker = null;
167693
+ this.targetWidth = 1;
167694
+ this.targetHeight = 1;
167695
+ this.lastCamera = null;
167696
+ this.sunLight = null;
167697
+ this.ambientSource = null;
167698
+ this.ambientCaptureCount = -1;
167699
+ this.previousEye = [0, 0, 0];
167700
+ this.currentEye = [0, 0, 0];
167701
+ this.hasPreviousEye = false;
167702
+ this.beforeTargetsObserver = null;
167703
+ this.afterTargetsObserver = null;
167704
+ this.disposeObserver = null;
167705
+ this.mirror = Matrix.Identity();
167706
+ this.inverseViewProjection = new Matrix();
167707
+ this.previousViewProjection = new Matrix();
167708
+ this.previousViewProjectionUnity = new Matrix();
167709
+ this.sunDirection = new Vector3();
167710
+ this.cameraForward = new Vector3();
167711
+ this.ambientTop = new Color3();
167712
+ this.ambientBottom = new Color3();
167713
+ this.scene = scene;
167714
+ this.block = block;
167715
+ this.sky = sky;
167716
+ this.root = root;
167717
+ scene._tkVolumetricClouds = this;
167718
+ const planet = (sky != null) ? sky.planet : null;
167719
+ this.isLocal = (planet != null && typeof planet.renderingspace === "number") ? planet.renderingspace === 1 : block.localclouds === true;
167720
+ const curvature = (typeof block.earthcurvature === "number") ? block.earthcurvature : 0;
167721
+ 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;
167722
+ this.planetCenter = (this.isLocal && planet != null && Array.isArray(planet.center)) ? Vector3.FromArray(planet.center) : new Vector3(0, -this.planetRadius, 0);
167723
+ this.scatteringTint = VolumetricClouds.LinearScatteringTint(block.scatteringtint);
167724
+ if (scene.useRightHandedSystem === true)
167725
+ Matrix.ScalingToRef(-1, 1, 1, this.mirror);
167726
+ this.localForward = new Vector3(0, 0, (scene.useRightHandedSystem === true) ? -1 : 1);
167727
+ this.tier = VolumetricCloudsMath.StartTier(scene.getEngine().getCaps().maxTextureSize, VolumetricClouds.CanvasCssWidth(scene));
167728
+ this.tierTracker = new VolumetricCloudsTier(this.tier);
167729
+ this.settings = VolumetricCloudsMath.TierSettings(this.tier, block);
167730
+ this.reportLoweredSettings();
167731
+ this.isPending = true;
167732
+ scene.addPendingData(this);
167733
+ this.preExposureBinding = HdrpRendering.Bind(scene, 1, 1, (scaled) => { this.preExposure = scaled; });
167734
+ this.disposeObserver = scene.onDisposeObservable.add(() => this.dispose());
167735
+ this.createPasses();
167736
+ this.loadNoise().then((loaded) => {
167737
+ if (this.disposed)
167738
+ return;
167739
+ if (!loaded) {
167740
+ this.releasePending();
167741
+ return;
167742
+ }
167743
+ this.waitUntilReady();
167744
+ });
167745
+ }
167746
+ dispose() {
167747
+ if (this.disposed)
167748
+ return;
167749
+ this.disposed = true;
167750
+ const scene = this.scene;
167751
+ this.releasePending();
167752
+ if (this.beforeRenderObserver != null)
167753
+ scene.onBeforeRenderObservable.remove(this.beforeRenderObserver);
167754
+ if (this.afterRenderObserver != null)
167755
+ scene.onAfterRenderObservable.remove(this.afterRenderObserver);
167756
+ if (this.beforeTargetsObserver != null)
167757
+ scene.onBeforeRenderTargetsRenderObservable.remove(this.beforeTargetsObserver);
167758
+ if (this.afterTargetsObserver != null)
167759
+ scene.onAfterRenderTargetsRenderObservable.remove(this.afterTargetsObserver);
167760
+ if (this.disposeObserver != null)
167761
+ scene.onDisposeObservable.remove(this.disposeObserver);
167762
+ if (this.preExposureBinding != null)
167763
+ HdrpRendering.Unbind(scene, this.preExposureBinding);
167764
+ 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);
167765
+ for (const disposable of disposables) {
167766
+ try {
167767
+ if (disposable != null)
167768
+ disposable.dispose();
167769
+ }
167770
+ catch (error) { }
167771
+ }
167772
+ const sceneWithClouds = scene;
167773
+ if (sceneWithClouds._tkVolumetricClouds === this)
167774
+ sceneWithClouds._tkVolumetricClouds = null;
167775
+ }
167776
+ createPasses() {
167777
+ const scene = this.scene;
167778
+ const engine = scene.getEngine();
167779
+ const isWebGPU = engine.isWebGPU === true;
167780
+ const language = isWebGPU ? ShaderLanguage.WGSL : ShaderLanguage.GLSL;
167781
+ const defines = [];
167782
+ if (this.isLocal)
167783
+ defines.push("#define CLOUD_LOCAL");
167784
+ if (this.block.microerosion === true && this.block.microerosionfactor > 0)
167785
+ defines.push("#define CLOUD_MICRO_EROSION");
167786
+ const liveSky = HdrpPhysicallyBasedSky.Get(scene);
167787
+ this.hasAerial = liveSky != null && HdrpFogMath != null && HdrpFogMath.HasAerialPerspective(liveSky);
167788
+ const marchUniforms = VolumetricCloudsShaders.MarchUniforms.slice();
167789
+ const marchSamplers = VolumetricCloudsShaders.MarchSamplers.slice();
167790
+ if (this.hasAerial) {
167791
+ defines.push("#define CLOUD_AERIAL");
167792
+ marchUniforms.push(VolumetricCloudsShaders.AerialCameraUniform, ...HdrpPbSkyShaders.AtmosphereUniforms);
167793
+ marchSamplers.push(VolumetricCloudsShaders.AerialSampler);
167794
+ this.placeholderAerial = new RawTexture2DArray(new Uint8Array(4), 1, 1, 1, Constants.TEXTUREFORMAT_RGBA, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
167795
+ this.placeholderAerial.name = "Volumetric Clouds Aerial Placeholder";
167796
+ }
167797
+ VolumetricCloudsShaders.Register();
167798
+ this.curveTexture = this.createCurveTexture();
167799
+ this.placeholderDepth = new RawTexture(new Float32Array([0]), 1, 1, Constants.TEXTUREFORMAT_R, scene, false, false, Texture.NEAREST_SAMPLINGMODE, Constants.TEXTURETYPE_FLOAT);
167800
+ this.placeholderDepth.name = "Volumetric Clouds Depth Placeholder";
167801
+ const textureType = (engine.getCaps().textureHalfFloatRender === true) ? Constants.TEXTURETYPE_HALF_FLOAT : Constants.TEXTURETYPE_FLOAT;
167802
+ this.updateTargetSize();
167803
+ this.marchTarget = this.createCloudTarget("Volumetric Clouds Lighting", textureType);
167804
+ this.historyTargets.push(this.createCloudTarget("Volumetric Clouds History A", textureType), this.createCloudTarget("Volumetric Clouds History B", textureType));
167805
+ this.renderer = new EffectRenderer(engine);
167806
+ this.marchPass = new EffectWrapper({
167807
+ engine: engine, name: VolumetricCloudsShaders.MarchName, fragmentShader: VolumetricCloudsShaders.MarchName, useShaderStore: true,
167808
+ uniformNames: marchUniforms, samplerNames: marchSamplers,
167809
+ defines: defines.join("\n"), shaderLanguage: language
167810
+ });
167811
+ this.marchPass.onApplyObservable.add(() => this.bindMarch(this.marchPass.effect));
167812
+ this.temporalPass = new EffectWrapper({
167813
+ engine: engine, name: VolumetricCloudsShaders.TemporalName, fragmentShader: VolumetricCloudsShaders.TemporalName, useShaderStore: true,
167814
+ uniformNames: VolumetricCloudsShaders.TemporalUniforms.slice(), samplerNames: VolumetricCloudsShaders.TemporalSamplers.slice(),
167815
+ defines: defines.join("\n"), shaderLanguage: language
167816
+ });
167817
+ this.temporalPass.onApplyObservable.add(() => this.bindTemporal(this.temporalPass.effect));
167818
+ this.createCompositeMesh(language);
167819
+ }
167820
+ createCloudTarget(name, textureType) {
167821
+ 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);
167822
+ target.wrapU = Texture.CLAMP_ADDRESSMODE;
167823
+ target.wrapV = Texture.CLAMP_ADDRESSMODE;
167824
+ target.anisotropicFilteringLevel = 1;
167825
+ return target;
167826
+ }
167827
+ createCurveTexture() {
167828
+ const curves = this.block.curves || {};
167829
+ const texels = VolumetricCloudsMath.PackCurves(curves.density, curves.erosion, curves.ambientocclusion);
167830
+ const halfTexels = new Uint16Array(texels.length);
167831
+ for (let i = 0; i < texels.length; i++)
167832
+ halfTexels[i] = TextureTools.ToHalfFloat(texels[i]);
167833
+ const texture = new RawTexture(halfTexels, CLOUD_CURVE_SAMPLES, 1, Constants.TEXTUREFORMAT_RGBA, this.scene, false, false, Texture.BILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_HALF_FLOAT);
167834
+ texture.name = "Volumetric Clouds Curves";
167835
+ texture.wrapU = Texture.WRAP_ADDRESSMODE;
167836
+ texture.wrapV = Texture.CLAMP_ADDRESSMODE;
167837
+ return texture;
167838
+ }
167839
+ createCompositeMesh(language) {
167840
+ const scene = this.scene;
167841
+ const mesh = new Mesh(VolumetricClouds.MeshName, scene);
167842
+ const vertexData = new VertexData();
167843
+ vertexData.positions = [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0];
167844
+ vertexData.indices = [0, 1, 2, 0, 2, 3];
167845
+ vertexData.applyToMesh(mesh, false);
167846
+ const material = new ShaderMaterial("Volumetric Clouds Composite", scene, { vertex: VolumetricCloudsShaders.CompositeName, fragment: VolumetricCloudsShaders.CompositeName }, { attributes: ["position"], uniforms: ["cloudDisplayEncoded"], samplers: ["cloudLighting"], needAlphaBlending: true, shaderLanguage: language });
167847
+ material.alphaMode = Constants.ALPHA_PREMULTIPLIED;
167848
+ material.disableDepthWrite = true;
167849
+ material.depthFunction = Constants.ALWAYS;
167850
+ material.backFaceCulling = false;
167851
+ material.setTexture("cloudLighting", this.marchTarget);
167852
+ material.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
167853
+ mesh.material = material;
167854
+ mesh.renderingGroupId = Utilities.DefaultRenderGroup();
167855
+ mesh.alphaIndex = CLOUD_ALPHA_INDEX;
167856
+ mesh.alwaysSelectAsActiveMesh = true;
167857
+ mesh.isPickable = false;
167858
+ mesh.doNotSyncBoundingInfo = true;
167859
+ mesh.applyFog = false;
167860
+ mesh.setEnabled(false);
167861
+ this.compositeMesh = mesh;
167862
+ this.compositeMaterial = material;
167863
+ this.beforeTargetsObserver = scene.onBeforeRenderTargetsRenderObservable.add(() => { mesh.isVisible = false; });
167864
+ this.afterTargetsObserver = scene.onAfterRenderTargetsRenderObservable.add(() => { mesh.isVisible = true; });
167865
+ }
167866
+ async loadNoise() {
167867
+ const noise = this.block.noise;
167868
+ if (noise == null || noise.worley == null || noise.erosion == null) {
167869
+ LightingConversions.WarnOnce("urp:clouds:nonoise", "Volumetric clouds: the export carries no cloud noise, so the clouds do not draw");
167870
+ return false;
167871
+ }
167872
+ try {
167873
+ const buffers = await Promise.all([
167874
+ Tools.LoadFileAsync(this.root + noise.worley.url, true),
167875
+ Tools.LoadFileAsync(this.root + noise.erosion.url, true)
167876
+ ]);
167877
+ if (this.disposed)
167878
+ return false;
167879
+ this.worleyTexture = this.createNoiseTexture("Volumetric Clouds Worley Noise", buffers[0], noise.worley.size);
167880
+ this.erosionTexture = this.createNoiseTexture("Volumetric Clouds Erosion Noise", buffers[1], noise.erosion.size);
167881
+ return this.worleyTexture != null && this.erosionTexture != null;
167882
+ }
167883
+ catch (error) {
167884
+ LightingConversions.WarnOnce("urp:clouds:noiseload", "Volumetric clouds: a cloud noise file failed to load, so the clouds do not draw (" + error + ")");
167885
+ return false;
167886
+ }
167887
+ }
167888
+ createNoiseTexture(name, buffer, size) {
167889
+ if (buffer == null || buffer.byteLength !== size * size * size) {
167890
+ LightingConversions.WarnOnce("urp:clouds:noisesize", "Volumetric clouds: " + name + " is not " + size + "³ bytes, so the clouds do not draw");
167891
+ return null;
167892
+ }
167893
+ const texture = new RawTexture3D(new Uint8Array(buffer), size, size, size, Constants.TEXTUREFORMAT_R, this.scene, true, false, Texture.TRILINEAR_SAMPLINGMODE, Constants.TEXTURETYPE_UNSIGNED_BYTE);
167894
+ texture.name = name;
167895
+ texture.wrapU = Texture.WRAP_ADDRESSMODE;
167896
+ texture.wrapV = Texture.WRAP_ADDRESSMODE;
167897
+ texture.wrapR = Texture.WRAP_ADDRESSMODE;
167898
+ return texture;
167899
+ }
167900
+ waitUntilReady() {
167901
+ if (this.disposed)
167902
+ return;
167903
+ const camera = this.scene.activeCamera;
167904
+ if (camera != null && this.sceneDepth == null)
167905
+ this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
167906
+ const noiseReady = this.worleyTexture.isReady() && this.erosionTexture.isReady() && this.curveTexture.isReady();
167907
+ const compositeReady = this.compositeMaterial.isReady(this.compositeMesh);
167908
+ if (!noiseReady || !this.marchPass.isReady() || !this.temporalPass.isReady() || !compositeReady || camera == null) {
167909
+ setTimeout(() => this.waitUntilReady(), CLOUD_READY_POLL_MS);
167910
+ return;
167911
+ }
167912
+ this.isReady = true;
167913
+ this.compositeMesh.setEnabled(true);
167914
+ this.beforeRenderObserver = this.scene.onBeforeRenderObservable.add(() => this.render());
167915
+ this.releasePending();
167916
+ SceneManager.WhenSceneRevealed(this.scene).then((revealed) => {
167917
+ if (!revealed || this.disposed)
167918
+ return;
167919
+ this.afterRenderObserver = this.scene.onAfterRenderObservable.add(() => this.updateTier());
167920
+ });
167921
+ }
167922
+ releasePending() {
167923
+ if (!this.isPending)
167924
+ return;
167925
+ this.isPending = false;
167926
+ this.scene.removePendingData(this);
167927
+ }
167928
+ render() {
167929
+ const camera = this.scene.activeCamera;
167930
+ if (!this.isReady || this.disposed || camera == null)
167931
+ return;
167932
+ this.advanceWind(this.scene.getEngine().getDeltaTime() / 1000);
167933
+ this.resizeTargets();
167934
+ this.frameSeed = (this.frameSeed + 1) % 65536;
167935
+ this.sceneDepth = CameraDepthCopy.AcquireForSampler(this.scene, camera, this);
167936
+ this.trackCamera(camera);
167937
+ this.renderer.render(this.marchPass, this.marchTarget);
167938
+ const accumulation = (typeof this.block.temporalaccumulationfactor === "number") ? this.block.temporalaccumulationfactor : 0;
167939
+ if (accumulation < CLOUD_MINIMUM_ACCUMULATION) {
167940
+ this.compositeMaterial.setTexture("cloudLighting", this.marchTarget);
167941
+ }
167942
+ else {
167943
+ this.historyIndex = 1 - this.historyIndex;
167944
+ this.renderer.render(this.temporalPass, this.historyTargets[this.historyIndex]);
167945
+ this.compositeMaterial.setTexture("cloudLighting", this.historyTargets[this.historyIndex]);
167946
+ this.hasHistory = true;
167947
+ }
167948
+ this.compositeMaterial.setFloat("cloudDisplayEncoded", this.isDisplayEncoded() ? 1 : 0);
167949
+ this.previousViewProjection.copyFrom(camera.getTransformationMatrix());
167950
+ }
167951
+ isDisplayEncoded() {
167952
+ return HdrpRendering.IsParity(this.scene) !== true && this.scene.imageProcessingConfiguration.applyByPostProcess !== true;
167953
+ }
167954
+ trackCamera(camera) {
167955
+ const eye = camera.globalPosition;
167956
+ this.currentEye[0] = eye.x;
167957
+ this.currentEye[1] = eye.y;
167958
+ this.currentEye[2] = eye.z;
167959
+ const isCut = camera !== this.lastCamera || VolumetricCloudsMath.IsCameraCut(this.hasPreviousEye ? this.previousEye : null, this.currentEye);
167960
+ if (isCut)
167961
+ this.hasHistory = false;
167962
+ this.lastCamera = camera;
167963
+ this.previousEye[0] = eye.x;
167964
+ this.previousEye[1] = eye.y;
167965
+ this.previousEye[2] = eye.z;
167966
+ this.hasPreviousEye = true;
167967
+ }
167968
+ updateTier() {
167969
+ if (this.disposed || !this.tierTracker.addFrame(this.scene.getEngine().getDeltaTime()))
167970
+ return;
167971
+ this.tier = this.tierTracker.tier;
167972
+ this.settings = VolumetricCloudsMath.TierSettings(this.tier, this.block);
167973
+ if (this.tierTracker.isSettled) {
167974
+ 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");
167975
+ return;
167976
+ }
167977
+ LightingConversions.WarnOnce("urp:clouds:tier" + this.tier, "Volumetric clouds: frames ran over " + CLOUD_TIER_BUDGET_MS + " ms, trying tier " + this.tier
167978
+ + " (resolution " + this.settings.resolutionScale + ", " + this.settings.primarySteps + " primary steps, " + this.settings.lightSteps + " light steps)");
167979
+ }
167980
+ reportLoweredSettings() {
167981
+ const block = this.block;
167982
+ const settings = this.settings;
167983
+ if (typeof block.numprimarysteps === "number" && settings.primarySteps < block.numprimarysteps) {
167984
+ LightingConversions.WarnOnce("urp:clouds:primarysteps:" + this.tier, "Volumetric clouds: " + block.numprimarysteps + " primary steps are capped at " + settings.primarySteps + " in the browser (tier " + this.tier + ")");
167985
+ }
167986
+ if (typeof block.longraysamplemultiplier === "number" && settings.longRayMultiplier < block.longraysamplemultiplier) {
167987
+ 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 + ")");
167988
+ }
167989
+ if (typeof block.numlightsteps === "number" && settings.lightSteps < block.numlightsteps) {
167990
+ LightingConversions.WarnOnce("urp:clouds:lightsteps:" + this.tier, "Volumetric clouds: " + block.numlightsteps + " light steps are capped at " + settings.lightSteps + " (tier " + this.tier + ")");
167991
+ }
167992
+ if (typeof block.resolutionscale === "number" && settings.resolutionScale < block.resolutionscale) {
167993
+ LightingConversions.WarnOnce("urp:clouds:resolution:" + this.tier, "Volumetric clouds: resolution scale " + block.resolutionscale + " is lowered to " + settings.resolutionScale + " (tier " + this.tier + ")");
167994
+ }
167995
+ }
167996
+ advanceWind(deltaSeconds) {
167997
+ const block = this.block;
167998
+ const signedDelta = Math.min(Math.max(deltaSeconds, 0), 1) * CLOUD_WIND_KMH_TO_MS;
167999
+ const theta = Tools.ToRadians(block.globalorientation || 0);
168000
+ const speed = block.globalspeed || 0;
168001
+ this.windOffset[0] = (this.windOffset[0] + signedDelta * speed * Math.cos(theta)) % CLOUD_WIND_WRAP_METERS;
168002
+ this.windOffset[1] = (this.windOffset[1] + signedDelta * speed * Math.sin(theta)) % CLOUD_WIND_WRAP_METERS;
168003
+ this.verticalShapeOffset = (this.verticalShapeOffset + signedDelta * (block.verticalshapewindspeed || 0)) % CLOUD_WIND_WRAP_METERS;
168004
+ this.verticalErosionOffset = (this.verticalErosionOffset + signedDelta * (block.erosionspeedmultiplier || 0)) % CLOUD_WIND_WRAP_METERS;
168005
+ }
168006
+ resizeTargets() {
168007
+ if (!this.updateTargetSize())
168008
+ return;
168009
+ const size = { width: this.targetWidth, height: this.targetHeight };
168010
+ this.marchTarget.resize(size);
168011
+ for (const history of this.historyTargets)
168012
+ history.resize(size);
168013
+ this.hasHistory = false;
168014
+ }
168015
+ updateTargetSize() {
168016
+ const engine = this.scene.getEngine();
168017
+ const scale = this.settings.resolutionScale;
168018
+ const width = Math.max(1, Math.round(engine.getRenderWidth() * scale));
168019
+ const height = Math.max(1, Math.round(engine.getRenderHeight() * scale));
168020
+ if (width === this.targetWidth && height === this.targetHeight)
168021
+ return false;
168022
+ this.targetWidth = width;
168023
+ this.targetHeight = height;
168024
+ return true;
168025
+ }
168026
+ bindMarch(effect) {
168027
+ const scene = this.scene;
168028
+ const camera = scene.activeCamera;
168029
+ if (camera == null)
168030
+ return;
168031
+ const block = this.block;
168032
+ const settings = this.settings;
168033
+ const mirrorX = this.mirror.m[0];
168034
+ camera.getTransformationMatrix().invertToRef(this.inverseViewProjection);
168035
+ this.inverseViewProjection.multiplyToRef(this.mirror, this.inverseViewProjection);
168036
+ effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
168037
+ const eye = camera.globalPosition;
168038
+ effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
168039
+ camera.getDirectionToRef(this.localForward, this.cameraForward);
168040
+ effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, mirrorX);
168041
+ const hasDepth = this.sceneDepth != null;
168042
+ effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
168043
+ effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.frameSeed, this.preExposure);
168044
+ if (this.hasAerial)
168045
+ this.bindAerial(effect);
168046
+ const center = this.planetCenter;
168047
+ const bottomRadius = this.planetRadius + (block.bottomaltitude || 0);
168048
+ const altitudeRange = block.altituderange || 1;
168049
+ effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
168050
+ effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, VolumetricCloudsMath.MaximumStep(altitudeRange, block.numprimarysteps, settings.primarySteps), settings.primarySteps);
168051
+ const isAutomaticFade = block.fadeinmode !== CLOUD_FADE_MODE_MANUAL;
168052
+ const fadeStart = isAutomaticFade ? Math.max(altitudeRange * CLOUD_AUTO_FADE_START_FRACTION, camera.minZ) : Math.max(block.fadeinstart || 0, camera.minZ);
168053
+ const fadeDistance = isAutomaticFade ? altitudeRange * CLOUD_AUTO_FADE_DISTANCE_FRACTION : (block.fadeindistance || 0);
168054
+ effect.setFloat4("cloudSteps", settings.longRayMultiplier, settings.lightSteps, fadeStart, fadeDistance);
168055
+ effect.setFloat4("cloudShape", block.shapescale, block.shapefactor, block.erosionscale, block.erosionfactor);
168056
+ const density = block.densitymultiplier * block.densitymultiplier * CLOUD_DENSITY_SCALE;
168057
+ effect.setFloat4("cloudDetail", block.erosionocclusion, block.microerosionscale, block.microerosionfactor, density);
168058
+ const shapeOffset = Array.isArray(block.shapeoffset) ? block.shapeoffset : [0, 0, 0];
168059
+ effect.setFloat4("cloudShapeOffset", shapeOffset[0], shapeOffset[2], shapeOffset[1], block.altitudedistortion * CLOUD_ALTITUDE_DISTORTION_SCALE);
168060
+ const theta = Tools.ToRadians(block.globalorientation || 0);
168061
+ effect.setFloat4("cloudWind", -Math.cos(theta), -Math.sin(theta), this.windOffset[0], this.windOffset[1]);
168062
+ effect.setFloat4("cloudWindSpeed", block.shapespeedmultiplier, block.erosionspeedmultiplier, this.verticalShapeOffset, this.verticalErosionOffset);
168063
+ const coverageInverseSize = 1 / Math.max(CLOUD_MINIMUM_COVERAGE_PATTERN_METERS, block.coveragepatternsize || CLOUD_DEFAULT_COVERAGE_PATTERN_METERS);
168064
+ const coverageGap = (typeof block.coveragegapamount === "number") ? block.coveragegapamount : CLOUD_DEFAULT_COVERAGE_GAP;
168065
+ effect.setFloat4("cloudCoverage", block.coveragevariation || 0, coverageInverseSize, coverageGap, 0);
168066
+ const coverageOffset = Array.isArray(block.coveragepatternoffset) ? block.coveragepatternoffset : [0, 0];
168067
+ effect.setFloat4("cloudCoverageOffset", coverageOffset[0], coverageOffset[1], 0, 0);
168068
+ effect.setFloat4("cloudScattering", 1 - block.multiscattering * CLOUD_MULTI_SCATTERING_SCALE, block.powdereffectintensity, 0, 0);
168069
+ effect.setFloat4("cloudTint", this.scatteringTint.r, this.scatteringTint.g, this.scatteringTint.b, 0);
168070
+ this.bindLighting(effect, mirrorX);
168071
+ effect.setTexture("worleyNoise", this.worleyTexture);
168072
+ effect.setTexture("erosionNoise", this.erosionTexture);
168073
+ effect.setTexture("cloudCurves", this.curveTexture);
168074
+ }
168075
+ bindLighting(effect, mirrorX) {
168076
+ const block = this.block;
168077
+ const sun = this.resolveSun();
168078
+ const sunScale = (this.sky != null && typeof this.sky.sunintensityscale === "number") ? this.sky.sunintensityscale : 1;
168079
+ const dimmer = (typeof block.sunlightdimmer === "number") ? block.sunlightdimmer : 1;
168080
+ if (sun != null) {
168081
+ const direction = this.sunDirection;
168082
+ const strength = sun.intensity * sunScale * dimmer;
168083
+ effect.setFloat4("cloudSunDirection", -direction.x * mirrorX, -direction.y, -direction.z, 0);
168084
+ effect.setFloat4("cloudSunColor", sun.diffuse.r * strength, sun.diffuse.g * strength, sun.diffuse.b * strength, 0);
168085
+ }
168086
+ else {
168087
+ effect.setFloat4("cloudSunDirection", 0, 1, 0, 0);
168088
+ effect.setFloat4("cloudSunColor", 0, 0, 0, 0);
168089
+ }
168090
+ this.refreshAmbient();
168091
+ const ambientDimmer = (typeof block.ambientlightprobedimmer === "number") ? block.ambientlightprobedimmer : 1;
168092
+ effect.setFloat4("cloudAmbientTop", this.ambientTop.r * ambientDimmer, this.ambientTop.g * ambientDimmer, this.ambientTop.b * ambientDimmer, 0);
168093
+ effect.setFloat4("cloudAmbientBottom", this.ambientBottom.r * ambientDimmer, this.ambientBottom.g * ambientDimmer, this.ambientBottom.b * ambientDimmer, 0);
168094
+ }
168095
+ bindAerial(effect) {
168096
+ const sky = HdrpPhysicallyBasedSky.Get(this.scene);
168097
+ const isBound = sky != null && sky.bindAerialPerspective(effect, VolumetricCloudsShaders.AerialSampler, VolumetricCloudsShaders.AerialCameraUniform);
168098
+ if (isBound)
168099
+ return;
168100
+ effect.setTexture(VolumetricCloudsShaders.AerialSampler, this.placeholderAerial);
168101
+ effect.setFloat4(VolumetricCloudsShaders.AerialCameraUniform, 0, 0, 1, 0);
168102
+ }
168103
+ resolveSun() {
168104
+ if (this.sunLight == null || this.sunLight.isDisposed() || !this.sunLight.isEnabled()) {
168105
+ const lights = HdrpPhysicallyBasedSky.InteractingLights(this.scene.lights);
168106
+ this.sunLight = (lights.length > 0) ? lights[0] : null;
168107
+ }
168108
+ const sun = this.sunLight;
168109
+ if (sun == null)
168110
+ return null;
168111
+ sun.computeTransformedInformation();
168112
+ const transformed = sun.transformedDirection;
168113
+ this.sunDirection.copyFrom((transformed != null && sun.parent != null) ? transformed : sun.direction).normalize();
168114
+ return sun;
168115
+ }
168116
+ refreshAmbient() {
168117
+ const sky = HdrpPhysicallyBasedSky.Get(this.scene);
168118
+ const source = (sky != null) ? sky : this.scene.environmentTexture;
168119
+ const captureCount = (sky != null) ? sky.captureCount : 0;
168120
+ if (source === this.ambientSource && captureCount === this.ambientCaptureCount && source != null)
168121
+ return;
168122
+ this.ambientSource = source;
168123
+ this.ambientCaptureCount = captureCount;
168124
+ if (sky != null) {
168125
+ this.ambientTop.copyFrom(sky.ambientAt(Vector3.UpReadOnly));
168126
+ this.ambientBottom.copyFrom(sky.ambientAt(Vector3.DownReadOnly));
168127
+ return;
168128
+ }
168129
+ const environment = this.scene.environmentTexture;
168130
+ const polynomial = (environment != null) ? environment.sphericalPolynomial : null;
168131
+ if (polynomial != null) {
168132
+ this.ambientTop.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.UpReadOnly));
168133
+ this.ambientBottom.copyFrom(HdrpPbSkyMath.EvaluatePolynomial(polynomial, Vector3.DownReadOnly));
168134
+ return;
168135
+ }
168136
+ this.ambientTop.copyFrom(this.scene.ambientColor);
168137
+ this.ambientBottom.copyFrom(this.scene.ambientColor);
168138
+ }
168139
+ bindTemporal(effect) {
168140
+ const camera = this.scene.activeCamera;
168141
+ if (camera == null)
168142
+ return;
168143
+ const mirrorX = this.mirror.m[0];
168144
+ this.mirror.multiplyToRef(this.previousViewProjection, this.previousViewProjectionUnity);
168145
+ effect.setMatrix("cloudInvViewProj", this.inverseViewProjection);
168146
+ effect.setMatrix("cloudPrevViewProj", this.previousViewProjectionUnity);
168147
+ const eye = camera.globalPosition;
168148
+ effect.setFloat4("cloudEye", eye.x * mirrorX, eye.y, eye.z, 0);
168149
+ effect.setFloat4("cloudForward", this.cameraForward.x * mirrorX, this.cameraForward.y, this.cameraForward.z, 0);
168150
+ const hasDepth = this.sceneDepth != null;
168151
+ effect.setTexture("sceneDepth", hasDepth ? this.sceneDepth : this.placeholderDepth);
168152
+ const accumulation = this.block.temporalaccumulationfactor;
168153
+ effect.setFloat4("cloudCamera", (camera.maxZ > 0) ? camera.maxZ : Number.MAX_VALUE, hasDepth ? 1 : 0, this.hasHistory ? 1 : 0, accumulation);
168154
+ const center = this.planetCenter;
168155
+ const bottomRadius = this.planetRadius + (this.block.bottomaltitude || 0);
168156
+ const altitudeRange = this.block.altituderange || 1;
168157
+ effect.setFloat4("cloudPlanet", center.x * mirrorX, center.y, center.z, this.planetRadius);
168158
+ effect.setFloat4("cloudLayer", bottomRadius, bottomRadius + altitudeRange, altitudeRange / CLOUD_MAX_STEP_DIVISOR, this.settings.primarySteps);
168159
+ effect.setFloat4("cloudTexel", 1 / this.targetWidth, 1 / this.targetHeight, 0, 0);
168160
+ effect.setTexture("cloudCurrent", this.marchTarget);
168161
+ effect.setTexture("cloudHistory", this.historyTargets[1 - this.historyIndex]);
168162
+ }
168163
+ static LinearScatteringTint(tint) {
168164
+ const source = Array.isArray(tint) ? tint : [0, 0, 0];
168165
+ 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();
168166
+ }
168167
+ static CanvasCssWidth(scene) {
168168
+ const engine = scene.getEngine();
168169
+ const canvas = engine.getRenderingCanvas();
168170
+ if (canvas != null && typeof canvas.clientWidth === "number" && canvas.clientWidth > 0)
168171
+ return canvas.clientWidth;
168172
+ return engine.getRenderWidth();
168173
+ }
168174
+ }
168175
+ VolumetricClouds.MeshName = "Volumetric Clouds";
168176
+ export class VolumetricCloudsShaders {
168177
+ static Register() {
168178
+ if (VolumetricCloudsShaders.registered === true)
168179
+ return;
168180
+ const glsl = ShaderStore.ShadersStore;
168181
+ const wgsl = ShaderStore.ShadersStoreWGSL;
168182
+ const shaders = VolumetricCloudsShaders;
168183
+ glsl[shaders.MarchName + "FragmentShader"] = shaders.GlslMarch();
168184
+ glsl[shaders.CompositeName + "VertexShader"] = shaders.GlslCompositeVertex();
168185
+ glsl[shaders.CompositeName + "FragmentShader"] = shaders.GlslCompositeFragment();
168186
+ glsl[shaders.TemporalName + "FragmentShader"] = shaders.GlslTemporal();
168187
+ wgsl[shaders.MarchName + "FragmentShader"] = shaders.WgslMarch();
168188
+ wgsl[shaders.CompositeName + "VertexShader"] = shaders.WgslCompositeVertex();
168189
+ wgsl[shaders.CompositeName + "FragmentShader"] = shaders.WgslCompositeFragment();
168190
+ wgsl[shaders.TemporalName + "FragmentShader"] = shaders.WgslTemporal();
168191
+ VolumetricCloudsShaders.registered = true;
168192
+ }
168193
+ static AllSources() {
168194
+ const shaders = VolumetricCloudsShaders;
168195
+ return [
168196
+ shaders.GlslMarch(), shaders.GlslCompositeVertex(), shaders.GlslCompositeFragment(), shaders.GlslTemporal(),
168197
+ shaders.WgslMarch(), shaders.WgslCompositeVertex(), shaders.WgslCompositeFragment(), shaders.WgslTemporal()
168198
+ ];
168199
+ }
168200
+ static GlslMarchUniforms() {
168201
+ return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n";
168202
+ }
168203
+ static GlslConstants() {
168204
+ return `const float CLOUD_DENSITY_THRESHOLD = 0.001;
168205
+ const int EMPTY_STEPS_BEFORE_LARGE_STEPS = 8;
168206
+ const float FORWARD_ECCENTRICITY = 0.7;
168207
+ const float BACKWARD_ECCENTRICITY = 0.7;
168208
+ const float MIN_EROSION_DISTANCE = 3000.0;
168209
+ const float MAX_EROSION_DISTANCE = 100000.0;
168210
+ const float NOISE_NORMALIZATION = 100000.0;
168211
+ const float MAX_SKYBOX_DISTANCE = 200000.0;
168212
+ const float LIGHT_STEP_MAXIMAL_SIZE = 1000.0;
168213
+ const float CLOUD_PI = 3.14159265359;
168214
+ const int MAX_PRIMARY_STEPS = 1024;
168215
+ const int MAX_LIGHT_STEPS = 16;
168216
+ `;
168217
+ }
168218
+ static GlslGeometry() {
168219
+ return `vec2 cloudIntersectSphere(float sphereRadius, float cosChi, float radialDistance) {
168220
+ float ratio = sphereRadius / radialDistance;
168221
+ float d = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
168222
+ return (d < 0.0) ? vec2(d) : radialDistance * vec2(-cosChi - sqrt(d), -cosChi + sqrt(d));
168223
+ }
168224
+ float cloudHorizonCosine(float radialDistance) {
168225
+ float sinHorizon = cloudPlanet.w / radialDistance;
168226
+ return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
168227
+ }
168228
+ bool cloudIntersectVolume(vec3 originPS, vec3 direction, out float entry, out float exitDistance) {
168229
+ float radialDistance = length(originPS);
168230
+ float cosChi = dot(originPS, direction) / radialDistance;
168231
+ vec2 inner = cloudIntersectSphere(cloudLayer.x, cosChi, radialDistance);
168232
+ vec2 outer = cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance);
168233
+ if (inner.x < 0.0 && inner.y >= 0.0) {
168234
+ entry = inner.y;
168235
+ exitDistance = outer.y;
168236
+ return cosChi >= cloudHorizonCosine(radialDistance);
168237
+ }
168238
+ entry = max(outer.x, 0.0);
168239
+ exitDistance = (inner.x >= 0.0) ? inner.x : outer.y;
168240
+ return outer.y >= 0.0;
168241
+ }
168242
+ int cloudRaySphere(vec3 startWS, vec3 direction, float radius, out vec2 result) {
168243
+ vec3 startPS = startWS + vec3(0.0, cloudPlanet.w, 0.0);
168244
+ float a = dot(direction, direction);
168245
+ float b = 2.0 * dot(direction, startPS);
168246
+ float c = dot(startPS, startPS) - radius * radius;
168247
+ float d = b * b - 4.0 * a * c;
168248
+ result = vec2(0.0);
168249
+ int solutions = 0;
168250
+ if (d >= 0.0) {
168251
+ float q = -0.5 * (b + ((b >= 0.0) ? 1.0 : -1.0) * sqrt(d));
168252
+ result = vec2(c / q, q / a);
168253
+ solutions = 2;
168254
+ if (result.x < 0.0) { solutions--; result.x = result.y; }
168255
+ if (result.y < 0.0) solutions--;
168256
+ }
168257
+ return solutions;
168258
+ }
168259
+ bool cloudRange(vec3 originWS, vec3 direction, out float rangeStart, out float rangeEnd) {
168260
+ #ifdef CLOUD_LOCAL
168261
+ return cloudIntersectVolume(originWS - cloudPlanet.xyz, direction, rangeStart, rangeEnd);
168262
+ #else
168263
+ vec2 inner = vec2(0.0);
168264
+ vec2 outer = vec2(0.0);
168265
+ cloudRaySphere(originWS, direction, cloudLayer.x, inner);
168266
+ cloudRaySphere(originWS, direction, cloudLayer.y, outer);
168267
+ rangeStart = inner.x;
168268
+ rangeEnd = outer.x;
168269
+ return true;
168270
+ #endif
168271
+ }
168272
+ `;
168273
+ }
168274
+ static GlslDensity() {
168275
+ return `struct CloudProperties {
168276
+ float density;
168277
+ float ambientOcclusion;
168278
+ float height;
168279
+ float sigmaT;
168280
+ };
168281
+ float cloudRemap(float x, float a, float b, float c, float d) {
168282
+ return (((x - a) / (b - a)) * (d - c)) + c;
168283
+ }
168284
+ float cloudNormalizedHeight(vec3 positionPS) {
168285
+ return clamp((length(positionPS) - cloudLayer.x) / (cloudLayer.y - cloudLayer.x), 0.0, 1.0);
168286
+ }
168287
+ vec3 cloudAnimateShape(vec3 positionPS) {
168288
+ vec3 position = positionPS;
168289
+ position.y += position.x / 3.0 + position.z / 7.0;
168290
+ return position + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.x + vec3(0.0, cloudWindSpeed.z, 0.0);
168291
+ }
168292
+ vec3 cloudAnimateErosion(vec3 positionPS) {
168293
+ return positionPS + vec3(cloudWind.z, 0.0, cloudWind.w) * cloudWindSpeed.y + vec3(0.0, cloudWindSpeed.w, 0.0);
168294
+ }
168295
+ float cloudCoverageHash(vec2 cell) {
168296
+ vec3 p = fract(vec3(cell.x, cell.y, cell.x) * 0.1031);
168297
+ p += dot(p, p.yzx + 33.33);
168298
+ return fract((p.x + p.y) * p.z);
168299
+ }
168300
+ float cloudCoverageNoise(vec2 position) {
168301
+ vec2 cell = floor(position);
168302
+ vec2 blend = fract(position);
168303
+ blend = blend * blend * (3.0 - 2.0 * blend);
168304
+ float bottom = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2(1.0, 0.0)), blend.x);
168305
+ float top = mix(cloudCoverageHash(cell + vec2(0.0, 1.0)), cloudCoverageHash(cell + vec2(1.0, 1.0)), blend.x);
168306
+ return mix(bottom, top, blend.y);
168307
+ }
168308
+ vec2 cloudCoverageAt(vec3 positionPS) {
168309
+ float coverage = 0.9;
168310
+ float shapeFill = 0.0;
168311
+ if (cloudCoverage.x != 0.0) {
168312
+ vec2 coordinates = (positionPS.xz + cloudWind.zw * cloudWindSpeed.x + cloudCoverageOffset.xy) * cloudCoverage.y;
168313
+ float coverageNoise = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2(17.13, 43.71)) * 0.3;
168314
+ float threshold = mix(-0.15, 1.15, cloudCoverage.z);
168315
+ float mask = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
168316
+ if (cloudCoverage.x > 0.0) {
168317
+ coverage *= mix(1.0, mask, cloudCoverage.x);
168318
+ } else {
168319
+ shapeFill = clamp(-cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
168320
+ coverage = mix(coverage, 1.0, shapeFill);
168321
+ }
168322
+ }
168323
+ return vec2(coverage, shapeFill);
168324
+ }
168325
+ CloudProperties cloudPropertiesAt(vec3 samplePositionPS, float noiseMip, float erosionMip, bool cheapVersion, bool lightSampling) {
168326
+ CloudProperties properties = CloudProperties(0.0, 0.0, 0.0, 0.0);
168327
+ vec3 positionPS = samplePositionPS;
168328
+ #ifndef CLOUD_LOCAL
168329
+ if (positionPS.y < cloudPlanet.w) return properties;
168330
+ #endif
168331
+ properties.ambientOcclusion = 1.0;
168332
+ properties.height = cloudNormalizedHeight(positionPS);
168333
+ #ifndef CLOUD_LOCAL
168334
+ positionPS.xz += cloudEye.xz;
168335
+ #endif
168336
+ vec3 baseCoordinates = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * cloudShape.x - cloudShapeOffset.xyz;
168337
+ baseCoordinates += properties.height * vec3(cloudWind.x, cloudWind.y, 0.0) * cloudShapeOffset.w;
168338
+ float lowFrequencyNoise = textureLod(worleyNoise, baseCoordinates, noiseMip).r;
168339
+ vec2 coverage = cloudCoverageAt(positionPS);
168340
+ if (coverage.x <= CLOUD_DENSITY_THRESHOLD) return properties;
168341
+ vec3 curves = textureLod(cloudCurves, vec2(properties.height, 0.5), 0.0).xyz;
168342
+ float shapeFactor = mix(0.1, 1.0, cloudShape.y * (1.0 - coverage.y)) * curves.y;
168343
+ float erosionFactor = cloudShape.w * curves.y;
168344
+ float microDetailFactor = cloudDetail.z * curves.y;
168345
+ lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
168346
+ float baseCloud = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
168347
+ baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
168348
+ properties.sigmaT = 0.04;
168349
+ float occlusionBlend = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
168350
+ properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
168351
+ if (!cheapVersion) {
168352
+ vec3 erosionCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudShape.z;
168353
+ float erosionValue = 1.0 - textureLod(erosionNoise, erosionCoordinates, erosionMip).r;
168354
+ erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
168355
+ properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * cloudDetail.x), 0.0, 1.0);
168356
+ baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
168357
+ #ifdef CLOUD_MICRO_EROSION
168358
+ vec3 fineCoordinates = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * cloudDetail.y;
168359
+ float fineValue = 1.0 - textureLod(erosionNoise, fineCoordinates, erosionMip).r;
168360
+ fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
168361
+ baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
168362
+ #endif
168363
+ }
168364
+ if (lightSampling) {
168365
+ baseCloud -= erosionFactor * 0.1;
168366
+ #ifdef CLOUD_MICRO_EROSION
168367
+ baseCloud -= microDetailFactor * 0.15;
168368
+ #endif
168369
+ }
168370
+ properties.density = max(0.0, baseCloud) * cloudDetail.w;
168371
+ return properties;
168372
+ }
168373
+ `;
168374
+ }
168375
+ static GlslLighting() {
168376
+ return `float cloudPhase(float anisotropy, float cosTheta) {
168377
+ float denominator = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
168378
+ float falloff = inversesqrt(max(denominator, 1e-6));
168379
+ return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
168380
+ }
168381
+ float cloudPowder(float density, float cosAngle, float intensity) {
168382
+ float powder = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
168383
+ return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
168384
+ }
168385
+ vec3 cloudSunTransmittance(vec3 positionPS, vec3 sunDirection, vec2 phase) {
168386
+ float radialDistance = length(positionPS);
168387
+ float cosChi = dot(positionPS, sunDirection) / radialDistance;
168388
+ vec3 transmittance = vec3(0.0);
168389
+ if (cosChi < cloudHorizonCosine(radialDistance)) return transmittance;
168390
+ float lightSteps = cloudSteps.y;
168391
+ float totalLightDistance = clamp(cloudIntersectSphere(cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
168392
+ float intervalSize = totalLightDistance / lightSteps;
168393
+ float opticalDepth = 0.0;
168394
+ for (int j = 0; j < MAX_LIGHT_STEPS; j++) {
168395
+ if (float(j) >= lightSteps) break;
168396
+ float distanceAlongLight = intervalSize * (0.25 + float(j));
168397
+ CloudProperties lightProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * float(j) / lightSteps, 0.0, true, true);
168398
+ opticalDepth += lightProperties.density * lightProperties.sigmaT;
168399
+ }
168400
+ vec3 extinction = intervalSize * opticalDepth * cloudTint.rgb;
168401
+ transmittance += exp(-extinction) * phase.x;
168402
+ transmittance += exp(-extinction * cloudScattering.x) * (phase.y * cloudScattering.x);
168403
+ return transmittance;
168404
+ }
168405
+ `;
168406
+ }
168407
+ static GlslAerial() {
168408
+ const skyShaders = HdrpPbSkyShaders;
168409
+ const fogPass = HdrpFogPass;
168410
+ if (skyShaders == null || fogPass == null)
168411
+ return "";
168412
+ return "#ifdef CLOUD_AERIAL\nprecision highp sampler2DArray;\nuniform sampler2DArray apLut;\nuniform vec4 cloudApCamera;\n"
168413
+ + skyShaders.GlslUniforms() + skyShaders.GlslCommon() + fogPass.AerialPerspectiveGlsl() + "#endif\n";
168414
+ }
168415
+ static GlslMarch() {
168416
+ const shaders = VolumetricCloudsShaders;
168417
+ return "precision highp float;\nprecision highp sampler3D;\nvarying vec2 vUV;\nuniform sampler3D worleyNoise;\nuniform sampler3D erosionNoise;\nuniform sampler2D cloudCurves;\nuniform sampler2D sceneDepth;\n"
168418
+ + shaders.GlslMarchUniforms() + shaders.GlslConstants() + shaders.GlslGeometry() + shaders.GlslDensity() + shaders.GlslLighting() + shaders.GlslAerial() + `
168419
+ uint cloudJenkins(uint value) {
168420
+ uint x = value;
168421
+ x += (x << 10u);
168422
+ x ^= (x >> 6u);
168423
+ x += (x << 3u);
168424
+ x ^= (x >> 11u);
168425
+ x += (x << 15u);
168426
+ return x;
168427
+ }
168428
+ float cloudRandom(vec2 pixel, float seed) {
168429
+ uvec3 v = uvec3(uvec2(pixel), uint(seed));
168430
+ uint hash = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
168431
+ return uintBitsToFloat((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
168432
+ }
168433
+ void main(void) {
168434
+ gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
168435
+ vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
168436
+ vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
168437
+ float maxRayLength = MAX_SKYBOX_DISTANCE;
168438
+ if (cloudCamera.y > 0.5) {
168439
+ float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
168440
+ if (viewDepth < cloudCamera.x * 0.999) {
168441
+ #ifdef CLOUD_LOCAL
168442
+ maxRayLength = viewDepth / max(dot(direction, cloudForward.xyz), 0.0001);
168443
+ #else
168444
+ return;
168445
+ #endif
168446
+ }
168447
+ }
168448
+ #ifdef CLOUD_LOCAL
168449
+ vec3 originWS = cloudEye.xyz;
168450
+ #else
168451
+ vec3 originWS = vec3(0.0);
168452
+ #endif
168453
+ float rangeStart = 0.0;
168454
+ float rangeEnd = 0.0;
168455
+ if (!cloudRange(originWS, direction, rangeStart, rangeEnd)) return;
168456
+ if (maxRayLength < rangeStart) return;
168457
+ float integrationNoise = cloudRandom(gl_FragCoord.xy, cloudCamera.z);
168458
+ float primarySteps = cloudLayer.w;
168459
+ float totalDistance = min(rangeEnd, maxRayLength) - rangeStart;
168460
+ float stepSize = min(totalDistance / primarySteps, cloudLayer.z);
168461
+ totalDistance = stepSize * primarySteps;
168462
+ float layerThickness = max(1.0, cloudLayer.y - cloudLayer.x);
168463
+ float sampleMultiplier = min(max(1.0, totalDistance / layerThickness), max(1.0, cloudSteps.x));
168464
+ int primaryStepCount = min(int(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
168465
+ stepSize = totalDistance / float(max(1, primaryStepCount));
168466
+ vec3 sunDirection = cloudSunDirection.xyz;
168467
+ float cosAngle = dot(direction, sunDirection);
168468
+ vec2 phase = vec2(
168469
+ cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
168470
+ cloudPhase(FORWARD_ECCENTRICITY * cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * cloudScattering.x, cosAngle));
168471
+ vec3 scattering = vec3(0.0);
168472
+ float ambient = 0.0;
168473
+ float transmittance = 1.0;
168474
+ float meanDistance = 0.0;
168475
+ float meanDistanceDivider = 0.0;
168476
+ float currentDistance = integrationNoise;
168477
+ vec3 currentPositionWS = originWS + (rangeStart + currentDistance) * direction;
168478
+ bool activeSampling = true;
168479
+ int sequentialEmptySamples = 0;
168480
+ int currentIndex = 0;
168481
+ for (int iteration = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
168482
+ if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) break;
168483
+ float cameraDistance = rangeStart + currentDistance;
168484
+ float densityFade = clamp((cameraDistance - cloudSteps.z) / (cloudSteps.z + cloudSteps.w), 0.0, 1.0);
168485
+ float erosionMip = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
168486
+ vec3 currentPositionPS = currentPositionWS - cloudPlanet.xyz;
168487
+ if (activeSampling) {
168488
+ CloudProperties properties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
168489
+ properties.density *= densityFade;
168490
+ if (properties.density > CLOUD_DENSITY_THRESHOLD) {
168491
+ float transmittanceDensity = transmittance * properties.density;
168492
+ meanDistance += cameraDistance * transmittanceDensity;
168493
+ meanDistanceDivider += transmittanceDensity;
168494
+ float stepTransmittance = exp(-properties.density * properties.sigmaT * stepSize);
168495
+ float powder = cloudPowder(properties.density, cosAngle, cloudScattering.y);
168496
+ vec3 sunLuminance = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
168497
+ float absorbed = transmittance - transmittance * stepTransmittance;
168498
+ scattering += sunLuminance * absorbed;
168499
+ ambient += properties.ambientOcclusion * absorbed;
168500
+ transmittance *= stepTransmittance;
168501
+ if (transmittance < 0.003) {
168502
+ transmittance = 0.0;
168503
+ break;
168504
+ }
168505
+ sequentialEmptySamples = 0;
168506
+ } else {
168507
+ sequentialEmptySamples++;
168508
+ }
168509
+ if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) activeSampling = false;
168510
+ float relativeStepSize = mix(integrationNoise, 1.0, clamp(float(currentIndex), 0.0, 1.0));
168511
+ currentPositionWS += direction * stepSize * relativeStepSize;
168512
+ currentDistance += stepSize * relativeStepSize;
168513
+ } else {
168514
+ CloudProperties properties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
168515
+ properties.density *= densityFade;
168516
+ if (properties.density < CLOUD_DENSITY_THRESHOLD) {
168517
+ currentPositionWS += direction * stepSize * 2.0;
168518
+ currentDistance += stepSize * 2.0;
168519
+ } else {
168520
+ currentPositionWS -= direction * stepSize;
168521
+ currentDistance -= stepSize;
168522
+ currentIndex -= 1;
168523
+ activeSampling = true;
168524
+ sequentialEmptySamples = 0;
168525
+ }
168526
+ }
168527
+ currentIndex++;
168528
+ }
168529
+ if (meanDistance == 0.0) return;
168530
+ meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
168531
+ float relativeHeight = cloudNormalizedHeight(originWS - cloudPlanet.xyz + meanDistance * direction);
168532
+ vec3 ambientColor = max(vec3(0.0), mix(cloudAmbientBottom.rgb, cloudAmbientTop.rgb, relativeHeight));
168533
+ vec3 color = (cloudSunColor.rgb * scattering + ambientColor * ambient) * cloudCamera.w;
168534
+ #ifdef CLOUD_AERIAL
168535
+ if (cloudApCamera.w > 0.5) {
168536
+ vec3 apDirection = vec3(direction.x * cloudForward.w, direction.y, direction.z);
168537
+ vec3 apColor = vec3(0.0);
168538
+ vec3 apOpacity = vec3(0.0);
168539
+ fogAerialPerspective(vUV, cloudApCamera.xyz, apDirection, meanDistance, apColor, apOpacity);
168540
+ color = apColor * (1.0 - transmittance) + (vec3(1.0) - apOpacity) * color;
168541
+ }
168542
+ #endif
168543
+ gl_FragColor = vec4(color, transmittance);
168544
+ }
168545
+ `;
168546
+ }
168547
+ static GlslCompositeVertex() {
168548
+ return `precision highp float;
168549
+ attribute vec3 position;
168550
+ varying vec2 vUV;
168551
+ void main(void) {
168552
+ vUV = position.xy * 0.5 + 0.5;
168553
+ gl_Position = vec4(position.xy, 0.0, 1.0);
168554
+ }
168555
+ `;
168556
+ }
168557
+ static GlslCompositeFragment() {
168558
+ return `precision highp float;
168559
+ varying vec2 vUV;
168560
+ uniform sampler2D cloudLighting;
168561
+ uniform float cloudDisplayEncoded;
168562
+ const float CLOUD_DISPLAY_GAMMA = 2.2;
168563
+ const vec3 CLOUD_LUMINANCE = vec3(0.2126, 0.7152, 0.0722);
168564
+ void main(void) {
168565
+ vec4 cloud = texture2D(cloudLighting, vUV);
168566
+ vec3 scattering = cloud.rgb;
168567
+ float throughput = cloud.a;
168568
+ if (cloudDisplayEncoded > 0.5) {
168569
+ float encodeExponent = 1.0 / CLOUD_DISPLAY_GAMMA;
168570
+ float scatteringLuminance = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
168571
+ throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
168572
+ scattering = pow(clamp(scattering, vec3(0.0), vec3(1.0)), vec3(encodeExponent));
168573
+ }
168574
+ gl_FragColor = vec4(scattering, 1.0 - throughput);
168575
+ }
168576
+ `;
168577
+ }
168578
+ static GlslTemporal() {
168579
+ const shaders = VolumetricCloudsShaders;
168580
+ return "precision highp float;\nvarying vec2 vUV;\nuniform sampler2D cloudCurrent;\nuniform sampler2D cloudHistory;\nuniform sampler2D sceneDepth;\n"
168581
+ + shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform mat4 " + name + ";" : "uniform vec4 " + name + ";").join("\n") + "\n"
168582
+ + shaders.GlslGeometry() + `
168583
+ float cloudReprojectionDistance(vec3 direction) {
168584
+ float distanceToCloud = 0.0;
168585
+ float exitDistance = 0.0;
168586
+ #ifdef CLOUD_LOCAL
168587
+ if (!cloudIntersectVolume(cloudEye.xyz - cloudPlanet.xyz, direction, distanceToCloud, exitDistance)) distanceToCloud = exitDistance;
168588
+ #else
168589
+ cloudRange(vec3(0.0), direction, distanceToCloud, exitDistance);
168590
+ #endif
168591
+ distanceToCloud = max(distanceToCloud, 1.0);
168592
+ if (cloudCamera.y > 0.5) {
168593
+ float viewDepth = textureLod(sceneDepth, vUV, 0.0).r;
168594
+ if (viewDepth < cloudCamera.x * 0.999) distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, cloudForward.xyz), 0.0001));
168595
+ }
168596
+ return distanceToCloud;
168597
+ }
168598
+ void main(void) {
168599
+ vec4 current = textureLod(cloudCurrent, vUV, 0.0);
168600
+ gl_FragColor = current;
168601
+ if (cloudCamera.z < 0.5 || current.a >= 1.0) return;
168602
+ vec3 boxMin = current.rgb;
168603
+ vec3 boxMax = current.rgb;
168604
+ vec3 neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, -cloudTexel.y), 0.0).rgb;
168605
+ boxMin = min(boxMin, neighbour);
168606
+ boxMax = max(boxMax, neighbour);
168607
+ neighbour = textureLod(cloudCurrent, vUV + vec2(-cloudTexel.x, 0.0), 0.0).rgb;
168608
+ boxMin = min(boxMin, neighbour);
168609
+ boxMax = max(boxMax, neighbour);
168610
+ neighbour = textureLod(cloudCurrent, vUV + vec2(cloudTexel.x, 0.0), 0.0).rgb;
168611
+ boxMin = min(boxMin, neighbour);
168612
+ boxMax = max(boxMax, neighbour);
168613
+ neighbour = textureLod(cloudCurrent, vUV + vec2(0.0, cloudTexel.y), 0.0).rgb;
168614
+ boxMin = min(boxMin, neighbour);
168615
+ boxMax = max(boxMax, neighbour);
168616
+ vec4 farPoint = cloudInvViewProj * vec4(vUV * 2.0 - 1.0, 1.0, 1.0);
168617
+ vec3 direction = normalize(farPoint.xyz / farPoint.w - cloudEye.xyz);
168618
+ vec3 worldPosition = cloudEye.xyz + direction * cloudReprojectionDistance(direction);
168619
+ vec4 previousClip = cloudPrevViewProj * vec4(worldPosition, 1.0);
168620
+ vec2 previousUV = previousClip.xy / previousClip.w * 0.5 + 0.5;
168621
+ vec2 velocity = previousUV - vUV;
168622
+ if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) return;
168623
+ vec3 previous = clamp(textureLod(cloudHistory, previousUV, 0.0).rgb, boxMin, boxMax);
168624
+ float accumulation = cloudCamera.w;
168625
+ float intensity = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
168626
+ gl_FragColor = vec4(mix(current.rgb, previous, intensity), current.a);
168627
+ }
168628
+ `;
168629
+ }
168630
+ static WgslMarchUniforms() {
168631
+ return VolumetricCloudsShaders.MarchUniforms.map((name) => (name === "cloudInvViewProj") ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n";
168632
+ }
168633
+ static WgslConstants() {
168634
+ return `const CLOUD_DENSITY_THRESHOLD: f32 = 0.001;
168635
+ const EMPTY_STEPS_BEFORE_LARGE_STEPS: i32 = 8;
168636
+ const FORWARD_ECCENTRICITY: f32 = 0.7;
168637
+ const BACKWARD_ECCENTRICITY: f32 = 0.7;
168638
+ const MIN_EROSION_DISTANCE: f32 = 3000.0;
168639
+ const MAX_EROSION_DISTANCE: f32 = 100000.0;
168640
+ const NOISE_NORMALIZATION: f32 = 100000.0;
168641
+ const MAX_SKYBOX_DISTANCE: f32 = 200000.0;
168642
+ const LIGHT_STEP_MAXIMAL_SIZE: f32 = 1000.0;
168643
+ const CLOUD_PI: f32 = 3.14159265359;
168644
+ const MAX_PRIMARY_STEPS: i32 = 1024;
168645
+ const MAX_LIGHT_STEPS: i32 = 16;
168646
+ `;
168647
+ }
168648
+ static WgslGeometry() {
168649
+ return `struct CloudRange {
168650
+ hit: bool,
168651
+ entryDistance: f32,
168652
+ exitDistance: f32,
168653
+ };
168654
+ fn cloudIntersectSphere(sphereRadius: f32, cosChi: f32, radialDistance: f32) -> vec2f {
168655
+ let ratio: f32 = sphereRadius / radialDistance;
168656
+ let d: f32 = ratio * ratio - clamp(1.0 - cosChi * cosChi, 0.0, 1.0);
168657
+ if (d < 0.0) { return vec2f(d); }
168658
+ return radialDistance * vec2f(-cosChi - sqrt(d), -cosChi + sqrt(d));
168659
+ }
168660
+ fn cloudHorizonCosine(radialDistance: f32) -> f32 {
168661
+ let sinHorizon: f32 = uniforms.cloudPlanet.w / radialDistance;
168662
+ return -sqrt(clamp(1.0 - sinHorizon * sinHorizon, 0.0, 1.0));
168663
+ }
168664
+ fn cloudIntersectVolume(originPS: vec3f, direction: vec3f) -> CloudRange {
168665
+ let radialDistance: f32 = length(originPS);
168666
+ let cosChi: f32 = dot(originPS, direction) / radialDistance;
168667
+ let inner: vec2f = cloudIntersectSphere(uniforms.cloudLayer.x, cosChi, radialDistance);
168668
+ let outer: vec2f = cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance);
168669
+ if (inner.x < 0.0 && inner.y >= 0.0) {
168670
+ return CloudRange(cosChi >= cloudHorizonCosine(radialDistance), inner.y, outer.y);
168671
+ }
168672
+ return CloudRange(outer.y >= 0.0, max(outer.x, 0.0), select(outer.y, inner.x, inner.x >= 0.0));
168673
+ }
168674
+ fn cloudRaySphere(startWS: vec3f, direction: vec3f, radius: f32) -> vec2f {
168675
+ let startPS: vec3f = startWS + vec3f(0.0, uniforms.cloudPlanet.w, 0.0);
168676
+ let a: f32 = dot(direction, direction);
168677
+ let b: f32 = 2.0 * dot(direction, startPS);
168678
+ let c: f32 = dot(startPS, startPS) - radius * radius;
168679
+ let d: f32 = b * b - 4.0 * a * c;
168680
+ var result: vec2f = vec2f(0.0);
168681
+ if (d >= 0.0) {
168682
+ let q: f32 = -0.5 * (b + select(-1.0, 1.0, b >= 0.0) * sqrt(d));
168683
+ result = vec2f(c / q, q / a);
168684
+ if (result.x < 0.0) { result.x = result.y; }
168685
+ }
168686
+ return result;
168687
+ }
168688
+ fn cloudRange(originWS: vec3f, direction: vec3f) -> CloudRange {
168689
+ #ifdef CLOUD_LOCAL
168690
+ return cloudIntersectVolume(originWS - uniforms.cloudPlanet.xyz, direction);
168691
+ #else
168692
+ let inner: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.x);
168693
+ let outer: vec2f = cloudRaySphere(originWS, direction, uniforms.cloudLayer.y);
168694
+ return CloudRange(true, inner.x, outer.x);
168695
+ #endif
168696
+ }
168697
+ `;
168698
+ }
168699
+ static WgslDensity() {
168700
+ return `struct CloudProperties {
168701
+ density: f32,
168702
+ ambientOcclusion: f32,
168703
+ height: f32,
168704
+ sigmaT: f32,
168705
+ };
168706
+ fn cloudRemap(x: f32, a: f32, b: f32, c: f32, d: f32) -> f32 {
168707
+ return (((x - a) / (b - a)) * (d - c)) + c;
168708
+ }
168709
+ fn cloudNormalizedHeight(positionPS: vec3f) -> f32 {
168710
+ return clamp((length(positionPS) - uniforms.cloudLayer.x) / (uniforms.cloudLayer.y - uniforms.cloudLayer.x), 0.0, 1.0);
168711
+ }
168712
+ fn cloudAnimateShape(positionPS: vec3f) -> vec3f {
168713
+ var position: vec3f = positionPS;
168714
+ position.y = position.y + position.x / 3.0 + position.z / 7.0;
168715
+ return position + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.x + vec3f(0.0, uniforms.cloudWindSpeed.z, 0.0);
168716
+ }
168717
+ fn cloudAnimateErosion(positionPS: vec3f) -> vec3f {
168718
+ return positionPS + vec3f(uniforms.cloudWind.z, 0.0, uniforms.cloudWind.w) * uniforms.cloudWindSpeed.y + vec3f(0.0, uniforms.cloudWindSpeed.w, 0.0);
168719
+ }
168720
+ fn cloudCoverageHash(cell: vec2f) -> f32 {
168721
+ var p: vec3f = fract(vec3f(cell.x, cell.y, cell.x) * 0.1031);
168722
+ p = p + vec3f(dot(p, p.yzx + vec3f(33.33)));
168723
+ return fract((p.x + p.y) * p.z);
168724
+ }
168725
+ fn cloudCoverageNoise(position: vec2f) -> f32 {
168726
+ let cell: vec2f = floor(position);
168727
+ var blend: vec2f = fract(position);
168728
+ blend = blend * blend * (vec2f(3.0) - 2.0 * blend);
168729
+ let bottom: f32 = mix(cloudCoverageHash(cell), cloudCoverageHash(cell + vec2f(1.0, 0.0)), blend.x);
168730
+ let top: f32 = mix(cloudCoverageHash(cell + vec2f(0.0, 1.0)), cloudCoverageHash(cell + vec2f(1.0, 1.0)), blend.x);
168731
+ return mix(bottom, top, blend.y);
168732
+ }
168733
+ fn cloudCoverageAt(positionPS: vec3f) -> vec2f {
168734
+ var coverage: f32 = 0.9;
168735
+ var shapeFill: f32 = 0.0;
168736
+ if (uniforms.cloudCoverage.x != 0.0) {
168737
+ let coordinates: vec2f = (positionPS.xz + uniforms.cloudWind.zw * uniforms.cloudWindSpeed.x + uniforms.cloudCoverageOffset.xy) * uniforms.cloudCoverage.y;
168738
+ let coverageNoise: f32 = cloudCoverageNoise(coordinates) * 0.7 + cloudCoverageNoise(coordinates * 2.03 + vec2f(17.13, 43.71)) * 0.3;
168739
+ let threshold: f32 = mix(-0.15, 1.15, uniforms.cloudCoverage.z);
168740
+ let mask: f32 = smoothstep(threshold - 0.12, threshold + 0.12, coverageNoise);
168741
+ if (uniforms.cloudCoverage.x > 0.0) {
168742
+ coverage = coverage * mix(1.0, mask, uniforms.cloudCoverage.x);
168743
+ } else {
168744
+ shapeFill = clamp(-uniforms.cloudCoverage.x, 0.0, 1.0) * (1.0 - mask);
168745
+ coverage = mix(coverage, 1.0, shapeFill);
168746
+ }
168747
+ }
168748
+ return vec2f(coverage, shapeFill);
168749
+ }
168750
+ fn cloudPropertiesAt(samplePositionPS: vec3f, noiseMip: f32, erosionMip: f32, cheapVersion: bool, lightSampling: bool) -> CloudProperties {
168751
+ var properties: CloudProperties = CloudProperties(0.0, 0.0, 0.0, 0.0);
168752
+ var positionPS: vec3f = samplePositionPS;
168753
+ #ifndef CLOUD_LOCAL
168754
+ if (positionPS.y < uniforms.cloudPlanet.w) { return properties; }
168755
+ #endif
168756
+ properties.ambientOcclusion = 1.0;
168757
+ properties.height = cloudNormalizedHeight(positionPS);
168758
+ #ifndef CLOUD_LOCAL
168759
+ positionPS = vec3f(positionPS.x + uniforms.cloudEye.x, positionPS.y, positionPS.z + uniforms.cloudEye.z);
168760
+ #endif
168761
+ var baseCoordinates: vec3f = (cloudAnimateShape(positionPS).xzy / NOISE_NORMALIZATION) * uniforms.cloudShape.x - uniforms.cloudShapeOffset.xyz;
168762
+ baseCoordinates = baseCoordinates + properties.height * vec3f(uniforms.cloudWind.x, uniforms.cloudWind.y, 0.0) * uniforms.cloudShapeOffset.w;
168763
+ var lowFrequencyNoise: f32 = textureSampleLevel(worleyNoise, worleyNoiseSampler, baseCoordinates, noiseMip).r;
168764
+ let coverage: vec2f = cloudCoverageAt(positionPS);
168765
+ if (coverage.x <= CLOUD_DENSITY_THRESHOLD) { return properties; }
168766
+ let curves: vec3f = textureSampleLevel(cloudCurves, cloudCurvesSampler, vec2f(properties.height, 0.5), 0.0).xyz;
168767
+ let shapeFactor: f32 = mix(0.1, 1.0, uniforms.cloudShape.y * (1.0 - coverage.y)) * curves.y;
168768
+ let erosionFactor: f32 = uniforms.cloudShape.w * curves.y;
168769
+ let microDetailFactor: f32 = uniforms.cloudDetail.z * curves.y;
168770
+ lowFrequencyNoise = mix(1.0, lowFrequencyNoise, shapeFactor);
168771
+ var baseCloud: f32 = 1.0 - curves.x * coverage.x * (1.0 - shapeFactor);
168772
+ baseCloud = clamp(cloudRemap(lowFrequencyNoise, baseCloud, 1.0, 0.0, 1.0), 0.0, 1.0) * coverage.x * coverage.x;
168773
+ properties.sigmaT = 0.04;
168774
+ let occlusionBlend: f32 = clamp(1.0 - max(erosionFactor, shapeFactor) * 0.5, 0.0, 1.0);
168775
+ properties.ambientOcclusion = mix(1.0, curves.z, occlusionBlend);
168776
+ if (!cheapVersion) {
168777
+ let erosionCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudShape.z;
168778
+ var erosionValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, erosionCoordinates, erosionMip).r;
168779
+ erosionValue = mix(0.0, erosionValue, erosionFactor * 0.75 * coverage.x);
168780
+ properties.ambientOcclusion = clamp(properties.ambientOcclusion - sqrt(erosionValue * uniforms.cloudDetail.x), 0.0, 1.0);
168781
+ baseCloud = cloudRemap(baseCloud, erosionValue, 1.0, 0.0, 1.0);
168782
+ #ifdef CLOUD_MICRO_EROSION
168783
+ let fineCoordinates: vec3f = cloudAnimateErosion(positionPS) / NOISE_NORMALIZATION * uniforms.cloudDetail.y;
168784
+ var fineValue: f32 = 1.0 - textureSampleLevel(erosionNoise, erosionNoiseSampler, fineCoordinates, erosionMip).r;
168785
+ fineValue = mix(0.0, fineValue, microDetailFactor * 0.5 * coverage.x);
168786
+ baseCloud = cloudRemap(baseCloud, fineValue, 1.0, 0.0, 1.0);
168787
+ #endif
168788
+ }
168789
+ if (lightSampling) {
168790
+ baseCloud = baseCloud - erosionFactor * 0.1;
168791
+ #ifdef CLOUD_MICRO_EROSION
168792
+ baseCloud = baseCloud - microDetailFactor * 0.15;
168793
+ #endif
168794
+ }
168795
+ properties.density = max(0.0, baseCloud) * uniforms.cloudDetail.w;
168796
+ return properties;
168797
+ }
168798
+ `;
168799
+ }
168800
+ static WgslLighting() {
168801
+ return `fn cloudPhase(anisotropy: f32, cosTheta: f32) -> f32 {
168802
+ let denominator: f32 = 1.0 + anisotropy * anisotropy - 2.0 * anisotropy * cosTheta;
168803
+ let falloff: f32 = inverseSqrt(max(denominator, 1e-6));
168804
+ return (1.0 / (4.0 * CLOUD_PI)) * (1.0 - anisotropy * anisotropy) * falloff * falloff * falloff;
168805
+ }
168806
+ fn cloudPowder(density: f32, cosAngle: f32, intensity: f32) -> f32 {
168807
+ let powder: f32 = clamp((1.0 - exp(-density * 4.0)) * 2.0, 0.0, 1.0);
168808
+ return mix(1.0, mix(1.0, powder, smoothstep(0.5, -0.5, cosAngle)), intensity);
168809
+ }
168810
+ fn cloudSunTransmittance(positionPS: vec3f, sunDirection: vec3f, phase: vec2f) -> vec3f {
168811
+ let radialDistance: f32 = length(positionPS);
168812
+ let cosChi: f32 = dot(positionPS, sunDirection) / radialDistance;
168813
+ var transmittance: vec3f = vec3f(0.0);
168814
+ if (cosChi < cloudHorizonCosine(radialDistance)) { return transmittance; }
168815
+ let lightSteps: f32 = uniforms.cloudSteps.y;
168816
+ let totalLightDistance: f32 = clamp(cloudIntersectSphere(uniforms.cloudLayer.y, cosChi, radialDistance).y, 0.0, lightSteps * LIGHT_STEP_MAXIMAL_SIZE) + 5.0;
168817
+ let intervalSize: f32 = totalLightDistance / lightSteps;
168818
+ var opticalDepth: f32 = 0.0;
168819
+ for (var j: i32 = 0; j < MAX_LIGHT_STEPS; j++) {
168820
+ if (f32(j) >= lightSteps) { break; }
168821
+ let distanceAlongLight: f32 = intervalSize * (0.25 + f32(j));
168822
+ let lightProperties: CloudProperties = cloudPropertiesAt(positionPS + sunDirection * distanceAlongLight, 3.0 * f32(j) / lightSteps, 0.0, true, true);
168823
+ opticalDepth = opticalDepth + lightProperties.density * lightProperties.sigmaT;
168824
+ }
168825
+ let extinction: vec3f = intervalSize * opticalDepth * uniforms.cloudTint.rgb;
168826
+ transmittance = transmittance + exp(-extinction) * phase.x;
168827
+ transmittance = transmittance + exp(-extinction * uniforms.cloudScattering.x) * (phase.y * uniforms.cloudScattering.x);
168828
+ return transmittance;
168829
+ }
168830
+ `;
168831
+ }
168832
+ static WgslAerial() {
168833
+ const skyShaders = HdrpPbSkyShaders;
168834
+ const fogPass = HdrpFogPass;
168835
+ if (skyShaders == null || fogPass == null)
168836
+ return "";
168837
+ return "#ifdef CLOUD_AERIAL\nvar apLutSampler: sampler;\nvar apLut: texture_2d_array<f32>;\nuniform cloudApCamera: vec4f;\n"
168838
+ + skyShaders.WgslUniforms() + skyShaders.WgslCommon() + fogPass.AerialPerspectiveWgsl() + "#endif\n";
168839
+ }
168840
+ static WgslMarch() {
168841
+ const shaders = VolumetricCloudsShaders;
168842
+ 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"
168843
+ + shaders.WgslMarchUniforms() + shaders.WgslConstants() + shaders.WgslGeometry() + shaders.WgslDensity() + shaders.WgslLighting() + shaders.WgslAerial() + `
168844
+ fn cloudJenkins(value: u32) -> u32 {
168845
+ var x: u32 = value;
168846
+ x = x + (x << 10u);
168847
+ x = x ^ (x >> 6u);
168848
+ x = x + (x << 3u);
168849
+ x = x ^ (x >> 11u);
168850
+ x = x + (x << 15u);
168851
+ return x;
168852
+ }
168853
+ fn cloudRandom(pixel: vec2f, seed: f32) -> f32 {
168854
+ let v: vec3u = vec3u(u32(pixel.x), u32(pixel.y), u32(seed));
168855
+ let hash: u32 = cloudJenkins(v.x ^ cloudJenkins(v.y ^ cloudJenkins(v.z)));
168856
+ return bitcast<f32>((hash & 0x007FFFFFu) | 0x3F800000u) - 1.0;
168857
+ }
168858
+ @fragment
168859
+ fn main(input: FragmentInputs) -> FragmentOutputs {
168860
+ fragmentOutputs.color = vec4f(0.0, 0.0, 0.0, 1.0);
168861
+ let uv: vec2f = fragmentInputs.vUV;
168862
+ let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
168863
+ let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
168864
+ var maxRayLength: f32 = MAX_SKYBOX_DISTANCE;
168865
+ let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
168866
+ if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
168867
+ #ifdef CLOUD_LOCAL
168868
+ maxRayLength = viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001);
168869
+ #else
168870
+ return fragmentOutputs;
168871
+ #endif
168872
+ }
168873
+ #ifdef CLOUD_LOCAL
168874
+ let originWS: vec3f = uniforms.cloudEye.xyz;
168875
+ #else
168876
+ let originWS: vec3f = vec3f(0.0);
168877
+ #endif
168878
+ let marchRange: CloudRange = cloudRange(originWS, direction);
168879
+ if (!marchRange.hit || maxRayLength < marchRange.entryDistance) { return fragmentOutputs; }
168880
+ let rangeStart: f32 = marchRange.entryDistance;
168881
+ let integrationNoise: f32 = cloudRandom(fragmentInputs.position.xy, uniforms.cloudCamera.z);
168882
+ let primarySteps: f32 = uniforms.cloudLayer.w;
168883
+ var totalDistance: f32 = min(marchRange.exitDistance, maxRayLength) - rangeStart;
168884
+ var stepSize: f32 = min(totalDistance / primarySteps, uniforms.cloudLayer.z);
168885
+ totalDistance = stepSize * primarySteps;
168886
+ let layerThickness: f32 = max(1.0, uniforms.cloudLayer.y - uniforms.cloudLayer.x);
168887
+ let sampleMultiplier: f32 = min(max(1.0, totalDistance / layerThickness), max(1.0, uniforms.cloudSteps.x));
168888
+ let primaryStepCount: i32 = min(i32(ceil(primarySteps * sampleMultiplier)), MAX_PRIMARY_STEPS);
168889
+ stepSize = totalDistance / f32(max(1, primaryStepCount));
168890
+ let sunDirection: vec3f = uniforms.cloudSunDirection.xyz;
168891
+ let cosAngle: f32 = dot(direction, sunDirection);
168892
+ let phase: vec2f = vec2f(
168893
+ cloudPhase(FORWARD_ECCENTRICITY, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY, cosAngle),
168894
+ cloudPhase(FORWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle) + cloudPhase(-BACKWARD_ECCENTRICITY * uniforms.cloudScattering.x, cosAngle));
168895
+ var scattering: vec3f = vec3f(0.0);
168896
+ var ambient: f32 = 0.0;
168897
+ var transmittance: f32 = 1.0;
168898
+ var meanDistance: f32 = 0.0;
168899
+ var meanDistanceDivider: f32 = 0.0;
168900
+ var currentDistance: f32 = integrationNoise;
168901
+ var currentPositionWS: vec3f = originWS + (rangeStart + currentDistance) * direction;
168902
+ var activeSampling: bool = true;
168903
+ var sequentialEmptySamples: i32 = 0;
168904
+ var currentIndex: i32 = 0;
168905
+ for (var iteration: i32 = 0; iteration < MAX_PRIMARY_STEPS * 2; iteration++) {
168906
+ if (currentIndex >= primaryStepCount || currentDistance >= totalDistance) { break; }
168907
+ let cameraDistance: f32 = rangeStart + currentDistance;
168908
+ let densityFade: f32 = clamp((cameraDistance - uniforms.cloudSteps.z) / (uniforms.cloudSteps.z + uniforms.cloudSteps.w), 0.0, 1.0);
168909
+ let erosionMip: f32 = mix(0.0, 4.0, clamp((cameraDistance - MIN_EROSION_DISTANCE) / (MAX_EROSION_DISTANCE - MIN_EROSION_DISTANCE), 0.0, 1.0));
168910
+ let currentPositionPS: vec3f = currentPositionWS - uniforms.cloudPlanet.xyz;
168911
+ if (activeSampling) {
168912
+ var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 0.0, erosionMip, false, false);
168913
+ properties.density = properties.density * densityFade;
168914
+ if (properties.density > CLOUD_DENSITY_THRESHOLD) {
168915
+ let transmittanceDensity: f32 = transmittance * properties.density;
168916
+ meanDistance = meanDistance + cameraDistance * transmittanceDensity;
168917
+ meanDistanceDivider = meanDistanceDivider + transmittanceDensity;
168918
+ let stepTransmittance: f32 = exp(-properties.density * properties.sigmaT * stepSize);
168919
+ let powder: f32 = cloudPowder(properties.density, cosAngle, uniforms.cloudScattering.y);
168920
+ let sunLuminance: vec3f = cloudSunTransmittance(currentPositionPS, sunDirection, phase) * powder;
168921
+ let absorbed: f32 = transmittance - transmittance * stepTransmittance;
168922
+ scattering = scattering + sunLuminance * absorbed;
168923
+ ambient = ambient + properties.ambientOcclusion * absorbed;
168924
+ transmittance = transmittance * stepTransmittance;
168925
+ if (transmittance < 0.003) {
168926
+ transmittance = 0.0;
168927
+ break;
168928
+ }
168929
+ sequentialEmptySamples = 0;
168930
+ } else {
168931
+ sequentialEmptySamples = sequentialEmptySamples + 1;
168932
+ }
168933
+ if (sequentialEmptySamples == EMPTY_STEPS_BEFORE_LARGE_STEPS) { activeSampling = false; }
168934
+ let relativeStepSize: f32 = mix(integrationNoise, 1.0, clamp(f32(currentIndex), 0.0, 1.0));
168935
+ currentPositionWS = currentPositionWS + direction * stepSize * relativeStepSize;
168936
+ currentDistance = currentDistance + stepSize * relativeStepSize;
168937
+ } else {
168938
+ var properties: CloudProperties = cloudPropertiesAt(currentPositionPS, 1.0, 0.0, true, false);
168939
+ properties.density = properties.density * densityFade;
168940
+ if (properties.density < CLOUD_DENSITY_THRESHOLD) {
168941
+ currentPositionWS = currentPositionWS + direction * stepSize * 2.0;
168942
+ currentDistance = currentDistance + stepSize * 2.0;
168943
+ } else {
168944
+ currentPositionWS = currentPositionWS - direction * stepSize;
168945
+ currentDistance = currentDistance - stepSize;
168946
+ currentIndex = currentIndex - 1;
168947
+ activeSampling = true;
168948
+ sequentialEmptySamples = 0;
168949
+ }
168950
+ }
168951
+ currentIndex = currentIndex + 1;
168952
+ }
168953
+ if (meanDistance == 0.0) { return fragmentOutputs; }
168954
+ meanDistance = min(meanDistance / meanDistanceDivider, maxRayLength);
168955
+ let relativeHeight: f32 = cloudNormalizedHeight(originWS - uniforms.cloudPlanet.xyz + meanDistance * direction);
168956
+ let ambientColor: vec3f = max(vec3f(0.0), mix(uniforms.cloudAmbientBottom.rgb, uniforms.cloudAmbientTop.rgb, relativeHeight));
168957
+ var color: vec3f = (uniforms.cloudSunColor.rgb * scattering + ambientColor * ambient) * uniforms.cloudCamera.w;
168958
+ #ifdef CLOUD_AERIAL
168959
+ if (uniforms.cloudApCamera.w > 0.5) {
168960
+ let apDirection: vec3f = vec3f(direction.x * uniforms.cloudForward.w, direction.y, direction.z);
168961
+ let aerial: FogAerial = fogAerialPerspective(uv, uniforms.cloudApCamera.xyz, apDirection, meanDistance);
168962
+ color = aerial.color * (1.0 - transmittance) + (vec3f(1.0) - aerial.opacity) * color;
168963
+ }
168964
+ #endif
168965
+ fragmentOutputs.color = vec4f(color, transmittance);
168966
+ }
168967
+ `;
168968
+ }
168969
+ static WgslCompositeVertex() {
168970
+ return `attribute position: vec3f;
168971
+ varying vUV: vec2f;
168972
+ @vertex
168973
+ fn main(input: VertexInputs) -> FragmentInputs {
168974
+ vertexOutputs.vUV = vertexInputs.position.xy * 0.5 + vec2f(0.5);
168975
+ vertexOutputs.position = vec4f(vertexInputs.position.xy, 0.0, 1.0);
168976
+ }
168977
+ `;
168978
+ }
168979
+ static WgslCompositeFragment() {
168980
+ return `varying vUV: vec2f;
168981
+ var cloudLightingSampler: sampler;
168982
+ var cloudLighting: texture_2d<f32>;
168983
+ uniform cloudDisplayEncoded: f32;
168984
+ const CLOUD_DISPLAY_GAMMA: f32 = 2.2;
168985
+ const CLOUD_LUMINANCE: vec3f = vec3f(0.2126, 0.7152, 0.0722);
168986
+ @fragment
168987
+ fn main(input: FragmentInputs) -> FragmentOutputs {
168988
+ let cloud: vec4f = textureSample(cloudLighting, cloudLightingSampler, fragmentInputs.vUV);
168989
+ var scattering: vec3f = cloud.rgb;
168990
+ var throughput: f32 = cloud.a;
168991
+ if (uniforms.cloudDisplayEncoded > 0.5) {
168992
+ let encodeExponent: f32 = 1.0 / CLOUD_DISPLAY_GAMMA;
168993
+ let scatteringLuminance: f32 = clamp(dot(scattering, CLOUD_LUMINANCE), 0.0, 1.0);
168994
+ throughput = pow(min(cloud.a + scatteringLuminance, 1.0), encodeExponent) - pow(scatteringLuminance, encodeExponent);
168995
+ scattering = pow(clamp(scattering, vec3f(0.0), vec3f(1.0)), vec3f(encodeExponent));
168996
+ }
168997
+ fragmentOutputs.color = vec4f(scattering, 1.0 - throughput);
168998
+ }
168999
+ `;
169000
+ }
169001
+ static WgslTemporal() {
169002
+ const shaders = VolumetricCloudsShaders;
169003
+ 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"
169004
+ + shaders.TemporalUniforms.map((name) => (name.indexOf("ViewProj") >= 0) ? "uniform " + name + ": mat4x4f;" : "uniform " + name + ": vec4f;").join("\n") + "\n"
169005
+ + shaders.WgslGeometry() + `
169006
+ fn cloudReprojectionDistance(direction: vec3f, viewDepth: f32) -> f32 {
169007
+ #ifdef CLOUD_LOCAL
169008
+ let layerRange: CloudRange = cloudIntersectVolume(uniforms.cloudEye.xyz - uniforms.cloudPlanet.xyz, direction);
169009
+ var distanceToCloud: f32 = select(layerRange.exitDistance, layerRange.entryDistance, layerRange.hit);
169010
+ #else
169011
+ let layerRange: CloudRange = cloudRange(vec3f(0.0), direction);
169012
+ var distanceToCloud: f32 = layerRange.entryDistance;
169013
+ #endif
169014
+ distanceToCloud = max(distanceToCloud, 1.0);
169015
+ if (uniforms.cloudCamera.y > 0.5 && viewDepth < uniforms.cloudCamera.x * 0.999) {
169016
+ distanceToCloud = min(distanceToCloud, viewDepth / max(dot(direction, uniforms.cloudForward.xyz), 0.0001));
169017
+ }
169018
+ return distanceToCloud;
169019
+ }
169020
+ @fragment
169021
+ fn main(input: FragmentInputs) -> FragmentOutputs {
169022
+ let uv: vec2f = fragmentInputs.vUV;
169023
+ let current: vec4f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv, 0.0);
169024
+ let viewDepth: f32 = textureSampleLevel(sceneDepth, sceneDepthSampler, uv, 0.0).r;
169025
+ var north: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, -uniforms.cloudTexel.y), 0.0).rgb;
169026
+ var west: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(-uniforms.cloudTexel.x, 0.0), 0.0).rgb;
169027
+ var east: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(uniforms.cloudTexel.x, 0.0), 0.0).rgb;
169028
+ var south: vec3f = textureSampleLevel(cloudCurrent, cloudCurrentSampler, uv + vec2f(0.0, uniforms.cloudTexel.y), 0.0).rgb;
169029
+ fragmentOutputs.color = current;
169030
+ if (uniforms.cloudCamera.z < 0.5 || current.a >= 1.0) { return fragmentOutputs; }
169031
+ let boxMin: vec3f = min(min(min(current.rgb, north), min(west, east)), south);
169032
+ let boxMax: vec3f = max(max(max(current.rgb, north), max(west, east)), south);
169033
+ let farPoint: vec4f = uniforms.cloudInvViewProj * vec4f(uv * 2.0 - vec2f(1.0), 1.0, 1.0);
169034
+ let direction: vec3f = normalize(farPoint.xyz / farPoint.w - uniforms.cloudEye.xyz);
169035
+ let worldPosition: vec3f = uniforms.cloudEye.xyz + direction * cloudReprojectionDistance(direction, viewDepth);
169036
+ let previousClip: vec4f = uniforms.cloudPrevViewProj * vec4f(worldPosition, 1.0);
169037
+ let previousUV: vec2f = previousClip.xy / previousClip.w * 0.5 + vec2f(0.5);
169038
+ let velocity: vec2f = previousUV - uv;
169039
+ let history: vec3f = textureSampleLevel(cloudHistory, cloudHistorySampler, clamp(previousUV, vec2f(0.0), vec2f(1.0)), 0.0).rgb;
169040
+ if (previousUV.x > 1.0 || previousUV.x < 0.0 || previousUV.y > 1.0 || previousUV.y < 0.0) { return fragmentOutputs; }
169041
+ let previous: vec3f = clamp(history, boxMin, boxMax);
169042
+ let accumulation: f32 = uniforms.cloudCamera.w;
169043
+ let intensity: f32 = clamp(min(accumulation - abs(velocity.x) * accumulation, accumulation - abs(velocity.y) * accumulation), 0.0, 1.0);
169044
+ fragmentOutputs.color = vec4f(mix(current.rgb, previous, intensity), current.a);
169045
+ }
169046
+ `;
169047
+ }
169048
+ }
169049
+ VolumetricCloudsShaders.MarchName = "tkCloudMarch";
169050
+ VolumetricCloudsShaders.CompositeName = "tkCloudComposite";
169051
+ VolumetricCloudsShaders.TemporalName = "tkCloudTemporal";
169052
+ VolumetricCloudsShaders.TemporalUniforms = ["cloudInvViewProj", "cloudPrevViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudTexel"];
169053
+ VolumetricCloudsShaders.TemporalSamplers = ["cloudCurrent", "cloudHistory", "sceneDepth"];
169054
+ VolumetricCloudsShaders.MarchUniforms = [
169055
+ "cloudInvViewProj", "cloudEye", "cloudForward", "cloudCamera", "cloudPlanet", "cloudLayer", "cloudSteps", "cloudShape",
169056
+ "cloudDetail", "cloudShapeOffset", "cloudWind", "cloudWindSpeed", "cloudCoverage", "cloudCoverageOffset", "cloudScattering",
169057
+ "cloudTint", "cloudSunDirection", "cloudSunColor", "cloudAmbientTop", "cloudAmbientBottom"
169058
+ ];
169059
+ VolumetricCloudsShaders.MarchSamplers = ["worleyNoise", "erosionNoise", "cloudCurves", "sceneDepth"];
169060
+ VolumetricCloudsShaders.AerialSampler = "apLut";
169061
+ VolumetricCloudsShaders.AerialCameraUniform = "cloudApCamera";
169062
+ VolumetricCloudsShaders.registered = false;
166557
169063
  export class WebVideoPlayer extends ScriptComponent {
166558
169064
  getVideoMaterial() { return this.m_videoMaterial; }
166559
169065
  getVideoTexture() { return this.m_videoTexture; }
@@ -166908,6 +169414,7 @@ SceneManager.RegisterClass("TOOLKIT.HdrpWaterDecalSurface", HdrpWaterDecalSurfac
166908
169414
  SceneManager.RegisterClass("TOOLKIT.LensFlareComponentSRP", LensFlareComponentSRP);
166909
169415
  SceneManager.RegisterClass("TOOLKIT.LegacyLensFlare", LegacyLensFlare);
166910
169416
  SceneManager.RegisterClass("TOOLKIT.LightProbeNetwork", LightProbeNetwork);
169417
+ SceneManager.RegisterClass("TOOLKIT.MeshWaterSurface", MeshWaterSurface);
166911
169418
  SceneManager.RegisterClass("TOOLKIT.NavigationAgent", NavigationAgent);
166912
169419
  Object.defineProperty(PlayerRef, "None", { value: new PlayerRef(0), writable: false });
166913
169420
  Object.defineProperty(PlayerRef, "MasterClient", { value: new PlayerRef(-1), writable: false });
@@ -166939,11 +169446,14 @@ SceneManager.RegisterClass("TOOLKIT.ShurikenParticles", ShurikenParticles);
166939
169446
  SceneManager.RegisterClass("TOOLKIT.SkinTextureArray", SkinTextureArray);
166940
169447
  SceneManager.RegisterClass(SplineAnimate.ClassName, SplineAnimate);
166941
169448
  SceneManager.RegisterClass(SplineContainer.ClassName, SplineContainer);
169449
+ const TERRAIN_EDGE_TOLERANCE_METERS = 0.01;
166942
169450
  SceneManager.RegisterClass("TOOLKIT.TerrainBuilder", TerrainBuilder);
166943
169451
  SceneManager.RegisterClass("TOOLKIT.TreeBranchMaterial", TreeBranchMaterial);
166944
169452
  SceneManager.RegisterClass("TOOLKIT.GrassStandardMaterial", GrassStandardMaterial);
166945
169453
  SceneManager.RegisterClass("TOOLKIT.GrassBillboardMaterial", GrassBillboardMaterial);
166946
169454
  SceneManager.RegisterClass("TOOLKIT.TerrainGrassPlugin", TerrainGrassPlugin);
169455
+ const TREE_INITIAL_CAPACITY = 64;
169456
+ const TREE_GROWTH_FACTOR = 1.5;
166947
169457
  SceneManager.RegisterClass("TOOLKIT.TextureAtlasSkin", TextureAtlasSkin);
166948
169458
  SceneManager.RegisterClass(ThirdPersonController.ClassName, ThirdPersonController);
166949
169459
  SceneManager.RegisterClass("BABYLON.GUI.UnitySlider", UnitySlider);
@@ -166953,4 +169463,36 @@ SceneManager.RegisterClass("TOOLKIT.UserInterface", UserInterface);
166953
169463
  SceneManager.RegisterClass("TOOLKIT.VisualEffect", VisualEffect);
166954
169464
  const VFX_EPSILON = 1e-5;
166955
169465
  const VFX_FLT_MIN = 1.175494351e-38;
169466
+ const CLOUD_ALPHA_INDEX = -1000000;
169467
+ const CLOUD_TIER_WINDOW_FRAMES = 120;
169468
+ const CLOUD_TIER_BUDGET_MS = 33;
169469
+ const CLOUD_TIER_MINIMUM_SAVING = 0.1;
169470
+ const CLOUD_MAX_STEP_STRETCH = 4;
169471
+ const CLOUD_CAMERA_CUT_METERS = 50;
169472
+ const CLOUD_CURVE_SAMPLES = 64;
169473
+ const CLOUD_EARTH_RADIUS_METERS = 6378100;
169474
+ const CLOUD_MINIMUM_CURVATURE_SCALE = 0.025;
169475
+ const CLOUD_WIND_KMH_TO_MS = -0.277778;
169476
+ const CLOUD_WIND_WRAP_METERS = 100000;
169477
+ const CLOUD_MAX_STEP_DIVISOR = 8;
169478
+ const CLOUD_PHONE_WIDTH_CSS_PX = 800;
169479
+ const CLOUD_WEAK_GPU_TEXTURE_SIZE = 8192;
169480
+ const CLOUD_READY_POLL_MS = 16;
169481
+ const CLOUD_MINIMUM_ACCUMULATION = 0.01;
169482
+ const CLOUD_AUTO_FADE_START_FRACTION = 0.2;
169483
+ const CLOUD_AUTO_FADE_DISTANCE_FRACTION = 0.3;
169484
+ const CLOUD_FADE_MODE_MANUAL = 1;
169485
+ const CLOUD_DENSITY_SCALE = 2;
169486
+ const CLOUD_ALTITUDE_DISTORTION_SCALE = 0.25;
169487
+ const CLOUD_MULTI_SCATTERING_SCALE = 0.95;
169488
+ const CLOUD_SCATTERING_TINT_SCALE = 0.75;
169489
+ const CLOUD_MINIMUM_COVERAGE_PATTERN_METERS = 100;
169490
+ const CLOUD_DEFAULT_COVERAGE_PATTERN_METERS = 10000;
169491
+ const CLOUD_DEFAULT_COVERAGE_GAP = 0.45;
169492
+ const CLOUD_DEFAULT_SETTINGS = { resolutionScale: 0.5, primarySteps: 32, longRayMultiplier: 1, lightSteps: 2 };
169493
+ const CLOUD_TIER_CAPS = [
169494
+ { resolutionScale: Infinity, primarySteps: 64, longRayMultiplier: 2, lightSteps: Infinity },
169495
+ { resolutionScale: 0.35, primarySteps: 64, longRayMultiplier: 1, lightSteps: 2 },
169496
+ { resolutionScale: 0.25, primarySteps: 64, longRayMultiplier: 1, lightSteps: 1 }
169497
+ ];
166956
169498
  SceneManager.RegisterClass("TOOLKIT.WebVideoPlayer", WebVideoPlayer);