@babylonjs-toolkit/next 9.0.1 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -75,13 +75,11 @@ import { GPUParticleSystem } from '@babylonjs/core/Particles/gpuParticleSystem';
75
75
  import { AdvancedDynamicTexture } from '@babylonjs/gui/2D/advancedDynamicTexture';
76
76
  import { InputText } from '@babylonjs/gui/2D/controls/inputText';
77
77
  import { VideoTexture } from '@babylonjs/core/Materials/Textures/videoTexture';
78
- import { UniversalTerrainMaterial, UniversalTerrainMaterialPlugin } from './universalterrainmaterial';
79
- import { TreeBranchMaterial, TreeBranchMaterialPlugin } from './treebranchmaterial';
80
78
  import { UnitySlider } from './unityslider';
81
79
  import { UnityScrollBar } from './unityscrollbar';
82
80
  import { UnityDropdownMenu } from './unitydropdownmenu';
83
81
  export class SceneManager {
84
- static get Version() { return "9.0.1 - R1"; }
82
+ static get Version() { return "9.1.0 - R1"; }
85
83
  static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
86
84
  static GetEngine(scene) {
87
85
  let result = null;
@@ -9263,6 +9261,643 @@ Utilities.LoadingState = -1;
9263
9261
  Utilities.OnPreloaderProgress = null;
9264
9262
  Utilities.OnPreloaderComplete = null;
9265
9263
  Utilities.LoaderItemsMarkedForDisposal = [];
9264
+ export class UniversalTerrainMaterial extends CustomShaderMaterial {
9265
+ constructor(name, scene) {
9266
+ super(name, scene);
9267
+ this.terrainInfo = null;
9268
+ this.shader = this.getShaderName();
9269
+ this.plugin = new UniversalTerrainMaterialPlugin(this, this.shader);
9270
+ }
9271
+ awake() {
9272
+ }
9273
+ update() {
9274
+ }
9275
+ getShaderName() {
9276
+ return "UniversalTerrainMaterial";
9277
+ }
9278
+ getTerrainInfo() {
9279
+ return this.terrainInfo;
9280
+ }
9281
+ }
9282
+ export class UniversalTerrainMaterialPlugin extends CustomShaderMaterialPlugin {
9283
+ constructor(customMaterial, shaderName) {
9284
+ super(customMaterial, shaderName, 100, { UNIVERSALTERRAINMATERIAL: false }, true, true, false);
9285
+ this.colorName = "surfaceAlbedo";
9286
+ this.splatmapSampler = "splatmapSampler";
9287
+ this.detailsSampler = "detailsSampler";
9288
+ this.normalsSampler = "normalsSampler";
9289
+ this.GLSL_CustomFragment = null;
9290
+ this.GLSL_CustomVertex = null;
9291
+ this.GLSL_VertexMainEnd = null;
9292
+ this.GLSL_FragmentUpdateColor = null;
9293
+ this.WGSL_CustomFragment = null;
9294
+ this.WGSL_CustomVertex = null;
9295
+ this.WGSL_VertexMainEnd = null;
9296
+ this.WGSL_FragmentUpdateColor = null;
9297
+ }
9298
+ isCompatible(shaderLanguage) {
9299
+ return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
9300
+ }
9301
+ getClassName() {
9302
+ return "UniversalTerrainMaterialPlugin";
9303
+ }
9304
+ getCustomCode(shaderType, shaderLanguage) {
9305
+ const terrainInfo = this.getCustomShaderMaterial().getTerrainInfo();
9306
+ if (shaderType === "vertex") {
9307
+ if (shaderLanguage === ShaderLanguage.WGSL) {
9308
+ if (this.WGSL_CustomVertex == null)
9309
+ this.WGSL_CustomVertex = this.WGSL_FormatTerrainVertexDefintions(terrainInfo);
9310
+ if (this.WGSL_VertexMainEnd == null)
9311
+ this.WGSL_VertexMainEnd = this.WGSL_FormatTerrainVertexMainEnd(terrainInfo);
9312
+ return {
9313
+ CUSTOM_VERTEX_DEFINITIONS: this.WGSL_CustomVertex,
9314
+ CUSTOM_VERTEX_MAIN_END: this.WGSL_VertexMainEnd,
9315
+ };
9316
+ }
9317
+ else if (shaderLanguage === ShaderLanguage.GLSL) {
9318
+ if (this.GLSL_CustomVertex == null)
9319
+ this.GLSL_CustomVertex = this.GLSL_FormatTerrainVertexDefintions(terrainInfo);
9320
+ if (this.GLSL_VertexMainEnd == null)
9321
+ this.GLSL_VertexMainEnd = this.GLSL_FormatTerrainVertexMainEnd(terrainInfo);
9322
+ return {
9323
+ CUSTOM_VERTEX_DEFINITIONS: this.GLSL_CustomVertex,
9324
+ CUSTOM_VERTEX_MAIN_END: this.GLSL_VertexMainEnd,
9325
+ };
9326
+ }
9327
+ }
9328
+ else if (shaderType === "fragment") {
9329
+ if (shaderLanguage === ShaderLanguage.WGSL) {
9330
+ if (this.WGSL_CustomFragment == null)
9331
+ this.WGSL_CustomFragment = this.WGSL_FormatTerrainFragmentDefintions(terrainInfo, this.splatmapSampler, this.detailsSampler, this.normalsSampler);
9332
+ if (this.WGSL_FragmentUpdateColor == null)
9333
+ this.WGSL_FragmentUpdateColor = this.WGSL_FormatTerrainFragmentUpdateColor(terrainInfo, this.colorName, this.splatmapSampler, this.detailsSampler, this.normalsSampler, SceneManager.TerrainColorCorrection);
9334
+ return {
9335
+ CUSTOM_FRAGMENT_DEFINITIONS: this.WGSL_CustomFragment,
9336
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.WGSL_FragmentUpdateColor,
9337
+ };
9338
+ }
9339
+ else if (shaderLanguage === ShaderLanguage.GLSL) {
9340
+ if (this.GLSL_CustomFragment == null)
9341
+ this.GLSL_CustomFragment = this.GLSL_FormatTerrainFragmentDefintions(terrainInfo, this.splatmapSampler, this.detailsSampler, this.normalsSampler);
9342
+ if (this.GLSL_FragmentUpdateColor == null)
9343
+ this.GLSL_FragmentUpdateColor = this.GLSL_FormatTerrainFragmentUpdateColor(terrainInfo, this.colorName, this.splatmapSampler, this.detailsSampler, this.normalsSampler, SceneManager.TerrainColorCorrection);
9344
+ return {
9345
+ CUSTOM_FRAGMENT_DEFINITIONS: this.GLSL_CustomFragment,
9346
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.GLSL_FragmentUpdateColor,
9347
+ };
9348
+ }
9349
+ }
9350
+ return null;
9351
+ }
9352
+ getUniforms(shaderLanguage) {
9353
+ const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
9354
+ this.vertexDefinitions = this.getCustomShaderMaterial().getCustomVertexCode(wgsl);
9355
+ this.fragmentDefinitions = (wgsl === true) ? this.getCustomShaderMaterial().getCustomFragmentCode(wgsl) : null;
9356
+ return this.getCustomShaderMaterial().getCustomUniforms(wgsl);
9357
+ }
9358
+ getSamplers(samplers) {
9359
+ const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
9360
+ if (customSamplers != null && customSamplers.length > 0)
9361
+ samplers.push(...customSamplers);
9362
+ }
9363
+ getAttributes(attributes, scene, mesh) {
9364
+ const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
9365
+ if (customAttributes != null && customAttributes.length > 0)
9366
+ attributes.push(...customAttributes);
9367
+ }
9368
+ prepareDefines(defines, scene, mesh) {
9369
+ if (!this.getIsEnabled())
9370
+ return;
9371
+ this.getCustomShaderMaterial().prepareCustomDefines(defines);
9372
+ }
9373
+ bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
9374
+ if (!this.getIsEnabled())
9375
+ return;
9376
+ this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
9377
+ }
9378
+ WGSL_FormatTerrainVertexDefintions(terrainInfo) {
9379
+ let result = "";
9380
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9381
+ result = ("\r\n#ifndef TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
9382
+ + "#define TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
9383
+ + "varying vSplatmapUV: vec2<f32>;\r\n"
9384
+ + "\r\n"
9385
+ + "#endif\r\n\r\n");
9386
+ }
9387
+ return result;
9388
+ }
9389
+ WGSL_FormatTerrainVertexMainEnd(terrainInfo) {
9390
+ let result = "";
9391
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9392
+ result = ("\r\n#ifndef TERRAIN_VERTEX_MAIN_END\r\n\r\n"
9393
+ + "#define TERRAIN_VERTEX_MAIN_END\r\n\r\n"
9394
+ + "#ifdef UV1\r\n"
9395
+ + "vertexOutputs.vSplatmapUV = uvUpdated;\r\n"
9396
+ + "#endif\r\n"
9397
+ + "\r\n"
9398
+ + "#endif\r\n\r\n");
9399
+ }
9400
+ return result;
9401
+ }
9402
+ WGSL_FormatTerrainFragmentDefintions(terrainInfo, splatmapSampler, detailsSampler, normalsSampler) {
9403
+ let result = "";
9404
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9405
+ result = ("\r\n#ifndef TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
9406
+ + "#define TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
9407
+ + "varying vSplatmapUV: vec2<f32>;\r\n"
9408
+ + "\r\n"
9409
+ + "fn srgb_to_linear(c: vec3<f32>) -> vec3<f32>\r\n"
9410
+ + "{\r\n"
9411
+ + " return pow(c, vec3<f32>(2.2));\r\n"
9412
+ + "}\r\n"
9413
+ + "\r\n"
9414
+ + "fn linear_to_srgb(c: vec3<f32>) -> vec3<f32>\r\n"
9415
+ + "{\r\n"
9416
+ + " return pow(c, vec3<f32>(1.0 / 2.2));\r\n"
9417
+ + "}\r\n"
9418
+ + "\r\n"
9419
+ + "fn calculateMipmapLevel(uvs: vec2<f32>, size: vec2<f32>) -> f32\r\n"
9420
+ + "{\r\n"
9421
+ + " let dx: vec2<f32> = dpdx(uvs * size.x);\r\n"
9422
+ + " let dy: vec2<f32> = dpdy(uvs * size.y);\r\n"
9423
+ + " let d: f32 = max(dot(dx, dx), dot(dy, dy));\r\n"
9424
+ + " return (0.4 * log2(d));\r\n"
9425
+ + "}\r\n"
9426
+ + "\r\n"
9427
+ + "fn sampleTextureAtlas2D(atlas: texture_2d<f32>, sampler: sampler, gamma: f32, tile: vec2<f32>, rect: vec4<f32>, uvs: vec2<f32>, lod: f32) -> vec4<f32>\r\n"
9428
+ + "{\r\n"
9429
+ + " var level: f32 = lod;\r\n"
9430
+ + " if (level < 0.0) {\r\n"
9431
+ + " level = clamp(calculateMipmapLevel(uvs, vec2(tile.x, tile.x)), 0.0, tile.y);\r\n"
9432
+ + " }\r\n"
9433
+ + " let size: f32 = pow(2.0, tile.y - level);\r\n"
9434
+ + " let sizex: f32 = size / rect.z;\r\n"
9435
+ + " let sizey: f32 = size / rect.w;\r\n"
9436
+ + " var uv: vec2<f32> = fract(uvs);\r\n"
9437
+ + " uv.x = uv.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x;\r\n"
9438
+ + " uv.y = uv.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y;\r\n"
9439
+ + " var color: vec4<f32> = textureSampleLevel(atlas, sampler, uv, level);\r\n"
9440
+ + " if (gamma != 1.0) {\r\n"
9441
+ + " color.r = pow(color.r, gamma);\r\n"
9442
+ + " color.g = pow(color.g, gamma);\r\n"
9443
+ + " color.b = pow(color.b, gamma);\r\n"
9444
+ + " }\r\n"
9445
+ + " return color;\r\n"
9446
+ + "}\r\n"
9447
+ + "\r\n"
9448
+ + "fn sampleSplatmapAtlas2D(atlas: texture_2d<f32>, sampler: sampler, tile: vec2<f32>, rect: vec4<f32>, uvs: vec2<f32>) -> vec4<f32>\r\n"
9449
+ + "{\r\n"
9450
+ + " let size: f32 = pow(2.0, tile.y);\r\n"
9451
+ + " let sizex: f32 = size / rect.z;\r\n"
9452
+ + " let sizey: f32 = size / rect.w;\r\n"
9453
+ + " var uv: vec2<f32> = uvs;\r\n"
9454
+ + " uv.x = uv.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x;\r\n"
9455
+ + " uv.y = uv.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y;\r\n"
9456
+ + " return textureSample(atlas, sampler, uv);\r\n"
9457
+ + "}\r\n"
9458
+ + "\r\n"
9459
+ + "fn blendSplatmapAtlasColors(splatmap: vec4<f32>, color1: vec4<f32>, color2: vec4<f32>, color3: vec4<f32>, color4: vec4<f32>, mixbuffer: vec3<f32>) -> vec3<f32>\r\n"
9460
+ + "{\r\n"
9461
+ + " let buffer1: vec3<f32> = mix(mixbuffer, color1.rgb, splatmap.r);\r\n"
9462
+ + " let buffer2: vec3<f32> = mix(buffer1, color2.rgb, splatmap.g);\r\n"
9463
+ + " let buffer3: vec3<f32> = mix(buffer2, color3.rgb, splatmap.b);\r\n"
9464
+ + " return mix(buffer3, color4.rgb, splatmap.a);\r\n"
9465
+ + "}\r\n"
9466
+ + "\r\n"
9467
+ + "fn perturbNormalSamplerColor(cotangentFrame: mat3x3<f32>, samplerColor: vec3<f32>, scale: f32) -> vec3<f32>\r\n"
9468
+ + "{\r\n"
9469
+ + " var map: vec3<f32> = samplerColor.xyz;\r\n"
9470
+ + " map = map * 2.00787402 - 1.00787402;\r\n"
9471
+ + " #ifdef NORMALXYSCALE\r\n"
9472
+ + " map = normalize(map * vec3<f32>(scale, scale, 1.0));\r\n"
9473
+ + " #endif\r\n"
9474
+ + " return normalize(cotangentFrame * map);\r\n"
9475
+ + "}\r\n"
9476
+ + "#endif\r\n\r\n");
9477
+ }
9478
+ return result;
9479
+ }
9480
+ WGSL_FormatTerrainFragmentUpdateColor(terrainInfo, colorName, splatmapSampler, detailsSampler, normalsSampler, colorCorrection = 1.0) {
9481
+ let result = "";
9482
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9483
+ result = ("\r\n#ifndef TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
9484
+ + "#define TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
9485
+ + "var normalsColor: vec3<f32> = vec3<f32>(0.5, 0.5, 1.0);\r\n"
9486
+ + "var normalsBuffer: vec3<f32> = normalW.rgb;\r\n"
9487
+ + "var splatmapBuffer: vec3<f32> = " + colorName + ".rgb;\r\n"
9488
+ + "var autoMipMapLevel: f32 = -1.0;\r\n"
9489
+ + "var normalCorrection: f32 = 1.0;\r\n"
9490
+ + "var detailCorrection: f32 = " + colorCorrection.toFixed(4) + ";\r\n"
9491
+ + "\r\n"
9492
+ + "#if defined(ALBEDO) && defined(" + splatmapSampler.toUpperCase() + ") && defined(" + detailsSampler.toUpperCase() + ")\r\n"
9493
+ + "\r\n"
9494
+ + "// Reset Normal Values\r\n"
9495
+ + "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
9496
+ + " uvOffset = vec2<f32>(0.0, 0.0);\r\n"
9497
+ + " #ifdef NORMAL\r\n"
9498
+ + " normalW = normalize(input.vNormalW);\r\n"
9499
+ + " #else\r\n"
9500
+ + " normalW = normalize(cross(dpdx(input.vPositionW), dpdy(input.vPositionW))) * scene.vEyePosition.w;\r\n"
9501
+ + " #endif\r\n"
9502
+ + " #ifdef CLEARCOAT\r\n"
9503
+ + " clearCoatNormalW = normalW;\r\n"
9504
+ + " #endif\r\n"
9505
+ + " #if defined(BUMP) || defined(PARALLAX)\r\n"
9506
+ + " #if defined(CLEARCOAT_BUMP) && defined(TANGENT) && defined(NORMAL)\r\n"
9507
+ + " TBN = vTBN;\r\n"
9508
+ + " #else\r\n"
9509
+ + " TBN = cotangent_frame(normalW, input.vPositionW, fragmentInputs.vSplatmapUV, vec2<f32>(1.0, 1.0));\r\n"
9510
+ + " #endif\r\n"
9511
+ + " #elif defined(ANISOTROPIC)\r\n"
9512
+ + " #if defined(CLEARCOAT_BUMP) && defined(TANGENT) && defined(NORMAL)\r\n"
9513
+ + " TBN = vTBN;\r\n"
9514
+ + " #else\r\n"
9515
+ + " TBN = cotangent_frame(normalW, input.vPositionW, fragmentInputs.vSplatmapUV, vec2<f32>(1.0, 1.0));\r\n"
9516
+ + " #endif\r\n"
9517
+ + " #endif\r\n"
9518
+ + " #ifdef PARALLAX\r\n"
9519
+ + " invTBN = transposeMat3(TBN);\r\n"
9520
+ + " #endif\r\n"
9521
+ + " normalW = perturbNormalSamplerColor(TBN, normalsColor, 1.0);\r\n"
9522
+ + "#endif\r\n"
9523
+ + "\r\n"
9524
+ + "// Global Atlas Values\r\n"
9525
+ + "let splatTileSize: f32 = " + terrainInfo.splatmapAtlas[2].toFixed(4) + ";\r\n"
9526
+ + "let splatTileBits: f32 = " + terrainInfo.splatmapAtlas[3].toFixed(4) + ";\r\n"
9527
+ + "let detailTileSize: f32 = " + terrainInfo.textureAtlas[2].toFixed(4) + ";\r\n"
9528
+ + "let detailTileBits: f32 = " + terrainInfo.textureAtlas[3].toFixed(4) + ";\r\n"
9529
+ + "\r\n"
9530
+ + "// Sample splatmap textures\r\n");
9531
+ if (terrainInfo.splatmapCount > 0) {
9532
+ let counter = 0;
9533
+ result += "normalsBuffer = vec3<f32>(0.0,0.0,0.0);\r\n";
9534
+ for (let index = 0; index < terrainInfo.splatmapCount; index++) {
9535
+ counter = (index * 4);
9536
+ const splatmapRect = terrainInfo["splatmapRect" + index];
9537
+ result += "var splatmapRect" + index + ": vec4<f32> = vec4<f32>(" + splatmapRect[0].toFixed(4) + ", " + splatmapRect[1].toFixed(4) + ", " + splatmapRect[2].toFixed(4) + ", " + splatmapRect[3].toFixed(4) + ");\r\n";
9538
+ result += "var splatmapAlbedo" + index + ": vec4<f32> = sampleSplatmapAtlas2D(" + splatmapSampler + ", " + splatmapSampler + "Sampler, vec2<f32>(splatTileSize, splatTileBits), splatmapRect" + index + ", (fragmentInputs.vSplatmapUV + uvOffset));\r\n";
9539
+ result += "var textureAlbedo" + (counter + 0) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9540
+ result += "var textureAlbedo" + (counter + 1) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9541
+ result += "var textureAlbedo" + (counter + 2) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9542
+ result += "var textureAlbedo" + (counter + 3) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9543
+ if (terrainInfo["textureRect" + (counter + 0)]) {
9544
+ const textureRect = terrainInfo["textureRect" + (counter + 0)];
9545
+ const textureInfo = terrainInfo["textureInfo" + (counter + 0)];
9546
+ result += "var textureRect" + (counter + 0) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9547
+ result += "var textureScale" + (counter + 0) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9548
+ result += "var textureOffset" + (counter + 0) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9549
+ result += "var textureTileUV" + (counter + 0) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 0) + ") * textureScale" + (counter + 0) + ");\r\n";
9550
+ result += "textureAlbedo" + (counter + 0) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
9551
+ }
9552
+ if (terrainInfo["textureRect" + (counter + 1)]) {
9553
+ const textureRect = terrainInfo["textureRect" + (counter + 1)];
9554
+ const textureInfo = terrainInfo["textureInfo" + (counter + 1)];
9555
+ result += "var textureRect" + (counter + 1) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9556
+ result += "var textureScale" + (counter + 1) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9557
+ result += "var textureOffset" + (counter + 1) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9558
+ result += "var textureTileUV" + (counter + 1) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 1) + ") * textureScale" + (counter + 1) + ");\r\n";
9559
+ result += "textureAlbedo" + (counter + 1) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
9560
+ }
9561
+ if (terrainInfo["textureRect" + (counter + 2)]) {
9562
+ const textureRect = terrainInfo["textureRect" + (counter + 2)];
9563
+ const textureInfo = terrainInfo["textureInfo" + (counter + 2)];
9564
+ result += "var textureRect" + (counter + 2) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9565
+ result += "var textureScale" + (counter + 2) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9566
+ result += "var textureOffset" + (counter + 2) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9567
+ result += "var textureTileUV" + (counter + 2) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 2) + ") * textureScale" + (counter + 2) + ");\r\n";
9568
+ result += "textureAlbedo" + (counter + 2) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
9569
+ }
9570
+ if (terrainInfo["textureRect" + (counter + 3)]) {
9571
+ const textureRect = terrainInfo["textureRect" + (counter + 3)];
9572
+ const textureInfo = terrainInfo["textureInfo" + (counter + 3)];
9573
+ result += "var textureRect" + (counter + 3) + ": vec4<f32> = vec4<f32>(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9574
+ result += "var textureScale" + (counter + 3) + ": vec2<f32> = vec2<f32>(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9575
+ result += "var textureOffset" + (counter + 3) + ": vec2<f32> = vec2<f32>(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9576
+ result += "var textureTileUV" + (counter + 3) + ": vec2<f32> = ((fragmentInputs.vSplatmapUV + textureOffset" + (counter + 3) + ") * textureScale" + (counter + 3) + ");\r\n";
9577
+ result += "textureAlbedo" + (counter + 3) + " = sampleTextureAtlas2D(" + detailsSampler + ", " + detailsSampler + "Sampler, detailCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
9578
+ }
9579
+ result += "splatmapBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", textureAlbedo" + (counter + 0) + ", textureAlbedo" + (counter + 1) + ", textureAlbedo" + (counter + 2) + ", textureAlbedo" + (counter + 3) + ", splatmapBuffer);\r\n";
9580
+ result += "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n";
9581
+ result += " #if defined(" + normalsSampler.toUpperCase() + ")\r\n";
9582
+ result += " var normalColor" + (counter + 0) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9583
+ result += " var normalColor" + (counter + 1) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9584
+ result += " var normalColor" + (counter + 2) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9585
+ result += " var normalColor" + (counter + 3) + ": vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 1.0);\r\n";
9586
+ if (terrainInfo["textureRect" + (counter + 0)]) {
9587
+ const normalScale = terrainInfo["normalsScale" + (counter + 0)];
9588
+ result += " var normalScale" + (counter + 0) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
9589
+ result += " normalColor" + (counter + 0) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
9590
+ result += " normalColor" + (counter + 0) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 0) + ".rgb, normalScale" + (counter + 0) + "), 1.0);\r\n";
9591
+ }
9592
+ if (terrainInfo["textureRect" + (counter + 1)]) {
9593
+ const normalScale = terrainInfo["normalsScale" + (counter + 1)];
9594
+ result += " var normalScale" + (counter + 1) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
9595
+ result += " normalColor" + (counter + 1) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
9596
+ result += " normalColor" + (counter + 1) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 1) + ".rgb, normalScale" + (counter + 1) + "), 1.0);\r\n";
9597
+ }
9598
+ if (terrainInfo["textureRect" + (counter + 2)]) {
9599
+ const normalScale = terrainInfo["normalsScale" + (counter + 2)];
9600
+ result += " var normalScale" + (counter + 2) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
9601
+ result += " normalColor" + (counter + 2) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
9602
+ result += " normalColor" + (counter + 2) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 2) + ".rgb, normalScale" + (counter + 2) + "), 1.0);\r\n";
9603
+ }
9604
+ if (terrainInfo["textureRect" + (counter + 3)]) {
9605
+ const normalScale = terrainInfo["normalsScale" + (counter + 3)];
9606
+ result += " var normalScale" + (counter + 3) + ": f32 = " + normalScale.toFixed(4) + ";\r\n";
9607
+ result += " normalColor" + (counter + 3) + " = sampleTextureAtlas2D(" + normalsSampler + ", " + normalsSampler + "Sampler, normalCorrection, vec2<f32>(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
9608
+ result += " normalColor" + (counter + 3) + " = vec4<f32>(perturbNormalSamplerColor(TBN, normalColor" + (counter + 3) + ".rgb, normalScale" + (counter + 3) + "), 1.0);\r\n";
9609
+ }
9610
+ result += " normalsBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", normalColor" + (counter + 0) + ", normalColor" + (counter + 1) + ", normalColor" + (counter + 2) + ", normalColor" + (counter + 3) + ", normalsBuffer);\r\n";
9611
+ result += " #endif\r\n";
9612
+ result += "#endif\r\n";
9613
+ result += "\r\n";
9614
+ }
9615
+ }
9616
+ result += ("// Update Color Values\r\n"
9617
+ + colorName + " = splatmapBuffer.rgb;\r\n"
9618
+ + "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
9619
+ + " #if defined(" + normalsSampler.toUpperCase() + ")\r\n"
9620
+ + " normalW = normalsBuffer.rgb;\r\n"
9621
+ + " #endif\r\n"
9622
+ + " #if defined(FORCENORMALFORWARD) && defined(NORMAL)\r\n"
9623
+ + " var faceNormal: vec3<f32> = normalize(cross(dpdx(input.vPositionW), dpdy(input.vPositionW))) * scene.vEyePosition.w;\r\n"
9624
+ + " #if defined(TWOSIDEDLIGHTING)\r\n"
9625
+ + " faceNormal = select(-faceNormal, faceNormal, fragmentInputs.frontFacing)\r\n"
9626
+ + " #endif\r\n"
9627
+ + " normalW *= sign(dot(normalW, faceNormal));\r\n"
9628
+ + " #endif\r\n"
9629
+ + " #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)\r\n"
9630
+ + " normalW = select(-normalW, normalW, fragmentInputs.frontFacing);\r\n"
9631
+ + " #endif\r\n"
9632
+ + "#endif\r\n"
9633
+ + "\r\n"
9634
+ + "#endif\r\n"
9635
+ + "\r\n"
9636
+ + "#endif\r\n\r\n");
9637
+ }
9638
+ return result;
9639
+ }
9640
+ GLSL_FormatTerrainVertexDefintions(terrainInfo) {
9641
+ let result = "";
9642
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9643
+ result = ("\r\n#ifndef TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
9644
+ + "#define TERRAIN_VERTEX_DEFINITIONS\r\n\r\n"
9645
+ + "varying vec2 vSplatmapUV;\r\n"
9646
+ + "\r\n"
9647
+ + "#endif\r\n\r\n");
9648
+ }
9649
+ return result;
9650
+ }
9651
+ GLSL_FormatTerrainVertexMainEnd(terrainInfo) {
9652
+ let result = "";
9653
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9654
+ result = ("\r\n#ifndef TERRAIN_VERTEX_MAIN_END\r\n\r\n"
9655
+ + "#define TERRAIN_VERTEX_MAIN_END\r\n\r\n"
9656
+ + "#ifdef UV1\r\n"
9657
+ + "vSplatmapUV = uv;\r\n"
9658
+ + "#endif\r\n"
9659
+ + "\r\n"
9660
+ + "#endif\r\n\r\n");
9661
+ }
9662
+ return result;
9663
+ }
9664
+ GLSL_FormatTerrainFragmentDefintions(terrainInfo, splatmapSampler, detailsSampler, normalsSampler) {
9665
+ let result = "";
9666
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9667
+ result = ("\r\n#ifndef TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
9668
+ + "#define TERRAIN_FRAGMENT_DEFNITIONS\r\n\r\n"
9669
+ + "varying vec2 vSplatmapUV;\r\n"
9670
+ + "\r\n"
9671
+ + "vec3 srgb_to_linear(const in vec3 c)\r\n"
9672
+ + "{\r\n"
9673
+ + " return pow(c, vec3(2.2));\r\n"
9674
+ + "}\r\n"
9675
+ + "\r\n"
9676
+ + "vec3 linear_to_srgb(const in vec3 c)\r\n"
9677
+ + "{\r\n"
9678
+ + " return pow(c, vec3(1.0 / 2.2));\r\n"
9679
+ + "}\r\n"
9680
+ + "\r\n"
9681
+ + "float calculateMipmapLevel(const in vec2 uvs, const in vec2 size)\r\n"
9682
+ + "{\r\n"
9683
+ + " vec2 dx = dFdx(uvs * size.x);\r\n"
9684
+ + " vec2 dy = dFdy(uvs * size.y);\r\n"
9685
+ + " float d = max(dot(dx, dx), dot(dy, dy));\r\n"
9686
+ + " return 0.4 * log2(d);\r\n"
9687
+ + "}\r\n"
9688
+ + "\r\n"
9689
+ + "vec4 sampleTextureAtlas2D(const in sampler2D atlas, const in float gamma, const in vec2 tile, const in vec4 rect, in vec2 uvs, in float lod)\r\n"
9690
+ + "{\r\n"
9691
+ + " if (lod < 0.0) lod = clamp(calculateMipmapLevel(uvs, vec2(tile.x, tile.x)), 0.0, tile.y); // Tile Info (tile.xy)\r\n"
9692
+ + " float size = pow(2.0, tile.y - lod); // Tile Bits (tile.y)\r\n"
9693
+ + " float sizex = size / rect.z; // Tile Width (rect.z)\r\n"
9694
+ + " float sizey = size / rect.w; // Tile Height (rect.w)\r\n"
9695
+ + " uvs = fract(uvs); // Perfrom Tiling (fract)\r\n"
9696
+ + " uvs.x = uvs.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x; // Tile Position X (rect.x)\r\n"
9697
+ + " uvs.y = uvs.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y; // Tile Position Y (rect.y)\r\n"
9698
+ + " vec4 color = texture2DLodEXT(atlas, uvs, lod);\r\n"
9699
+ + " if (gamma != 1.0) {\r\n"
9700
+ + " color.r = pow(color.r, gamma);\r\n"
9701
+ + " color.g = pow(color.g, gamma);\r\n"
9702
+ + " color.b = pow(color.b, gamma);\r\n"
9703
+ + " }\r\n"
9704
+ + " return color;\r\n"
9705
+ + "}\r\n"
9706
+ + "\r\n"
9707
+ + "vec4 sampleSplatmapAtlas2D(const in sampler2D atlas, const in vec2 tile, const in vec4 rect, in vec2 uvs)\r\n"
9708
+ + "{\r\n"
9709
+ + " float size = pow(2.0, tile.y); // Tile Bits (tile.y)\r\n"
9710
+ + " float sizex = size / rect.z; // Tile Width (rect.z)\r\n"
9711
+ + " float sizey = size / rect.w; // Tile Height (rect.w)\r\n"
9712
+ + " uvs.x = uvs.x * ((sizex * rect.z - 1.0) / sizex) + 0.5 / sizex + rect.z * rect.x; // Tile Position X (rect.x)\r\n"
9713
+ + " uvs.y = uvs.y * ((sizey * rect.w - 1.0) / sizey) + 0.5 / sizey + rect.w * rect.y; // Tile Position Y (rect.y)\r\n"
9714
+ + " return texture2D(atlas, uvs);\r\n"
9715
+ + "}\r\n"
9716
+ + "\r\n"
9717
+ + "vec3 blendSplatmapAtlasColors(const in vec4 splatmap, in vec4 color1, in vec4 color2, in vec4 color3, in vec4 color4, in vec3 mixbuffer)\r\n"
9718
+ + "{\r\n"
9719
+ + " vec3 buffer1 = mix(mixbuffer, color1.rgb, splatmap.r);\r\n"
9720
+ + " vec3 buffer2 = mix(buffer1, color2.rgb, splatmap.g);\r\n"
9721
+ + " vec3 buffer3 = mix(buffer2, color3.rgb, splatmap.b);\r\n"
9722
+ + " return mix(buffer3, color4.rgb, splatmap.a);\r\n"
9723
+ + "}\r\n"
9724
+ + "\r\n"
9725
+ + "vec3 perturbNormalSamplerColor(mat3 cotangentFrame, vec3 samplerColor, float scale)\r\n"
9726
+ + "{\r\n"
9727
+ + " vec3 map = samplerColor.xyz;\r\n"
9728
+ + " map = map * 2.00787402 - 1.00787402;\r\n"
9729
+ + " #ifdef NORMALXYSCALE\r\n"
9730
+ + " map = normalize(map * vec3(scale, scale, 1.0));\r\n"
9731
+ + " #endif\r\n"
9732
+ + " return normalize(cotangentFrame * map);\r\n"
9733
+ + "}\r\n"
9734
+ + "\r\n"
9735
+ + "\r\n"
9736
+ + "#endif\r\n\r\n");
9737
+ }
9738
+ return result;
9739
+ }
9740
+ GLSL_FormatTerrainFragmentUpdateColor(terrainInfo, colorName, splatmapSampler, detailsSampler, normalsSampler, colorCorrection = 1.0) {
9741
+ let result = "";
9742
+ if (terrainInfo != null && terrainInfo.textureAtlas != null && terrainInfo.splatmapAtlas != null && terrainInfo.splatmapCount > 0) {
9743
+ result = ("\r\n#ifndef TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
9744
+ + "#define TERRAIN_FRAGMENT_UPDATE_COLOR\r\n\r\n"
9745
+ + "vec3 normalsColor = vec3(0.5, 0.5, 1.0);\r\n"
9746
+ + "vec3 normalsBuffer = normalW.rgb;\r\n"
9747
+ + "vec3 splatmapBuffer = " + colorName + ".rgb;\r\n"
9748
+ + "float autoMipMapLevel = -1.0;\r\n"
9749
+ + "float normalCorrection = 1.0;\r\n"
9750
+ + "float detailCorrection = " + colorCorrection.toFixed(4) + ";\r\n"
9751
+ + "\r\n"
9752
+ + "#if defined(ALBEDO) && defined(" + splatmapSampler.toUpperCase() + ") && defined(" + detailsSampler.toUpperCase() + ")\r\n"
9753
+ + "\r\n"
9754
+ + "// Reset Normal Values\r\n"
9755
+ + "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
9756
+ + " uvOffset = vec2(0.0, 0.0);\r\n"
9757
+ + " #ifdef NORMAL\r\n"
9758
+ + " normalW = normalize(vNormalW);\r\n"
9759
+ + " #else\r\n"
9760
+ + " normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;\r\n"
9761
+ + " #endif\r\n"
9762
+ + " #ifdef CLEARCOAT\r\n"
9763
+ + " clearCoatNormalW = normalW;\r\n"
9764
+ + " #endif\r\n"
9765
+ + " #if defined(BUMP) || defined(PARALLAX)\r\n"
9766
+ + " #if defined(TANGENT) && defined(NORMAL)\r\n"
9767
+ + " TBN = vTBN;\r\n"
9768
+ + " #else\r\n"
9769
+ + " TBN = cotangent_frame(normalW, vPositionW, vSplatmapUV);\r\n"
9770
+ + " #endif\r\n"
9771
+ + " #elif defined(ANISOTROPIC)\r\n"
9772
+ + " #if defined(TANGENT) && defined(NORMAL)\r\n"
9773
+ + " TBN = vTBN;\r\n"
9774
+ + " #else\r\n"
9775
+ + " TBN = cotangent_frame(normalW, vPositionW, vSplatmapUV, vec2(1.0, 1.0));\r\n"
9776
+ + " #endif\r\n"
9777
+ + " #endif\r\n"
9778
+ + " #ifdef PARALLAX\r\n"
9779
+ + " invTBN = transposeMat3(TBN);\r\n"
9780
+ + " #endif\r\n"
9781
+ + " normalW = perturbNormalSamplerColor(TBN, normalsColor, 1.0);\r\n"
9782
+ + "#endif\r\n"
9783
+ + "\r\n"
9784
+ + "// Global Atlas Values\r\n"
9785
+ + "float splatTileSize = " + terrainInfo.splatmapAtlas[2].toFixed(4) + ";\r\n"
9786
+ + "float splatTileBits = " + terrainInfo.splatmapAtlas[3].toFixed(4) + ";\r\n"
9787
+ + "float detailTileSize = " + terrainInfo.textureAtlas[2].toFixed(4) + ";\r\n"
9788
+ + "float detailTileBits = " + terrainInfo.textureAtlas[3].toFixed(4) + ";\r\n"
9789
+ + "\r\n"
9790
+ + "// Sample splatmap textures\r\n");
9791
+ if (terrainInfo.splatmapCount > 0) {
9792
+ let counter = 0;
9793
+ result += "normalsBuffer = vec3(0.0,0.0,0.0);\r\n";
9794
+ for (let index = 0; index < terrainInfo.splatmapCount; index++) {
9795
+ counter = (index * 4);
9796
+ const splatmapRect = terrainInfo["splatmapRect" + index];
9797
+ result += "vec4 splatmapRect" + index + " = vec4(" + splatmapRect[0].toFixed(4) + ", " + splatmapRect[1].toFixed(4) + ", " + splatmapRect[2].toFixed(4) + ", " + splatmapRect[3].toFixed(4) + ");\r\n";
9798
+ result += "vec4 splatmapAlbedo" + index + " = sampleSplatmapAtlas2D(" + splatmapSampler + ", vec2(splatTileSize, splatTileBits), splatmapRect" + index + ", (vSplatmapUV + uvOffset));\r\n";
9799
+ result += "vec4 textureAlbedo" + (counter + 0) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9800
+ result += "vec4 textureAlbedo" + (counter + 1) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9801
+ result += "vec4 textureAlbedo" + (counter + 2) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9802
+ result += "vec4 textureAlbedo" + (counter + 3) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9803
+ if (terrainInfo["textureRect" + (counter + 0)]) {
9804
+ const textureRect = terrainInfo["textureRect" + (counter + 0)];
9805
+ const textureInfo = terrainInfo["textureInfo" + (counter + 0)];
9806
+ result += "vec4 textureRect" + (counter + 0) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9807
+ result += "vec2 textureScale" + (counter + 0) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9808
+ result += "vec2 textureOffset" + (counter + 0) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9809
+ result += "vec2 textureTileUV" + (counter + 0) + " = ((vSplatmapUV + textureOffset" + (counter + 0) + ") * textureScale" + (counter + 0) + ");\r\n";
9810
+ result += "textureAlbedo" + (counter + 0) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
9811
+ }
9812
+ if (terrainInfo["textureRect" + (counter + 1)]) {
9813
+ const textureRect = terrainInfo["textureRect" + (counter + 1)];
9814
+ const textureInfo = terrainInfo["textureInfo" + (counter + 1)];
9815
+ result += "vec4 textureRect" + (counter + 1) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9816
+ result += "vec2 textureScale" + (counter + 1) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9817
+ result += "vec2 textureOffset" + (counter + 1) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9818
+ result += "vec2 textureTileUV" + (counter + 1) + " = ((vSplatmapUV + textureOffset" + (counter + 1) + ") * textureScale" + (counter + 1) + ");\r\n";
9819
+ result += "textureAlbedo" + (counter + 1) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
9820
+ }
9821
+ if (terrainInfo["textureRect" + (counter + 2)]) {
9822
+ const textureRect = terrainInfo["textureRect" + (counter + 2)];
9823
+ const textureInfo = terrainInfo["textureInfo" + (counter + 2)];
9824
+ result += "vec4 textureRect" + (counter + 2) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9825
+ result += "vec2 textureScale" + (counter + 2) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9826
+ result += "vec2 textureOffset" + (counter + 2) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9827
+ result += "vec2 textureTileUV" + (counter + 2) + " = ((vSplatmapUV + textureOffset" + (counter + 2) + ") * textureScale" + (counter + 2) + ");\r\n";
9828
+ result += "textureAlbedo" + (counter + 2) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
9829
+ }
9830
+ if (terrainInfo["textureRect" + (counter + 3)]) {
9831
+ const textureRect = terrainInfo["textureRect" + (counter + 3)];
9832
+ const textureInfo = terrainInfo["textureInfo" + (counter + 3)];
9833
+ result += "vec4 textureRect" + (counter + 3) + " = vec4(" + textureRect[0].toFixed(4) + ", " + textureRect[1].toFixed(4) + ", " + textureRect[2].toFixed(4) + ", " + textureRect[3].toFixed(4) + ");\r\n";
9834
+ result += "vec2 textureScale" + (counter + 3) + " = vec2(" + textureInfo[0].toFixed(4) + ", " + textureInfo[1].toFixed(4) + ");\r\n";
9835
+ result += "vec2 textureOffset" + (counter + 3) + " = vec2(" + textureInfo[2].toFixed(4) + ", " + textureInfo[3].toFixed(4) + ");\r\n";
9836
+ result += "vec2 textureTileUV" + (counter + 3) + " = ((vSplatmapUV + textureOffset" + (counter + 3) + ") * textureScale" + (counter + 3) + ");\r\n";
9837
+ result += "textureAlbedo" + (counter + 3) + " = sampleTextureAtlas2D(" + detailsSampler + ", detailCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
9838
+ }
9839
+ result += "splatmapBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", textureAlbedo" + (counter + 0) + ", textureAlbedo" + (counter + 1) + ", textureAlbedo" + (counter + 2) + ", textureAlbedo" + (counter + 3) + ", splatmapBuffer);\r\n";
9840
+ result += "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n";
9841
+ result += " #if defined(" + normalsSampler.toUpperCase() + ")\r\n";
9842
+ result += " vec4 normalColor" + (counter + 0) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9843
+ result += " vec4 normalColor" + (counter + 1) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9844
+ result += " vec4 normalColor" + (counter + 2) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9845
+ result += " vec4 normalColor" + (counter + 3) + " = vec4(0.0, 0.0, 0.0, 1.0);\r\n";
9846
+ if (terrainInfo["textureRect" + (counter + 0)]) {
9847
+ const normalScale = terrainInfo["normalsScale" + (counter + 0)];
9848
+ result += " float normalScale" + (counter + 0) + " = " + normalScale.toFixed(4) + ";\r\n";
9849
+ result += " normalColor" + (counter + 0) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 0) + ", textureTileUV" + (counter + 0) + ", autoMipMapLevel);\r\n";
9850
+ result += " normalColor" + (counter + 0) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 0) + ".rgb, normalScale" + (counter + 0) + ");\r\n";
9851
+ }
9852
+ if (terrainInfo["textureRect" + (counter + 1)]) {
9853
+ const normalScale = terrainInfo["normalsScale" + (counter + 1)];
9854
+ result += " float normalScale" + (counter + 1) + " = " + normalScale.toFixed(4) + ";\r\n";
9855
+ result += " normalColor" + (counter + 1) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 1) + ", textureTileUV" + (counter + 1) + ", autoMipMapLevel);\r\n";
9856
+ result += " normalColor" + (counter + 1) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 1) + ".rgb, normalScale" + (counter + 1) + ");\r\n";
9857
+ }
9858
+ if (terrainInfo["textureRect" + (counter + 2)]) {
9859
+ const normalScale = terrainInfo["normalsScale" + (counter + 2)];
9860
+ result += " float normalScale" + (counter + 2) + " = " + normalScale.toFixed(4) + ";\r\n";
9861
+ result += " normalColor" + (counter + 2) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 2) + ", textureTileUV" + (counter + 2) + ", autoMipMapLevel);\r\n";
9862
+ result += " normalColor" + (counter + 2) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 2) + ".rgb, normalScale" + (counter + 2) + ");\r\n";
9863
+ }
9864
+ if (terrainInfo["textureRect" + (counter + 3)]) {
9865
+ const normalScale = terrainInfo["normalsScale" + (counter + 3)];
9866
+ result += " float normalScale" + (counter + 3) + " = " + normalScale.toFixed(4) + ";\r\n";
9867
+ result += " normalColor" + (counter + 3) + " = sampleTextureAtlas2D(" + normalsSampler + ", normalCorrection, vec2(detailTileSize, detailTileBits), textureRect" + (counter + 3) + ", textureTileUV" + (counter + 3) + ", autoMipMapLevel);\r\n";
9868
+ result += " normalColor" + (counter + 3) + ".rgb = perturbNormalSamplerColor(TBN, normalColor" + (counter + 3) + ".rgb, normalScale" + (counter + 3) + ");\r\n";
9869
+ }
9870
+ result += " normalsBuffer = blendSplatmapAtlasColors(splatmapAlbedo" + index + ", normalColor" + (counter + 0) + ", normalColor" + (counter + 1) + ", normalColor" + (counter + 2) + ", normalColor" + (counter + 3) + ", normalsBuffer);\r\n";
9871
+ result += " #endif\r\n";
9872
+ result += "#endif\r\n";
9873
+ result += "\r\n";
9874
+ }
9875
+ }
9876
+ result += ("// Update Color Values\r\n"
9877
+ + colorName + " = splatmapBuffer.rgb;\r\n"
9878
+ + "#if defined(BUMP) || defined(PARALLAX) || defined(ANISOTROPIC)\r\n"
9879
+ + " #if defined(" + normalsSampler.toUpperCase() + ")\r\n"
9880
+ + " normalW = normalsBuffer.rgb;\r\n"
9881
+ + " #endif\r\n"
9882
+ + " #if defined(FORCENORMALFORWARD) && defined(NORMAL)\r\n"
9883
+ + " vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;\r\n"
9884
+ + " #if defined(TWOSIDEDLIGHTING)\r\n"
9885
+ + " faceNormal = gl_FrontFacing ? faceNormal : -faceNormal;\r\n"
9886
+ + " #endif\r\n"
9887
+ + " normalW *= sign(dot(normalW, faceNormal));\r\n"
9888
+ + " #endif\r\n"
9889
+ + " #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)\r\n"
9890
+ + " normalW = gl_FrontFacing ? normalW : -normalW;\r\n"
9891
+ + " #endif\r\n"
9892
+ + "#endif\r\n"
9893
+ + "\r\n"
9894
+ + "#endif\r\n"
9895
+ + "\r\n"
9896
+ + "#endif\r\n\r\n");
9897
+ }
9898
+ return result;
9899
+ }
9900
+ }
9266
9901
  export class GrassStandardMaterial extends StandardShaderMaterial {
9267
9902
  constructor(name, scene) {
9268
9903
  super(name, scene);
@@ -10365,6 +11000,372 @@ export class GrassBillboardMaterialPlugin extends StandardShaderMaterialPlugin {
10365
11000
  this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
10366
11001
  }
10367
11002
  }
11003
+ export class TreeBranchMaterial extends CustomShaderMaterial {
11004
+ constructor(name, scene) {
11005
+ super(name, scene);
11006
+ this._windTimeAccum = 0.0;
11007
+ this.shader = this.getShaderName();
11008
+ this.plugin = new TreeBranchMaterialPlugin(this, this.shader);
11009
+ this.addFloatUniform("g_windTime", 0.0);
11010
+ this.addFloatUniform("g_windAmount", 0.5);
11011
+ this.addFloatUniform("g_windSpeed", 1.0);
11012
+ this.addFloatUniform("g_windStrength", 0.5);
11013
+ this.addVector4Uniform("g_windDirection", new Vector4(1.0, 0.0, 0.0, 0.0));
11014
+ this.addFloatUniform("g_branchRootY", 0.0);
11015
+ this.addFloatUniform("g_branchMaxAngle", 0.35);
11016
+ this.addFloatUniform("g_branchMaxHeight", 2.0);
11017
+ this.addFloatUniform("g_useVertexColorMask", 1.0);
11018
+ this.addFloatUniform("g_spatialFrequency", 0.15);
11019
+ this._windTimeAccum = 0.0;
11020
+ }
11021
+ awake() {
11022
+ }
11023
+ update() {
11024
+ try {
11025
+ const rawDt = SceneManager.GetDeltaSeconds(this.getScene());
11026
+ const dt = Math.min(rawDt, 1 / 30);
11027
+ const speed = this.getFloatValue("g_windSpeed") ?? 0;
11028
+ if (speed > 0) {
11029
+ this._windTimeAccum += dt;
11030
+ this.setFloatValue("g_windTime", this._windTimeAccum);
11031
+ }
11032
+ }
11033
+ catch (e) {
11034
+ }
11035
+ }
11036
+ getShaderName() {
11037
+ return "TreeBranchMaterial";
11038
+ }
11039
+ setWindDirection(x, y, z) {
11040
+ const v = new Vector3(x, y, z);
11041
+ if (v.lengthSquared() < 1e-8) {
11042
+ this.setVector4Value("g_windDirection", new Vector4(1, 0, 0, 0));
11043
+ return;
11044
+ }
11045
+ v.normalize();
11046
+ this.setVector4Value("g_windDirection", new Vector4(v.x, v.y, v.z, 0.0));
11047
+ }
11048
+ getWindDirection() {
11049
+ return this.getVector4Value("g_windDirection");
11050
+ }
11051
+ }
11052
+ export class TreeBranchMaterialPlugin extends CustomShaderMaterialPlugin {
11053
+ constructor(customMaterial, shaderName) {
11054
+ super(customMaterial, shaderName, 110, { TREEBRANCHMATERIAL: false });
11055
+ }
11056
+ isCompatible(shaderLanguage) {
11057
+ return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
11058
+ }
11059
+ getCustomCode(shaderType, shaderLanguage) {
11060
+ if (shaderType === "vertex") {
11061
+ if (shaderLanguage === ShaderLanguage.WGSL) {
11062
+ return {
11063
+ CUSTOM_VERTEX_MAIN_END: `
11064
+ // -------------------------
11065
+ // TreeBranch wind bending (local-space)
11066
+ // Authoring (recommended):
11067
+ // vertexColor.r = bend weight (0 trunk .. 1 tips)
11068
+ // vertexColor.g = phase offset noise (0..1)
11069
+ // Falls back to height-based weight if vertex color missing/disabled.
11070
+ // -------------------------
11071
+
11072
+ fn safeNormalize(v: vec3f) -> vec3f {
11073
+ let lsq = dot(v, v);
11074
+ if (lsq < 1e-8) { return vec3f(1.0, 0.0, 0.0); }
11075
+ return v * inverseSqrt(lsq);
11076
+ }
11077
+
11078
+ fn rotateRodrigues(p: vec3f, axis: vec3f, angle: f32) -> vec3f {
11079
+ let a = safeNormalize(axis);
11080
+ let c = cos(angle);
11081
+ let s = sin(angle);
11082
+ return p * c + cross(a, p) * s + a * dot(a, p) * (1.0 - c);
11083
+ }
11084
+
11085
+ // Local position & normal (preferred: use normalUpdated if present in shader)
11086
+ var pL: vec3f = positionUpdated;
11087
+
11088
+ // If COLOR attribute exists, Babylon usually provides vertexInputs.color.
11089
+ // If not available, these will be 1,1,1,1 (depending on pipeline) — we guard with g_useVertexColorMask.
11090
+ var bendMask: f32 = 1.0;
11091
+ var phaseNoise: f32 = 0.0;
11092
+ if (uniforms.g_useVertexColorMask > 0.5) {
11093
+ bendMask = clamp(vertexInputs.color.r, 0.0, 1.0);
11094
+ phaseNoise = vertexInputs.color.g; // 0..1
11095
+ }
11096
+
11097
+ // Height-based fallback weight (normalized between rootY..maxHeight)
11098
+ let denom = max(uniforms.g_branchMaxHeight - uniforms.g_branchRootY, 1e-3);
11099
+ let h01 = clamp((pL.y - uniforms.g_branchRootY) / denom, 0.0, 1.0);
11100
+
11101
+ // Final per-vertex weight:
11102
+ // - If using vertex colors: bendMask * smooth tip emphasis
11103
+ // - Else: height-based
11104
+ var w: f32 = h01;
11105
+ if (uniforms.g_useVertexColorMask > 0.5) {
11106
+ w = bendMask * (h01 * h01 * (3.0 - 2.0 * h01)); // smoothstep(h01)
11107
+ } else {
11108
+ w = h01 * h01; // a bit stiffer near base
11109
+ }
11110
+
11111
+ // Wind direction in LOCAL space:
11112
+ // We transform windDirWorld by inverse world matrix rotation (approx using finalWorld columns).
11113
+ // finalWorld transforms local->world. For direction, inverse rotation is transpose of 3x3 if no shear.
11114
+ let windW = safeNormalize(uniforms.g_windDirection.xyz);
11115
+ let rightW = safeNormalize(finalWorld[0].xyz);
11116
+ let upW = safeNormalize(finalWorld[1].xyz);
11117
+ let fwdW = safeNormalize(finalWorld[2].xyz);
11118
+
11119
+ // Convert world dir to local by dot with basis
11120
+ let windL = safeNormalize(vec3f(dot(windW, rightW), dot(windW, upW), dot(windW, fwdW)));
11121
+
11122
+ // Bend axis: perpendicular to up and wind, in LOCAL space
11123
+ var axisL = safeNormalize(cross(vec3f(0.0, 1.0, 0.0), windL));
11124
+ if (dot(axisL, axisL) < 1e-6) { axisL = vec3f(1.0, 0.0, 0.0); }
11125
+
11126
+ // Spatially varying phase so whole tree doesn't move as a rigid sheet
11127
+ // Use local xz for coherence; optionally mod by vertexColor.g
11128
+ let spatial = (pL.x + pL.z) * uniforms.g_spatialFrequency;
11129
+ let phase = uniforms.g_windTime * uniforms.g_windSpeed
11130
+ + spatial * uniforms.g_windStrength
11131
+ + phaseNoise * 6.28318;
11132
+
11133
+ // Two-wave composite like Unity-ish wind
11134
+ let base = sin(phase);
11135
+ let gust = 0.5 * sin(phase * 1.7 + 1.2);
11136
+ let sway = (base + gust) * uniforms.g_windAmount;
11137
+
11138
+ // Convert sway to angle (clamped)
11139
+ var angle = sway * w * uniforms.g_branchMaxAngle;
11140
+ angle = clamp(angle, -uniforms.g_branchMaxAngle, uniforms.g_branchMaxAngle);
11141
+
11142
+ // Pivot in local space
11143
+ let pivotL = vec3f(0.0, uniforms.g_branchRootY, 0.0);
11144
+ let relL = pL - pivotL;
11145
+ let bentRelL = rotateRodrigues(relL, axisL, angle);
11146
+ let bentPL = pivotL + bentRelL;
11147
+
11148
+ // Transform to world using finalWorld (keeps proper anchoring)
11149
+ let branchWorld = (finalWorld * vec4f(bentPL, 1.0)).xyz;
11150
+
11151
+ // Rotate normal similarly (only if NORMAL is active)
11152
+ #ifdef NORMAL
11153
+ // Babylon WGSL PBR usually provides normalUpdated in local/object space
11154
+ var nL: vec3f = normalize(normalUpdated);
11155
+ let bentNL = rotateRodrigues(nL, axisL, angle);
11156
+ // Convert to world by world 3x3
11157
+ let nW = normalize(vec3f(
11158
+ dot(bentNL, vec3f(finalWorld[0].x, finalWorld[0].y, finalWorld[0].z)),
11159
+ dot(bentNL, vec3f(finalWorld[1].x, finalWorld[1].y, finalWorld[1].z)),
11160
+ dot(bentNL, vec3f(finalWorld[2].x, finalWorld[2].y, finalWorld[2].z))
11161
+ ));
11162
+ vertexOutputs.vNormalW = nW;
11163
+ #endif
11164
+
11165
+ #if defined(POSITION) || defined(BUMP)
11166
+ vertexOutputs.vPositionW = branchWorld;
11167
+ #endif
11168
+
11169
+ vertexOutputs.position = scene.viewProjection * vec4f(branchWorld, 1.0);
11170
+ `
11171
+ };
11172
+ }
11173
+ else {
11174
+ return {
11175
+ CUSTOM_VERTEX_DEFINITIONS: `
11176
+ uniform float g_windTime;
11177
+ uniform float g_windAmount;
11178
+ uniform float g_windSpeed;
11179
+ uniform float g_windStrength;
11180
+ uniform vec4 g_windDirection;
11181
+
11182
+ uniform float g_branchRootY;
11183
+ uniform float g_branchMaxAngle;
11184
+ uniform float g_branchMaxHeight;
11185
+ uniform float g_useVertexColorMask;
11186
+ uniform float g_spatialFrequency;
11187
+
11188
+ vec3 safeNormalize(vec3 v) {
11189
+ float lsq = dot(v, v);
11190
+ if (lsq < 1e-8) return vec3(1.0, 0.0, 0.0);
11191
+ return v * inversesqrt(lsq);
11192
+ }
11193
+
11194
+ vec3 rotateRodrigues(vec3 p, vec3 axis, float angle) {
11195
+ vec3 a = safeNormalize(axis);
11196
+ float c = cos(angle);
11197
+ float s = sin(angle);
11198
+ return p * c + cross(a, p) * s + a * dot(a, p) * (1.0 - c);
11199
+ }
11200
+ `,
11201
+ CUSTOM_VERTEX_MAIN_END: `
11202
+ // Local/object position
11203
+ vec3 pL = positionUpdated;
11204
+
11205
+ // Vertex color mask (recommended authoring)
11206
+ float bendMask = 1.0;
11207
+ float phaseNoise = 0.0;
11208
+ #ifdef VERTEXCOLOR
11209
+ if (g_useVertexColorMask > 0.5) {
11210
+ bendMask = clamp(color.r, 0.0, 1.0);
11211
+ phaseNoise = color.g;
11212
+ }
11213
+ #endif
11214
+
11215
+ float denom = max(g_branchMaxHeight - g_branchRootY, 1e-3);
11216
+ float h01 = clamp((pL.y - g_branchRootY) / denom, 0.0, 1.0);
11217
+
11218
+ float w = h01;
11219
+ if (g_useVertexColorMask > 0.5) {
11220
+ // smoothstep(h01)
11221
+ w = bendMask * (h01 * h01 * (3.0 - 2.0 * h01));
11222
+ } else {
11223
+ w = h01 * h01;
11224
+ }
11225
+
11226
+ // Convert wind world dir to local using world basis (transpose approx)
11227
+ vec3 windW = safeNormalize(g_windDirection.xyz);
11228
+ vec3 rightW = safeNormalize(finalWorld[0].xyz);
11229
+ vec3 upW = safeNormalize(finalWorld[1].xyz);
11230
+ vec3 fwdW = safeNormalize(finalWorld[2].xyz);
11231
+ vec3 windL = safeNormalize(vec3(dot(windW, rightW), dot(windW, upW), dot(windW, fwdW)));
11232
+
11233
+ vec3 axisL = safeNormalize(cross(vec3(0.0, 1.0, 0.0), windL));
11234
+ if (dot(axisL, axisL) < 1e-6) axisL = vec3(1.0, 0.0, 0.0);
11235
+
11236
+ float spatial = (pL.x + pL.z) * g_spatialFrequency;
11237
+ float phase = g_windTime * g_windSpeed
11238
+ + spatial * g_windStrength
11239
+ + phaseNoise * 6.2831853;
11240
+
11241
+ float base = sin(phase);
11242
+ float gust = 0.5 * sin(phase * 1.7 + 1.2);
11243
+ float sway = (base + gust) * g_windAmount;
11244
+
11245
+ float angle = clamp(sway * w * g_branchMaxAngle, -g_branchMaxAngle, g_branchMaxAngle);
11246
+
11247
+ vec3 pivotL = vec3(0.0, g_branchRootY, 0.0);
11248
+ vec3 relL = pL - pivotL;
11249
+ vec3 bentPL = pivotL + rotateRodrigues(relL, axisL, angle);
11250
+
11251
+ vec3 branchWorld = (finalWorld * vec4(bentPL, 1.0)).xyz;
11252
+
11253
+ #if defined(POSITION) || defined(BUMP)
11254
+ vPositionW = branchWorld;
11255
+ #endif
11256
+
11257
+ #ifdef NORMAL
11258
+ // normalUpdated is local/object normal
11259
+ vec3 nL = normalize(normalUpdated);
11260
+ vec3 bentNL = rotateRodrigues(nL, axisL, angle);
11261
+
11262
+ // world normal from world basis (approx; assumes no non-uniform scale/shear)
11263
+ vec3 nW = normalize(bentNL.x * rightW + bentNL.y * upW + bentNL.z * fwdW);
11264
+ vNormalW = nW;
11265
+ #endif
11266
+
11267
+ gl_Position = viewProjection * vec4(branchWorld, 1.0);
11268
+ `
11269
+ };
11270
+ }
11271
+ }
11272
+ if (shaderType === "fragment") {
11273
+ if (shaderLanguage === ShaderLanguage.WGSL) {
11274
+ return {
11275
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: `
11276
+ // no-op
11277
+ `
11278
+ };
11279
+ }
11280
+ else {
11281
+ return {
11282
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: `
11283
+ // no-op
11284
+ `
11285
+ };
11286
+ }
11287
+ }
11288
+ return null;
11289
+ }
11290
+ getUniforms(shaderLanguage) {
11291
+ const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
11292
+ this.vertexDefinitions = this.getCustomShaderMaterial().getCustomVertexCode(wgsl);
11293
+ this.fragmentDefinitions = (wgsl === true) ? this.getCustomShaderMaterial().getCustomFragmentCode(wgsl) : null;
11294
+ return this.getCustomShaderMaterial().getCustomUniforms(wgsl);
11295
+ }
11296
+ getSamplers(samplers) {
11297
+ const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
11298
+ if (customSamplers && customSamplers.length > 0)
11299
+ samplers.push(...customSamplers);
11300
+ }
11301
+ getAttributes(attributes, scene, mesh) {
11302
+ const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
11303
+ if (customAttributes && customAttributes.length > 0)
11304
+ attributes.push(...customAttributes);
11305
+ if (attributes.indexOf("color") === -1)
11306
+ attributes.push("color");
11307
+ }
11308
+ prepareDefines(defines, scene, mesh) {
11309
+ if (!this.getIsEnabled())
11310
+ return;
11311
+ this.getCustomShaderMaterial().prepareCustomDefines(defines);
11312
+ }
11313
+ bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
11314
+ if (!this.getIsEnabled())
11315
+ return;
11316
+ this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
11317
+ }
11318
+ static ExtractWindZoneOverride(properties, terrainTransform, builderInstance) {
11319
+ const candidates = [];
11320
+ if (properties)
11321
+ candidates.push(properties);
11322
+ if (properties && properties.properties)
11323
+ candidates.push(properties.properties);
11324
+ const meta = terrainTransform?.metadata;
11325
+ if (meta)
11326
+ candidates.push(meta);
11327
+ if (meta && meta.toolkit)
11328
+ candidates.push(meta.toolkit);
11329
+ if (meta && meta.properties)
11330
+ candidates.push(meta.properties);
11331
+ const bmeta = builderInstance?.metadata;
11332
+ if (bmeta)
11333
+ candidates.push(bmeta);
11334
+ if (bmeta && bmeta.toolkit)
11335
+ candidates.push(bmeta.toolkit);
11336
+ const tmeta = builderInstance?.transform?.metadata;
11337
+ if (tmeta)
11338
+ candidates.push(tmeta);
11339
+ if (tmeta && tmeta.toolkit)
11340
+ candidates.push(tmeta.toolkit);
11341
+ let zones = null;
11342
+ for (const c of candidates) {
11343
+ if (!c)
11344
+ continue;
11345
+ if (Array.isArray(c.windzones)) {
11346
+ zones = c.windzones;
11347
+ break;
11348
+ }
11349
+ }
11350
+ if (!zones || zones.length === 0)
11351
+ return null;
11352
+ let best = null;
11353
+ let bestScore = -1e9;
11354
+ for (const z of zones) {
11355
+ if (!z)
11356
+ continue;
11357
+ const t = ((z.type != null) ? z.type : (z.mode != null ? z.mode : "")).toString().toLowerCase();
11358
+ const isDirectional = (t.indexOf("direction") >= 0);
11359
+ const main = (z.windMain != null) ? z.windMain : 0.0;
11360
+ const score = (isDirectional ? 1000.0 : 0.0) + main;
11361
+ if (score > bestScore) {
11362
+ bestScore = score;
11363
+ best = z;
11364
+ }
11365
+ }
11366
+ return best;
11367
+ }
11368
+ }
10368
11369
  export class VertexAnimationMaterial extends CustomShaderMaterial {
10369
11370
  constructor(name, scene) {
10370
11371
  super(name, scene);