@babylonjs-toolkit/next 9.12.1 → 9.12.1011

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.
@@ -9,7 +9,7 @@ import { Light } from '@babylonjs/core/Lights/light';
9
9
  import { Vector3, Quaternion, Vector2, Vector4, Matrix, TmpVectors } from '@babylonjs/core/Maths/math.vector';
10
10
  import { FreeCamera } from '@babylonjs/core/Cameras/freeCamera';
11
11
  import { Engine } from '@babylonjs/core/Engines/engine';
12
- import { SphericalPolynomial, HDRCubeTexture, EXRCubeTexture, SphericalHarmonics, Constants, PostProcess, BlurPostProcess, DefaultRenderingPipeline, SSAORenderingPipeline, SSRRenderingPipeline, MotionBlurPostProcess } from '@babylonjs/core';
12
+ import { SphericalPolynomial, HDRCubeTexture, EXRCubeTexture, SphericalHarmonics, Constants, SerializationHelper, PostProcess, BlurPostProcess, DefaultRenderingPipeline, SSAORenderingPipeline, SSRRenderingPipeline, MotionBlurPostProcess } from '@babylonjs/core';
13
13
  import { Material } from '@babylonjs/core/Materials/material';
14
14
  import { InstancedMesh } from '@babylonjs/core/Meshes/instancedMesh';
15
15
  import { CreateNavigationPluginAsync } from '@babylonjs/addons';
@@ -53,7 +53,7 @@ import { ShaderMaterial } from '@babylonjs/core/Materials/shaderMaterial';
53
53
  import { MultiMaterial } from '@babylonjs/core/Materials/multiMaterial';
54
54
  import { HemisphericLight } from '@babylonjs/core/Lights/hemisphericLight';
55
55
  import { MaterialPluginBase } from '@babylonjs/core/Materials/materialPluginBase';
56
- import { ShaderLanguage, RawTexture2DArray, RawTexture, ShadowDepthWrapper, ImageProcessingConfiguration, ColorCurves } from '@babylonjs/core/Materials';
56
+ import { ShaderLanguage, RawTexture, ShadowDepthWrapper, ImageProcessingConfiguration, ColorCurves, RawTexture2DArray } from '@babylonjs/core/Materials';
57
57
  import { SpotLight } from '@babylonjs/core/Lights/spotLight';
58
58
  import { UniversalCamera } from '@babylonjs/core/Cameras/universalCamera';
59
59
  import { DirectionalLight } from '@babylonjs/core/Lights/directionalLight';
@@ -98,7 +98,7 @@ import { ShadowOnlyMaterial } from '@babylonjs/materials/shadowOnly/shadowOnlyMa
98
98
  import { SkyMaterial } from '@babylonjs/materials/sky/skyMaterial';
99
99
  import { WaterMaterial } from '@babylonjs/materials/water/waterMaterial';
100
100
  export class SceneManager {
101
- static get Version() { return "9.12.1 - R1"; }
101
+ static get Version() { return "9.12.1011 - R1"; }
102
102
  static get Copyright() { return "All rights reserved (c) 2024 Mackey Kinard"; }
103
103
  static GetEngine(scene) {
104
104
  let result = null;
@@ -2824,9 +2824,15 @@ export class ScriptComponent {
2824
2824
  return SceneManager.GetSkinnedMesh(this.transform);
2825
2825
  }
2826
2826
  getTransformMesh() {
2827
+ const atransform = this.transform;
2828
+ if (atransform._skinnedMesh)
2829
+ return atransform._skinnedMesh;
2827
2830
  return (this._transform instanceof Mesh) ? this._transform : null;
2828
2831
  }
2829
2832
  getAbstractMesh() {
2833
+ const atransform = this.transform;
2834
+ if (atransform._skinnedMesh)
2835
+ return atransform._skinnedMesh;
2830
2836
  return (this._transform instanceof AbstractMesh) ? this._transform : null;
2831
2837
  }
2832
2838
  getInstancedMesh() {
@@ -3797,6 +3803,21 @@ export class CVTOOLS_unity_metadata {
3797
3803
  internal._sphericalPolynomialComputed = true;
3798
3804
  }
3799
3805
  }
3806
+ static ApplyGlobalReflectionProbeDiffuse(texture, scene, createPolynomialsFromFaces = false) {
3807
+ if (texture == null || createPolynomialsFromFaces === true)
3808
+ return;
3809
+ const sp = (scene != null) ? SceneManager.GetImageBasedLighting(scene) : null;
3810
+ if (sp == null) {
3811
+ CVTOOLS_unity_metadata.SuppressReflectionProbeDiffuse(texture);
3812
+ return;
3813
+ }
3814
+ texture.sphericalPolynomial = sp;
3815
+ const internal = texture._texture;
3816
+ if (internal != null) {
3817
+ internal._sphericalPolynomial = sp;
3818
+ internal._sphericalPolynomialComputed = true;
3819
+ }
3820
+ }
3800
3821
  constructor(loader) {
3801
3822
  this.name = SceneManager.CVTOOLS_NAME;
3802
3823
  this.enabled = false;
@@ -4154,6 +4175,7 @@ export class CVTOOLS_unity_metadata {
4154
4175
  }
4155
4176
  }
4156
4177
  }
4178
+ const createPolynomialsFromFaces = scene.extras?.metadata?.createpolynomials ?? false;
4157
4179
  if (this._reflectionMap != null && this._reflectionCache != null) {
4158
4180
  let tindex = 0;
4159
4181
  for (const key in this._reflectionMap) {
@@ -4173,7 +4195,7 @@ export class CVTOOLS_unity_metadata {
4173
4195
  if (cachedCubeTexture != null) {
4174
4196
  if (ctx.material != null && Utilities.HasOwnProperty(ctx.material, "reflectionTexture")) {
4175
4197
  ctx.material.reflectionTexture = cachedCubeTexture;
4176
- CVTOOLS_unity_metadata.SuppressReflectionProbeDiffuse(cachedCubeTexture);
4198
+ CVTOOLS_unity_metadata.ApplyGlobalReflectionProbeDiffuse(cachedCubeTexture, this._babylonScene, createPolynomialsFromFaces);
4177
4199
  }
4178
4200
  }
4179
4201
  else {
@@ -4186,8 +4208,8 @@ export class CVTOOLS_unity_metadata {
4186
4208
  buffer: bytes,
4187
4209
  prefiltered: true,
4188
4210
  forcedExtension: newCubeExtension,
4189
- createPolynomials: false,
4190
- onLoad: () => { CVTOOLS_unity_metadata.SuppressReflectionProbeDiffuse(newCubeTexture); },
4211
+ createPolynomials: createPolynomialsFromFaces,
4212
+ onLoad: () => { CVTOOLS_unity_metadata.ApplyGlobalReflectionProbeDiffuse(newCubeTexture, this._babylonScene, createPolynomialsFromFaces); },
4191
4213
  onError: (msg, ex) => console.error(msg, ex),
4192
4214
  });
4193
4215
  newCubeTexture.name = ctx.name;
@@ -4200,7 +4222,7 @@ export class CVTOOLS_unity_metadata {
4200
4222
  this._reflectionCache[ctx.mapkey] = newCubeTexture;
4201
4223
  if (ctx.material != null && Utilities.HasOwnProperty(ctx.material, "reflectionTexture")) {
4202
4224
  ctx.material.reflectionTexture = newCubeTexture;
4203
- CVTOOLS_unity_metadata.SuppressReflectionProbeDiffuse(newCubeTexture);
4225
+ CVTOOLS_unity_metadata.ApplyGlobalReflectionProbeDiffuse(newCubeTexture, this._babylonScene, createPolynomialsFromFaces);
4204
4226
  }
4205
4227
  }
4206
4228
  }
@@ -4605,28 +4627,19 @@ export class CVTOOLS_unity_metadata {
4605
4627
  }
4606
4628
  }
4607
4629
  if (ascene._AmbientLight != null) {
4608
- if (metadata.ambientskymode === "skybox") {
4609
- ascene._AmbientLight.name = "Ambient Skybox";
4610
- ascene._AmbientLight.diffuse = (metadata.ambientskycolor != null) ? Utilities.ParseColor3(metadata.ambientskycolor, Color3.Black()) : Color3.Black();
4630
+ ascene._AmbientLight.diffuse = (metadata.ambientskycolor != null) ? Utilities.ParseColor3(metadata.ambientskycolor, Color3.Gray()) : Color3.Gray();
4631
+ ascene._AmbientLight.specular = (metadata.ambientspecularcolor != null) ? Color3.Black() : Color3.Black();
4632
+ ascene._AmbientLight.groundColor = (metadata.ambientgroundcolor != null) ? Utilities.ParseColor3(metadata.ambientgroundcolor, Color3.Gray()) : Color3.Gray();
4633
+ ascene._AmbientLight.intensity = (metadata.ambientlightintensity != null) ? metadata.ambientlightintensity : 1.0;
4634
+ if (SceneManager.GetImageBasedLighting(babylonScene) != null) {
4635
+ ascene._AmbientLight.name = "Ambient Blacklight";
4636
+ ascene._AmbientLight.diffuse = Color3.Black();
4611
4637
  ascene._AmbientLight.specular = Color3.Black();
4612
- ascene._AmbientLight.groundColor = (metadata.ambientgroundcolor != null) ? Utilities.ParseColor3(metadata.ambientgroundcolor, Color3.Black()) : Color3.Black();
4613
- if (metadata.ambientlightintensity != null)
4614
- ascene._AmbientLight.intensity = metadata.ambientlightintensity;
4615
- ascene._AmbientLight.intensity *= SceneManager.AmbientLightIntensity;
4616
- ascene._AmbientLight.intensityMode = Light.INTENSITYMODE_ILLUMINANCE;
4617
- }
4618
- else {
4619
- if (metadata.ambientskycolor != null)
4620
- ascene._AmbientLight.diffuse = Utilities.ParseColor3(metadata.ambientskycolor, Color3.Gray());
4621
- if (metadata.ambientspecularcolor != null)
4622
- ascene._AmbientLight.specular = Color3.Black();
4623
- if (metadata.ambientgroundcolor != null)
4624
- ascene._AmbientLight.groundColor = Utilities.ParseColor3(metadata.ambientgroundcolor, Color3.Gray());
4625
- if (metadata.ambientlightintensity != null)
4626
- ascene._AmbientLight.intensity = (metadata.ambientlightintensity * 0.5);
4627
- ascene._AmbientLight.intensity *= SceneManager.AmbientLightIntensity;
4628
- ascene._AmbientLight.intensityMode = Light.INTENSITYMODE_ILLUMINANCE;
4638
+ ascene._AmbientLight.groundColor = Color3.Black();
4639
+ ascene._AmbientLight.intensity = 1.0;
4629
4640
  }
4641
+ ascene._AmbientLight.intensity *= SceneManager.AmbientLightIntensity;
4642
+ ascene._AmbientLight.intensityMode = Light.INTENSITYMODE_ILLUMINANCE;
4630
4643
  }
4631
4644
  }
4632
4645
  if (metadata.fogmode != null) {
@@ -4844,7 +4857,7 @@ export class CVTOOLS_unity_metadata {
4844
4857
  const IBL_MAX = 10.0;
4845
4858
  const LEVEL_MAX = 1000.0;
4846
4859
  const derivedRefl = (refl != null) ? refl : (ibl * lvl);
4847
- const diffuseIbl = 1.0;
4860
+ const diffuseIbl = Scalar.Clamp(ibl, 0.0, IBL_MAX);
4848
4861
  const specIbl = Scalar.Clamp(derivedRefl, 0.0, LEVEL_MAX);
4849
4862
  const hasDiffuseIbl = Array.isArray(environment.sh) && environment.sh.length === 27;
4850
4863
  const useUnitySH = (mode === 0 && hasDiffuseIbl === true);
@@ -5483,6 +5496,7 @@ export class CVTOOLS_unity_metadata {
5483
5496
  CVTOOLS_unity_metadata.IBL_TINY_VALUE = 0.001;
5484
5497
  CVTOOLS_unity_metadata.IBL_SPEC_FACTOR = 1.0;
5485
5498
  CVTOOLS_unity_metadata.IBL_PROBE_FACTOR = 1.0;
5499
+ CVTOOLS_unity_metadata._boxProjectionShaderPatched = false;
5486
5500
  CVTOOLS_unity_metadata.ScriptBundleCache = {};
5487
5501
  export class CVTOOLS_babylon_mesh {
5488
5502
  constructor(loader) {
@@ -5530,10 +5544,18 @@ export class CustomShaderMaterial extends PBRMaterial {
5530
5544
  this.shader = null;
5531
5545
  this.plugin = null;
5532
5546
  this._unityLightingPlugin = null;
5547
+ this._skinArray = false;
5548
+ this._skinArrayNormal = false;
5549
+ this._skinArrayMetalRough = false;
5550
+ this._skinArrayEmissive = false;
5551
+ this._skinLayerUniform = false;
5552
+ this._skinArraySwitchingPlugin = null;
5533
5553
  this.shader = "CustomShaderMaterial";
5534
5554
  this.addFloatUniform("tkIblDiffuseScale", 1.0);
5535
5555
  this.addFloatUniform("tkIblSpecularScale", 1.0);
5556
+ this.addFloatUniform("tkSkinLayerUni", 0.0);
5536
5557
  this._unityLightingPlugin = new UnityStyleLightingPlugin(this, this.shader);
5558
+ this._skinArraySwitchingPlugin = new SkinArraySwitchingPlugin(this);
5537
5559
  }
5538
5560
  initMaterial() {
5539
5561
  if (this["awake"])
@@ -5567,6 +5589,108 @@ export class CustomShaderMaterial extends PBRMaterial {
5567
5589
  this.plugin?.markAllDefinesAsDirty();
5568
5590
  this._unityLightingPlugin?.markAllDefinesAsDirty();
5569
5591
  }
5592
+ enableSkinArray(layerUniform = false) {
5593
+ this._skinArray = true;
5594
+ this._skinLayerUniform = layerUniform;
5595
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5596
+ this.plugin?.markAllDefinesAsDirty();
5597
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5598
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5599
+ return this;
5600
+ }
5601
+ setSkinLayer(layer) {
5602
+ this._skinLayerUniform = true;
5603
+ this.setFloatValue("tkSkinLayerUni", Math.max(0, Math.floor(layer)));
5604
+ return this;
5605
+ }
5606
+ enableSkinArrayNormal(enabled = true) {
5607
+ this._skinArrayNormal = enabled;
5608
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5609
+ this.plugin?.markAllDefinesAsDirty();
5610
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5611
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5612
+ return this;
5613
+ }
5614
+ enableSkinArrayMetalRough(enabled = true) {
5615
+ this._skinArrayMetalRough = enabled;
5616
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5617
+ this.plugin?.markAllDefinesAsDirty();
5618
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5619
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5620
+ return this;
5621
+ }
5622
+ enableSkinArrayEmissive(enabled = true) {
5623
+ this._skinArrayEmissive = enabled;
5624
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5625
+ this.plugin?.markAllDefinesAsDirty();
5626
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5627
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5628
+ return this;
5629
+ }
5630
+ setSkinArrayLayerMode(uniform) {
5631
+ this._skinLayerUniform = uniform;
5632
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5633
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5634
+ return this;
5635
+ }
5636
+ addTextureArrayUniform(name, texture) {
5637
+ if (this._samplers.indexOf(name) === -1)
5638
+ this._samplers.push(name);
5639
+ this._textures[name] = texture;
5640
+ this.markTextureAsRaw(name);
5641
+ return this;
5642
+ }
5643
+ get skinArray() { return this._skinArray; }
5644
+ get skinArrayNormal() { return this._skinArrayNormal; }
5645
+ get skinArrayMetalRough() { return this._skinArrayMetalRough; }
5646
+ get skinArrayEmissive() { return this._skinArrayEmissive; }
5647
+ get skinArrayActive() {
5648
+ return this._skinArray || this._skinArrayNormal || this._skinArrayMetalRough || this._skinArrayEmissive;
5649
+ }
5650
+ get skinLayerUniform() { return this._skinLayerUniform; }
5651
+ clone(name, cloneTexturesOnlyOnce = true, rootUrl = "") {
5652
+ const scene = this.getScene();
5653
+ const ctor = this.constructor;
5654
+ const result = SerializationHelper.Clone(() => new ctor(name, scene), this, { cloneTexturesOnlyOnce });
5655
+ if (result == null)
5656
+ return result;
5657
+ result.id = name;
5658
+ result.name = name;
5659
+ if (this.stencil && result.stencil)
5660
+ this.stencil.copyTo(result.stencil);
5661
+ this.copyCustomStateTo(result);
5662
+ return result;
5663
+ }
5664
+ copyCustomStateTo(result) {
5665
+ result.universalMaterial = this.universalMaterial;
5666
+ result._defines = Object.assign({}, this._defines);
5667
+ result._uniforms = this._uniforms.slice();
5668
+ result._samplers = this._samplers.slice();
5669
+ result._attributes = this._attributes.slice();
5670
+ result._ubos = (this._ubos != null) ? this._ubos.map((u) => ({ name: u.name, size: u.size, type: u.type, arraySize: u.arraySize })) : null;
5671
+ result._textures = Object.assign({}, this._textures);
5672
+ result._noMatrixTextures = (this._noMatrixTextures != null) ? new Set(this._noMatrixTextures) : null;
5673
+ result._vectors4 = {};
5674
+ for (const n in this._vectors4)
5675
+ result._vectors4[n] = this._vectors4[n].clone();
5676
+ result._vectors3 = {};
5677
+ for (const n in this._vectors3)
5678
+ result._vectors3[n] = this._vectors3[n].clone();
5679
+ result._vectors2 = {};
5680
+ for (const n in this._vectors2)
5681
+ result._vectors2[n] = this._vectors2[n].clone();
5682
+ result._floats = Object.assign({}, this._floats);
5683
+ result._bools = Object.assign({}, this._bools);
5684
+ result._skinArray = this._skinArray;
5685
+ result._skinArrayNormal = this._skinArrayNormal;
5686
+ result._skinArrayMetalRough = this._skinArrayMetalRough;
5687
+ result._skinArrayEmissive = this._skinArrayEmissive;
5688
+ result._skinLayerUniform = this._skinLayerUniform;
5689
+ result.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5690
+ result.plugin?.markAllDefinesAsDirty();
5691
+ result._unityLightingPlugin?.markAllDefinesAsDirty();
5692
+ result._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5693
+ }
5570
5694
  addTextureUniform(name, texture) {
5571
5695
  this.checkSampler(name, texture);
5572
5696
  this._textures[name] = texture;
@@ -6009,6 +6133,301 @@ export class UnityStyleLightingPlugin extends CustomShaderMaterialPlugin {
6009
6133
  this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
6010
6134
  }
6011
6135
  }
6136
+ export class SkinArraySwitchingPlugin extends CustomShaderMaterialPlugin {
6137
+ constructor(material) {
6138
+ super(material, "SkinArraySwitchingPlugin", 21, { TOOLKIT_SKIN_ARRAY: false, TOOLKIT_SKIN_LAYER_UNIFORM: false, TOOLKIT_SKIN_ARRAY_ALBEDO: false, TOOLKIT_SKIN_ARRAY_NORMAL: false, TOOLKIT_SKIN_ARRAY_MR: false, TOOLKIT_SKIN_ARRAY_EMISSIVE: false }, true, true, false);
6139
+ }
6140
+ isCompatible(shaderLanguage) {
6141
+ return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
6142
+ }
6143
+ getClassName() {
6144
+ return "SkinArraySwitchingPlugin";
6145
+ }
6146
+ getAttributes(attributes, scene, mesh) {
6147
+ const mat = this.getCustomShaderMaterial();
6148
+ if (!mat.skinArrayActive)
6149
+ return;
6150
+ if (mat.skinLayerUniform !== true && attributes.indexOf("tkSkinLayer") === -1)
6151
+ attributes.push("tkSkinLayer");
6152
+ }
6153
+ getSamplers(samplers) {
6154
+ const mat = this.getCustomShaderMaterial();
6155
+ if (!mat.skinArrayActive)
6156
+ return;
6157
+ const customSamplers = mat.getCustomSamplers();
6158
+ if (customSamplers != null) {
6159
+ for (const s of customSamplers)
6160
+ if (samplers.indexOf(s) === -1)
6161
+ samplers.push(s);
6162
+ }
6163
+ }
6164
+ prepareDefines(defines, scene, mesh) {
6165
+ const mat = this.getCustomShaderMaterial();
6166
+ defines.TOOLKIT_SKIN_ARRAY = !!mat.skinArrayActive;
6167
+ defines.TOOLKIT_SKIN_LAYER_UNIFORM = !!mat.skinLayerUniform;
6168
+ defines.TOOLKIT_SKIN_ARRAY_ALBEDO = !!mat.skinArray;
6169
+ defines.TOOLKIT_SKIN_ARRAY_NORMAL = !!mat.skinArrayNormal;
6170
+ defines.TOOLKIT_SKIN_ARRAY_MR = !!mat.skinArrayMetalRough;
6171
+ defines.TOOLKIT_SKIN_ARRAY_EMISSIVE = !!mat.skinArrayEmissive;
6172
+ }
6173
+ getCustomCode(shaderType, shaderLanguage) {
6174
+ const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
6175
+ if (shaderType === "vertex") {
6176
+ if (wgsl) {
6177
+ return {
6178
+ CUSTOM_VERTEX_DEFINITIONS: this.getWGSLVertexDefinitions(),
6179
+ CUSTOM_VERTEX_MAIN_END: this.getWGSLVertexMainEnd()
6180
+ };
6181
+ }
6182
+ return {
6183
+ CUSTOM_VERTEX_DEFINITIONS: this.getGLSLVertexDefinitions(),
6184
+ CUSTOM_VERTEX_MAIN_END: this.getGLSLVertexMainEnd()
6185
+ };
6186
+ }
6187
+ if (shaderType === "fragment") {
6188
+ if (wgsl) {
6189
+ return {
6190
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getWGSLFragmentDefinitions(),
6191
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getWGSLAlbedoCode() + this.getWGSLMetalRoughSampleCode(),
6192
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getWGSLNormalCode(),
6193
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getWGSLMetalRoughApplyCode(),
6194
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getWGSLEmissiveCode()
6195
+ };
6196
+ }
6197
+ return {
6198
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getGLSLFragmentDefinitions(),
6199
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getGLSLAlbedoCode() + this.getGLSLMetalRoughSampleCode(),
6200
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getGLSLNormalCode(),
6201
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getGLSLMetalRoughApplyCode(),
6202
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getGLSLEmissiveCode()
6203
+ };
6204
+ }
6205
+ return null;
6206
+ }
6207
+ getGLSLVertexDefinitions() {
6208
+ return `
6209
+ #ifdef TOOLKIT_SKIN_ARRAY
6210
+ #ifndef TOOLKIT_SKIN_V_DEFINED
6211
+ #define TOOLKIT_SKIN_V_DEFINED
6212
+ #ifdef TOOLKIT_SKIN_LAYER_UNIFORM
6213
+ uniform float tkSkinLayerUni;
6214
+ #else
6215
+ attribute float tkSkinLayer;
6216
+ #endif
6217
+ varying float vSkinLayer;
6218
+ varying vec2 vSkinUV;
6219
+ #endif
6220
+ #endif
6221
+ `;
6222
+ }
6223
+ getGLSLVertexMainEnd() {
6224
+ return `
6225
+ #ifdef TOOLKIT_SKIN_ARRAY
6226
+ #ifdef UV1
6227
+ #ifdef TOOLKIT_SKIN_LAYER_UNIFORM
6228
+ vSkinLayer = tkSkinLayerUni;
6229
+ #else
6230
+ vSkinLayer = tkSkinLayer;
6231
+ #endif
6232
+ vSkinUV = uv;
6233
+ #else
6234
+ vSkinLayer = 0.0;
6235
+ vSkinUV = vec2(0.0);
6236
+ #endif
6237
+ #endif
6238
+ `;
6239
+ }
6240
+ getGLSLFragmentDefinitions() {
6241
+ return `
6242
+ #ifdef TOOLKIT_SKIN_ARRAY
6243
+ #ifndef TOOLKIT_SKIN_F_DEFINED
6244
+ #define TOOLKIT_SKIN_F_DEFINED
6245
+ varying float vSkinLayer;
6246
+ varying vec2 vSkinUV;
6247
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6248
+ uniform sampler2DArray tkAlbedoArray;
6249
+ #endif
6250
+ #ifdef TOOLKIT_SKIN_ARRAY_NORMAL
6251
+ uniform sampler2DArray tkNormalArray;
6252
+ #endif
6253
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6254
+ uniform sampler2DArray tkMetalRoughArray;
6255
+ vec2 _tkMR;
6256
+ #endif
6257
+ #ifdef TOOLKIT_SKIN_ARRAY_EMISSIVE
6258
+ uniform sampler2DArray tkEmissiveArray;
6259
+ #endif
6260
+ #endif
6261
+ #endif
6262
+ `;
6263
+ }
6264
+ getGLSLAlbedoCode() {
6265
+ return `
6266
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6267
+ {
6268
+ float _tkLayer = floor(vSkinLayer + 0.5);
6269
+ vec4 _tkAlb = texture(tkAlbedoArray, vec3(vSkinUV, _tkLayer));
6270
+ #ifdef GAMMAALBEDO
6271
+ surfaceAlbedo = toLinearSpace(_tkAlb.rgb) * vAlbedoColor.rgb;
6272
+ #else
6273
+ surfaceAlbedo = _tkAlb.rgb * vAlbedoColor.rgb;
6274
+ #endif
6275
+ #ifdef ALPHATEST
6276
+ if (_tkAlb.a < alphaCutOff) discard;
6277
+ #endif
6278
+ }
6279
+ #endif
6280
+ `;
6281
+ }
6282
+ getGLSLNormalCode() {
6283
+ return `
6284
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
6285
+ {
6286
+ vec3 _tkNrmTexel = texture(tkNormalArray, vec3(vSkinUV, floor(vSkinLayer + 0.5))).xyz;
6287
+ normalW = perturbNormal(vTBN, _tkNrmTexel, vBumpInfos.y);
6288
+ }
6289
+ #endif
6290
+ `;
6291
+ }
6292
+ getGLSLMetalRoughSampleCode() {
6293
+ return `
6294
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6295
+ {
6296
+ vec4 _tkMRtexel = texture(tkMetalRoughArray, vec3(vSkinUV, floor(vSkinLayer + 0.5)));
6297
+ _tkMR = vec2(_tkMRtexel.b, _tkMRtexel.g);
6298
+ }
6299
+ #endif
6300
+ `;
6301
+ }
6302
+ getGLSLMetalRoughApplyCode() {
6303
+ return `
6304
+ #if defined(TOOLKIT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
6305
+ metallicRoughness.r = _tkMR.x;
6306
+ metallicRoughness.g = _tkMR.y;
6307
+ #endif
6308
+ `;
6309
+ }
6310
+ getGLSLEmissiveCode() {
6311
+ return `
6312
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_EMISSIVE)
6313
+ {
6314
+ vec3 _tkEmis = texture(tkEmissiveArray, vec3(vSkinUV, floor(vSkinLayer + 0.5))).rgb;
6315
+ finalEmissive = toLinearSpace(_tkEmis);
6316
+ }
6317
+ #endif
6318
+ `;
6319
+ }
6320
+ getWGSLVertexDefinitions() {
6321
+ return `
6322
+ #ifdef TOOLKIT_SKIN_ARRAY
6323
+ #ifndef TOOLKIT_SKIN_LAYER_UNIFORM
6324
+ attribute tkSkinLayer: f32;
6325
+ #endif
6326
+ varying vSkinLayer: f32;
6327
+ varying vSkinUV: vec2<f32>;
6328
+ #endif
6329
+ `;
6330
+ }
6331
+ getWGSLVertexMainEnd() {
6332
+ return `
6333
+ #ifdef TOOLKIT_SKIN_ARRAY
6334
+ #ifdef UV1
6335
+ #ifdef TOOLKIT_SKIN_LAYER_UNIFORM
6336
+ vertexOutputs.vSkinLayer = uniforms.tkSkinLayerUni;
6337
+ #else
6338
+ vertexOutputs.vSkinLayer = vertexInputs.tkSkinLayer;
6339
+ #endif
6340
+ vertexOutputs.vSkinUV = vertexInputs.uv;
6341
+ #else
6342
+ vertexOutputs.vSkinLayer = 0.0;
6343
+ vertexOutputs.vSkinUV = vec2f(0.0);
6344
+ #endif
6345
+ #endif
6346
+ `;
6347
+ }
6348
+ getWGSLFragmentDefinitions() {
6349
+ return `
6350
+ #ifdef TOOLKIT_SKIN_ARRAY
6351
+ varying vSkinLayer: f32;
6352
+ varying vSkinUV: vec2<f32>;
6353
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6354
+ var tkAlbedoArray: texture_2d_array<f32>;
6355
+ var tkAlbedoArraySampler: sampler;
6356
+ #endif
6357
+ #ifdef TOOLKIT_SKIN_ARRAY_NORMAL
6358
+ var tkNormalArray: texture_2d_array<f32>;
6359
+ var tkNormalArraySampler: sampler;
6360
+ #endif
6361
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6362
+ var tkMetalRoughArray: texture_2d_array<f32>;
6363
+ var tkMetalRoughArraySampler: sampler;
6364
+ var<private> _tkMR: vec2<f32>;
6365
+ #endif
6366
+ #ifdef TOOLKIT_SKIN_ARRAY_EMISSIVE
6367
+ var tkEmissiveArray: texture_2d_array<f32>;
6368
+ var tkEmissiveArraySampler: sampler;
6369
+ #endif
6370
+ #endif
6371
+ `;
6372
+ }
6373
+ getWGSLAlbedoCode() {
6374
+ return `
6375
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6376
+ {
6377
+ let _tkLayer = i32(fragmentInputs.vSkinLayer + 0.5);
6378
+ let _tkAlb = textureSample(tkAlbedoArray, tkAlbedoArraySampler, fragmentInputs.vSkinUV, _tkLayer);
6379
+ #ifdef GAMMAALBEDO
6380
+ surfaceAlbedo = toLinearSpaceVec3(_tkAlb.rgb) * vAlbedoColor.rgb;
6381
+ #else
6382
+ surfaceAlbedo = _tkAlb.rgb * vAlbedoColor.rgb;
6383
+ #endif
6384
+ #ifdef ALPHATEST
6385
+ if (_tkAlb.a < alphaCutOff) { discard; }
6386
+ #endif
6387
+ }
6388
+ #endif
6389
+ `;
6390
+ }
6391
+ getWGSLNormalCode() {
6392
+ return `
6393
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
6394
+ {
6395
+ let _tkNrmTexel = textureSample(tkNormalArray, tkNormalArraySampler, fragmentInputs.vSkinUV, i32(fragmentInputs.vSkinLayer + 0.5)).rgb;
6396
+ let _tkNrmTBN = mat3x3<f32>(fragmentInputs.vTBN0, fragmentInputs.vTBN1, fragmentInputs.vTBN2);
6397
+ normalW = perturbNormal(_tkNrmTBN, _tkNrmTexel, uniforms.vBumpInfos.y);
6398
+ }
6399
+ #endif
6400
+ `;
6401
+ }
6402
+ getWGSLMetalRoughSampleCode() {
6403
+ return `
6404
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6405
+ {
6406
+ let _tkMRtexel = textureSample(tkMetalRoughArray, tkMetalRoughArraySampler, fragmentInputs.vSkinUV, i32(fragmentInputs.vSkinLayer + 0.5));
6407
+ _tkMR = vec2<f32>(_tkMRtexel.b, _tkMRtexel.g);
6408
+ }
6409
+ #endif
6410
+ `;
6411
+ }
6412
+ getWGSLMetalRoughApplyCode() {
6413
+ return `
6414
+ #if defined(TOOLKIT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
6415
+ metallicRoughness.r = _tkMR.x;
6416
+ metallicRoughness.g = _tkMR.y;
6417
+ #endif
6418
+ `;
6419
+ }
6420
+ getWGSLEmissiveCode() {
6421
+ return `
6422
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_EMISSIVE)
6423
+ {
6424
+ let _tkEmis = textureSample(tkEmissiveArray, tkEmissiveArraySampler, fragmentInputs.vSkinUV, i32(fragmentInputs.vSkinLayer + 0.5)).rgb;
6425
+ finalEmissive = toLinearSpaceVec3(_tkEmis);
6426
+ }
6427
+ #endif
6428
+ `;
6429
+ }
6430
+ }
6012
6431
  export class UniversalShaderMaterial extends CustomShaderMaterial {
6013
6432
  constructor(name, scene) {
6014
6433
  super(name, scene);
@@ -11736,8 +12155,10 @@ export class VertexAnimationMaterial extends CustomShaderMaterial {
11736
12155
  constructor(name, scene) {
11737
12156
  super(name, scene);
11738
12157
  this.controller = null;
11739
- this.runtimeAlbedoSkins = null;
11740
- this.runtimeNormalSkins = null;
12158
+ this._vatSkinArray = false;
12159
+ this._vatSkinArrayNormal = false;
12160
+ this._vatSkinArrayMetalRough = false;
12161
+ this._vatSkinArrayEmissive = false;
11741
12162
  this.shader = this.getShaderName();
11742
12163
  this.addAttribute("g_vatAnim0");
11743
12164
  this.addAttribute("g_vatAnim1");
@@ -11793,97 +12214,90 @@ export class VertexAnimationMaterial extends CustomShaderMaterial {
11793
12214
  cloned.alpha = this.alpha;
11794
12215
  cloned.alphaMode = this.alphaMode;
11795
12216
  cloned.transparencyMode = this.transparencyMode;
11796
- cloned.runtimeAlbedoSkins = this.runtimeAlbedoSkins;
11797
- cloned.runtimeNormalSkins = this.runtimeNormalSkins;
11798
- cloned._notifyPluginOfRuntimeSkinChange();
12217
+ if (this._vatSkinArray) {
12218
+ const arr = this.getTextureValue("tkAlbedoArray");
12219
+ if (arr != null)
12220
+ cloned.addTextureArrayUniform("tkAlbedoArray", arr);
12221
+ cloned.enableVatSkinArray();
12222
+ }
12223
+ if (this._vatSkinArrayNormal) {
12224
+ const narr = this.getTextureValue("tkNormalArray");
12225
+ if (narr != null)
12226
+ cloned.addTextureArrayUniform("tkNormalArray", narr);
12227
+ cloned.enableVatSkinArrayNormal();
12228
+ }
12229
+ if (this._vatSkinArrayMetalRough) {
12230
+ const mrarr = this.getTextureValue("tkMetalRoughArray");
12231
+ if (mrarr != null)
12232
+ cloned.addTextureArrayUniform("tkMetalRoughArray", mrarr);
12233
+ cloned.enableVatSkinArrayMetalRough();
12234
+ }
12235
+ if (this._vatSkinArrayEmissive) {
12236
+ const earr = this.getTextureValue("tkEmissiveArray");
12237
+ if (earr != null)
12238
+ cloned.addTextureArrayUniform("tkEmissiveArray", earr);
12239
+ cloned.enableVatSkinArrayEmissive();
12240
+ }
11799
12241
  return cloned;
11800
12242
  }
11801
- setRuntimeSkinArrays(albedoArray, normalArray, nullOutDefaults = true) {
11802
- this.runtimeAlbedoSkins = albedoArray || null;
11803
- this.runtimeNormalSkins = normalArray || null;
11804
- if (nullOutDefaults) {
11805
- this.albedoTexture = null;
11806
- this.bumpTexture = null;
11807
- }
11808
- this._notifyPluginOfRuntimeSkinChange();
12243
+ enableVatSkinArray() {
12244
+ this._vatSkinArray = true;
12245
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12246
+ if (this.plugin)
12247
+ this.plugin.markAllDefinesAsDirty();
12248
+ return this;
11809
12249
  }
11810
- clearRuntimeSkinArrays() {
11811
- this.runtimeAlbedoSkins = null;
11812
- this.runtimeNormalSkins = null;
11813
- this._notifyPluginOfRuntimeSkinChange();
12250
+ disableVatSkinArray() {
12251
+ this._vatSkinArray = false;
12252
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12253
+ if (this.plugin)
12254
+ this.plugin.markAllDefinesAsDirty();
11814
12255
  }
11815
- async installSkinsFromUrlsAsync(layers, width, height, options) {
11816
- if (!layers || layers.length === 0) {
11817
- throw new Error("VertexAnimationMaterial.installSkinsFromUrlsAsync: layers list is empty.");
11818
- }
11819
- const opts = options || {};
11820
- const nullOutDefaults = opts.nullOutDefaults !== false;
11821
- const rootUrl = opts.rootUrl || "";
11822
- const generateMipMaps = opts.generateMipMaps !== false;
11823
- const samplingMode = (opts.samplingMode != null) ? opts.samplingMode : Texture.TRILINEAR_SAMPLINGMODE;
11824
- const scene = this.getScene();
11825
- const layerCount = layers.length;
11826
- const bytesPerLayer = width * height * 4;
11827
- const albedoData = new Uint8Array(bytesPerLayer * layerCount);
11828
- const normalData = new Uint8Array(bytesPerLayer * layerCount);
11829
- const resolveUrl = (rel) => {
11830
- if (!rel)
11831
- return "";
11832
- if (rel.indexOf("://") >= 0 || rel.indexOf("blob:") === 0 || rel.indexOf("data:") === 0 || rel.indexOf("/") === 0)
11833
- return rel;
11834
- if (rootUrl) {
11835
- const sep = rootUrl.charAt(rootUrl.length - 1) === "/" ? "" : "/";
11836
- return rootUrl + sep + rel;
11837
- }
11838
- return rel;
11839
- };
11840
- const loadAndCopy = (url, dst, layerIdx, label) => {
11841
- return new Promise((resolve, reject) => {
11842
- const fullUrl = resolveUrl(url);
11843
- const tex = new Texture(fullUrl, scene, true, true, samplingMode, () => {
11844
- const size = tex.getSize();
11845
- if (size.width !== width || size.height !== height) {
11846
- tex.dispose();
11847
- reject(new Error(`${label} '${fullUrl}' is ${size.width}x${size.height}, expected ${width}x${height}.`));
11848
- return;
11849
- }
11850
- const pixelsPromise = tex.readPixels();
11851
- const onPixels = (pixels) => {
11852
- if (!pixels) {
11853
- tex.dispose();
11854
- reject(new Error(`${label} '${fullUrl}': readPixels returned null.`));
11855
- return;
11856
- }
11857
- dst.set(new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength), layerIdx * bytesPerLayer);
11858
- tex.dispose();
11859
- resolve();
11860
- };
11861
- if (pixelsPromise && typeof pixelsPromise.then === "function") {
11862
- pixelsPromise.then(onPixels).catch((e) => { tex.dispose(); reject(e); });
11863
- }
11864
- else {
11865
- onPixels(pixelsPromise);
11866
- }
11867
- }, (msg) => reject(new Error(`${label} '${fullUrl}' load failed: ${msg}`)));
11868
- });
11869
- };
11870
- const tasks = [];
11871
- for (let i = 0; i < layerCount; i++) {
11872
- tasks.push(loadAndCopy(layers[i].albedo, albedoData, i, "Skin albedo"));
11873
- tasks.push(loadAndCopy(layers[i].normal, normalData, i, "Skin normal"));
11874
- }
11875
- await Promise.all(tasks);
11876
- const albedoArray = RawTexture2DArray.CreateRGBATexture(albedoData, width, height, layerCount, scene, generateMipMaps, false, samplingMode);
11877
- const normalArray = RawTexture2DArray.CreateRGBATexture(normalData, width, height, layerCount, scene, generateMipMaps, false, samplingMode);
11878
- normalArray.gammaSpace = false;
11879
- this.setRuntimeSkinArrays(albedoArray, normalArray, nullOutDefaults);
11880
- }
11881
- _notifyPluginOfRuntimeSkinChange() {
11882
- const plug = this.plugin;
11883
- if (plug && typeof plug.onRuntimeSkinArraysChanged === "function") {
11884
- plug.onRuntimeSkinArraysChanged();
11885
- }
12256
+ enableVatSkinArrayNormal() {
12257
+ this._vatSkinArrayNormal = true;
12258
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12259
+ if (this.plugin)
12260
+ this.plugin.markAllDefinesAsDirty();
12261
+ return this;
12262
+ }
12263
+ disableVatSkinArrayNormal() {
12264
+ this._vatSkinArrayNormal = false;
11886
12265
  this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12266
+ if (this.plugin)
12267
+ this.plugin.markAllDefinesAsDirty();
12268
+ }
12269
+ enableVatSkinArrayMetalRough() {
12270
+ this._vatSkinArrayMetalRough = true;
12271
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12272
+ if (this.plugin)
12273
+ this.plugin.markAllDefinesAsDirty();
12274
+ return this;
12275
+ }
12276
+ disableVatSkinArrayMetalRough() {
12277
+ this._vatSkinArrayMetalRough = false;
12278
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12279
+ if (this.plugin)
12280
+ this.plugin.markAllDefinesAsDirty();
12281
+ }
12282
+ enableVatSkinArrayEmissive() {
12283
+ this._vatSkinArrayEmissive = true;
12284
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12285
+ if (this.plugin)
12286
+ this.plugin.markAllDefinesAsDirty();
12287
+ return this;
12288
+ }
12289
+ disableVatSkinArrayEmissive() {
12290
+ this._vatSkinArrayEmissive = false;
12291
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12292
+ if (this.plugin)
12293
+ this.plugin.markAllDefinesAsDirty();
12294
+ }
12295
+ get vatSkinArray() { return this._vatSkinArray; }
12296
+ get vatSkinArrayNormal() { return this._vatSkinArrayNormal; }
12297
+ get vatSkinArrayMetalRough() { return this._vatSkinArrayMetalRough; }
12298
+ get vatSkinArrayEmissive() { return this._vatSkinArrayEmissive; }
12299
+ get vatSkinArrayActive() {
12300
+ return this._vatSkinArray || this._vatSkinArrayNormal || this._vatSkinArrayMetalRough || this._vatSkinArrayEmissive;
11887
12301
  }
11888
12302
  }
11889
12303
  export class VertexAnimationController {
@@ -11918,15 +12332,15 @@ export class VertexAnimationController {
11918
12332
  }
11919
12333
  return result;
11920
12334
  }
11921
- static SetSkinIndex(mesh, skinIndex) {
12335
+ static SetAtlasCellIndex(mesh, cellIndex) {
11922
12336
  if (!mesh)
11923
12337
  return;
11924
- mesh.runtimeSkinIndex = Math.max(0, Math.floor(skinIndex || 0));
12338
+ mesh.atlasCellIndex = Math.max(0, Math.floor(cellIndex || 0));
11925
12339
  }
11926
- static GetSkinIndex(mesh) {
12340
+ static GetAtlasCellIndex(mesh) {
11927
12341
  if (!mesh)
11928
12342
  return 0;
11929
- const v = mesh.runtimeSkinIndex;
12343
+ const v = mesh.atlasCellIndex;
11930
12344
  return (typeof v === "number" && isFinite(v) && v >= 0) ? Math.floor(v) : 0;
11931
12345
  }
11932
12346
  static DisposeAll(scene) {
@@ -12010,39 +12424,6 @@ export class VertexAnimationController {
12010
12424
  }
12011
12425
  setSpeed(speed) { this._speed = speed; }
12012
12426
  setLoop(loop) { this._manualLoopOverride = loop; this._loop = loop; }
12013
- setRuntimeSkinArrays(albedoArray, normalArray, nullOutDefaults = true) {
12014
- for (let i = 0; i < this._plugins.length; i++) {
12015
- const plugin = this._plugins[i];
12016
- const mat = plugin.getCustomShaderMaterial();
12017
- if (mat)
12018
- mat.setRuntimeSkinArrays(albedoArray, normalArray, nullOutDefaults);
12019
- }
12020
- }
12021
- clearRuntimeSkinArrays() {
12022
- for (let i = 0; i < this._plugins.length; i++) {
12023
- const plugin = this._plugins[i];
12024
- const mat = plugin.getCustomShaderMaterial();
12025
- if (mat)
12026
- mat.clearRuntimeSkinArrays();
12027
- }
12028
- }
12029
- async installSkinsFromUrlsAsync(layers, width, height, options) {
12030
- if (this._plugins.length === 0) {
12031
- throw new Error("VertexAnimationController.installSkinsFromUrlsAsync: no registered VAT materials.");
12032
- }
12033
- const opts = options || {};
12034
- const effectiveOptions = { ...opts, rootUrl: opts.rootUrl || this.rootUrl };
12035
- const nullOutDefaults = opts.nullOutDefaults !== false;
12036
- const firstMat = this._plugins[0].getCustomShaderMaterial();
12037
- await firstMat.installSkinsFromUrlsAsync(layers, width, height, effectiveOptions);
12038
- const sharedAlbedo = firstMat.runtimeAlbedoSkins;
12039
- const sharedNormal = firstMat.runtimeNormalSkins;
12040
- for (let i = 1; i < this._plugins.length; i++) {
12041
- const mat = this._plugins[i].getCustomShaderMaterial();
12042
- if (mat)
12043
- mat.setRuntimeSkinArrays(sharedAlbedo, sharedNormal, nullOutDefaults);
12044
- }
12045
- }
12046
12427
  setCurrentTime(time) {
12047
12428
  if (!this._currentClip)
12048
12429
  return;
@@ -12252,9 +12633,9 @@ export class VertexAnimationController {
12252
12633
  const s = current.settings;
12253
12634
  anim0.set(this._time * s.vertexfps, s.vertexframes, current.fromRow, this._blendWeight);
12254
12635
  const loopFlag = this._loopBlend ? 1.0 : 0.0;
12255
- const meshSkinRaw = imesh.runtimeSkinIndex;
12256
- const meshSkin = (typeof meshSkinRaw === "number" && meshSkinRaw >= 0) ? meshSkinRaw : 0;
12257
- anim1.w = meshSkin * 2.0 + loopFlag;
12636
+ const cellRaw = imesh.atlasCellIndex;
12637
+ const cell = (typeof cellRaw === "number" && cellRaw >= 0) ? Math.floor(cellRaw) : 0;
12638
+ anim1.w = cell * 2.0 + loopFlag;
12258
12639
  const prev = this.getPreviousRendererClip(rendererGuid);
12259
12640
  if (prev && this._previousClip && this._blendWeight < 0.999 && this._isRendererPositionReady(prev)) {
12260
12641
  const ps = prev.settings;
@@ -12424,17 +12805,17 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12424
12805
  super(customMaterial, shaderName, 100, {
12425
12806
  VERTEXANIMATIONMATERIAL: false,
12426
12807
  VAT_NORMAL_SEPARATE: false,
12427
- RUNTIME_SKINS_ALBEDO: false,
12428
- RUNTIME_SKINS_NORMAL: false,
12429
- RUNTIME_SKINS_NORMAL_DERIVE_TBN: false
12808
+ VAT_SKIN_ARRAY: false,
12809
+ VAT_SKIN_ARRAY_ALBEDO: false,
12810
+ VAT_SKIN_ARRAY_NORMAL: false,
12811
+ VAT_SKIN_ARRAY_MR: false,
12812
+ VAT_SKIN_ARRAY_EMISSIVE: false
12430
12813
  });
12431
12814
  this.vertexAnimations = null;
12432
12815
  this.controller = null;
12433
12816
  this._rendererTargets = [];
12434
12817
  this._targetRendererGuid = "";
12435
12818
  this._placeholderTexture = null;
12436
- this._placeholderAlbedoArray = null;
12437
- this._placeholderNormalArray = null;
12438
12819
  this._cc_posTex = null;
12439
12820
  this._cc_normTex = null;
12440
12821
  this._cc_prevPosTex = null;
@@ -12456,24 +12837,12 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12456
12837
  customMaterial.setTextureValue("vatNormal", this._placeholderTexture);
12457
12838
  customMaterial.setTextureValue("vatPrevPosition", this._placeholderTexture);
12458
12839
  customMaterial.setTextureValue("vatPrevNormal", this._placeholderTexture);
12459
- const greyArr = new Uint8Array([128, 128, 128, 255]);
12460
- const flatNormalArr = new Uint8Array([128, 128, 255, 255]);
12461
- this._placeholderAlbedoArray = RawTexture2DArray.CreateRGBATexture(greyArr, 1, 1, 1, scene, false, false, Texture.NEAREST_SAMPLINGMODE);
12462
- this._placeholderNormalArray = RawTexture2DArray.CreateRGBATexture(flatNormalArr, 1, 1, 1, scene, false, false, Texture.NEAREST_SAMPLINGMODE);
12463
12840
  }
12464
12841
  dispose() {
12465
12842
  if (this._placeholderTexture) {
12466
12843
  this._placeholderTexture.dispose();
12467
12844
  this._placeholderTexture = null;
12468
12845
  }
12469
- if (this._placeholderAlbedoArray) {
12470
- this._placeholderAlbedoArray.dispose();
12471
- this._placeholderAlbedoArray = null;
12472
- }
12473
- if (this._placeholderNormalArray) {
12474
- this._placeholderNormalArray.dispose();
12475
- this._placeholderNormalArray = null;
12476
- }
12477
12846
  super.dispose();
12478
12847
  }
12479
12848
  isCompatible(shaderLanguage) {
@@ -12487,28 +12856,32 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12487
12856
  CUSTOM_VERTEX_DEFINITIONS: this.getWGSLVertexDefinitions(),
12488
12857
  CUSTOM_VERTEX_UPDATE_POSITION: this.getWGSLVertexPositionCode(),
12489
12858
  CUSTOM_VERTEX_UPDATE_NORMAL: this.getWGSLVertexNormalCode(),
12490
- CUSTOM_VERTEX_MAIN_END: this.getWGSLVertexMainEnd()
12859
+ CUSTOM_VERTEX_MAIN_END: this.getWGSLSkinLayerVaryingCode()
12491
12860
  };
12492
12861
  }
12493
12862
  return {
12494
12863
  CUSTOM_VERTEX_DEFINITIONS: this.getGLSLVertexDefinitions(),
12495
12864
  CUSTOM_VERTEX_UPDATE_POSITION: this.getGLSLVertexPositionCode(),
12496
12865
  CUSTOM_VERTEX_UPDATE_NORMAL: this.getGLSLVertexNormalCode(),
12497
- CUSTOM_VERTEX_MAIN_END: this.getGLSLVertexMainEnd()
12866
+ CUSTOM_VERTEX_MAIN_END: this.getGLSLSkinLayerVaryingCode()
12498
12867
  };
12499
12868
  }
12500
12869
  if (shaderType === "fragment") {
12501
12870
  if (wgsl) {
12502
12871
  return {
12503
- CUSTOM_FRAGMENT_DEFINITIONS: this.getWGSLFragmentDefinitions(),
12504
- CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getWGSLFragmentAlbedoCode(),
12505
- CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getWGSLFragmentNormalCode()
12872
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getWGSLSkinFragmentDefs(),
12873
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getWGSLSkinAlbedoCode() + this.getWGSLSkinMetalRoughSampleCode(),
12874
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getWGSLSkinNormalCode(),
12875
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getWGSLSkinMetalRoughApplyCode(),
12876
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getWGSLSkinEmissiveCode()
12506
12877
  };
12507
12878
  }
12508
12879
  return {
12509
- CUSTOM_FRAGMENT_DEFINITIONS: this.getGLSLFragmentDefinitions(),
12510
- CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getGLSLFragmentAlbedoCode(),
12511
- CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getGLSLFragmentNormalCode()
12880
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getGLSLSkinFragmentDefs(),
12881
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getGLSLSkinAlbedoCode() + this.getGLSLSkinMetalRoughSampleCode(),
12882
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getGLSLSkinNormalCode(),
12883
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getGLSLSkinMetalRoughApplyCode(),
12884
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getGLSLSkinEmissiveCode()
12512
12885
  };
12513
12886
  }
12514
12887
  return null;
@@ -12523,13 +12896,6 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12523
12896
  const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
12524
12897
  if (customSamplers != null && customSamplers.length > 0)
12525
12898
  samplers.push(...customSamplers);
12526
- const mat = this.getCustomShaderMaterial();
12527
- if (mat) {
12528
- if (samplers.indexOf("runtimeAlbedoSkins") === -1)
12529
- samplers.push("runtimeAlbedoSkins");
12530
- if (samplers.indexOf("runtimeNormalSkins") === -1)
12531
- samplers.push("runtimeNormalSkins");
12532
- }
12533
12899
  }
12534
12900
  getAttributes(attributes, scene, mesh) {
12535
12901
  const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
@@ -12545,15 +12911,12 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12545
12911
  defines.VAT_NORMAL_SEPARATE = (mode === "separate");
12546
12912
  const engine = scene ? scene.getEngine() : null;
12547
12913
  defines.UV3 = !(engine && engine.isWebGPU);
12548
- const mat = this.getCustomShaderMaterial();
12549
- const hasAlbedoArr = !!(mat && mat.runtimeAlbedoSkins);
12550
- const hasNormalArr = !!(mat && mat.runtimeNormalSkins);
12551
- defines.RUNTIME_SKINS_ALBEDO = hasAlbedoArr;
12552
- defines.RUNTIME_SKINS_NORMAL = hasNormalArr;
12553
- defines.RUNTIME_SKINS_NORMAL_DERIVE_TBN = hasNormalArr && !(mat && mat.bumpTexture);
12554
- }
12555
- onRuntimeSkinArraysChanged() {
12556
- this.markAllDefinesAsDirty();
12914
+ const matSkin = this.getCustomShaderMaterial();
12915
+ defines.VAT_SKIN_ARRAY = !!(matSkin && matSkin.vatSkinArrayActive);
12916
+ defines.VAT_SKIN_ARRAY_ALBEDO = !!(matSkin && matSkin.vatSkinArray);
12917
+ defines.VAT_SKIN_ARRAY_NORMAL = !!(matSkin && matSkin.vatSkinArrayNormal);
12918
+ defines.VAT_SKIN_ARRAY_MR = !!(matSkin && matSkin.vatSkinArrayMetalRough);
12919
+ defines.VAT_SKIN_ARRAY_EMISSIVE = !!(matSkin && matSkin.vatSkinArrayEmissive);
12557
12920
  }
12558
12921
  bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
12559
12922
  if (!this.getIsEnabled())
@@ -12561,16 +12924,6 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12561
12924
  const renderMesh = subMesh && subMesh.getRenderingMesh ? subMesh.getRenderingMesh() : null;
12562
12925
  this._captureRendererTarget(renderMesh || (subMesh ? subMesh.getMesh() : null));
12563
12926
  this._pushVATUniforms(uniformBuffer, false);
12564
- this._pushRuntimeSkinSamplers(uniformBuffer);
12565
- }
12566
- _pushRuntimeSkinSamplers(buf) {
12567
- const mat = this.getCustomShaderMaterial();
12568
- const albedoArr = (mat && mat.runtimeAlbedoSkins && mat.runtimeAlbedoSkins.isReady && mat.runtimeAlbedoSkins.isReady())
12569
- ? mat.runtimeAlbedoSkins : this._placeholderAlbedoArray;
12570
- const normalArr = (mat && mat.runtimeNormalSkins && mat.runtimeNormalSkins.isReady && mat.runtimeNormalSkins.isReady())
12571
- ? mat.runtimeNormalSkins : this._placeholderNormalArray;
12572
- buf.setTexture("runtimeAlbedoSkins", albedoArr);
12573
- buf.setTexture("runtimeNormalSkins", normalArr);
12574
12927
  }
12575
12928
  setupAnimations(settings, controllerId, rootUrl = "", lazyLoadTextures = false) {
12576
12929
  this.vertexAnimations = settings;
@@ -12823,7 +13176,6 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12823
13176
  }
12824
13177
  bindVATToEffect(effect) {
12825
13178
  this._pushVATUniforms(effect, true);
12826
- this._pushRuntimeSkinSamplers(effect);
12827
13179
  }
12828
13180
  _pushVATUniforms(buf, isEffect) {
12829
13181
  const fb = this._placeholderTexture;
@@ -12896,13 +13248,12 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12896
13248
  uniform sampler2D vatPrevPosition;
12897
13249
  uniform sampler2D vatPrevNormal;
12898
13250
  #endif
12899
- // Runtime-skin varyings — pass packed skin index + UV0 to the fragment stage so
12900
- // fragment can sample runtimeAlbedoSkins[layer]/runtimeNormalSkins[layer]. Always
12901
- // emitted (cheap) so the shader compiles regardless of the runtime-skin defines.
12902
- #ifndef TOOLKIT_VAT_RUNTIMESKIN_VARYINGS
12903
- #define TOOLKIT_VAT_RUNTIMESKIN_VARYINGS
12904
- varying float vRuntimeSkinIndex;
12905
- varying vec2 vRuntimeSkinUV;
13251
+ #ifdef VAT_SKIN_ARRAY
13252
+ #ifndef TOOLKIT_VAT_SKIN_V_DEFINED
13253
+ #define TOOLKIT_VAT_SKIN_V_DEFINED
13254
+ varying float vVatSkinLayer;
13255
+ varying vec2 vVatSkinUV;
13256
+ #endif
12906
13257
  #endif
12907
13258
  `;
12908
13259
  }
@@ -12916,8 +13267,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12916
13267
  float vatPrevFrame = g_vatAnim1.x;
12917
13268
  float vatPrevFrameCount = g_vatAnim1.y;
12918
13269
  float vatPrevFromRow = g_vatAnim1.z;
12919
- float vatPackedSkinLoop = g_vatAnim1.w;
12920
- float vatLoopBlend = mod(vatPackedSkinLoop, 2.0);
13270
+ #ifdef VAT_SKIN_ARRAY
13271
+ float vatLoopBlend = mod(g_vatAnim1.w, 2.0); // low bit is loop-blend, high bits carry the skin layer index
13272
+ #else
13273
+ float vatLoopBlend = g_vatAnim1.w;
13274
+ #endif
12921
13275
  if (vatFrameCount > 0.5) {
12922
13276
  // GLSL path uses the third UV channel:
12923
13277
  // Unity uv3 -> glTF TEXCOORD_2 -> Babylon shader uv3
@@ -12985,9 +13339,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12985
13339
  float vatNormFrame = g_vatAnim0.x;
12986
13340
  float vatNormFrameCount = g_vatAnim0.y;
12987
13341
  float vatNormFromRow = g_vatAnim0.z;
12988
-
12989
- float vatNormPackedSkinLoop = g_vatAnim1.w;
12990
- float vatNormLoopBlend = mod(vatNormPackedSkinLoop, 2.0);
13342
+ #ifdef VAT_SKIN_ARRAY
13343
+ float vatNormLoopBlend = mod(g_vatAnim1.w, 2.0);
13344
+ #else
13345
+ float vatNormLoopBlend = g_vatAnim1.w;
13346
+ #endif
12991
13347
 
12992
13348
  if (vatNormFrameCount > 0.5) {
12993
13349
  // GLSL path uses the third UV channel:
@@ -13033,9 +13389,10 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13033
13389
  var vatPrevPositionSampler: sampler;
13034
13390
  var vatPrevNormal: texture_2d<f32>;
13035
13391
  var vatPrevNormalSampler: sampler;
13036
- // Runtime-skin varyings — packed skin index and UV0, passed to fragment.
13037
- varying vRuntimeSkinIndex: f32;
13038
- varying vRuntimeSkinUV: vec2<f32>;
13392
+ #endif
13393
+ #ifdef VAT_SKIN_ARRAY
13394
+ varying vVatSkinLayer: f32;
13395
+ varying vVatSkinUV: vec2<f32>;
13039
13396
  #endif
13040
13397
  `;
13041
13398
  }
@@ -13049,8 +13406,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13049
13406
  let vatPrevFrame = vertexInputs.g_vatAnim1.x;
13050
13407
  let vatPrevFrameCount = vertexInputs.g_vatAnim1.y;
13051
13408
  let vatPrevFromRow = vertexInputs.g_vatAnim1.z;
13052
- let packedSkinLoop = vertexInputs.g_vatAnim1.w;
13053
- let vatLoopBlend = packedSkinLoop % 2.0;
13409
+ #ifdef VAT_SKIN_ARRAY
13410
+ let vatLoopBlend = vertexInputs.g_vatAnim1.w % 2.0; // low bit is loop-blend, high bits carry the skin layer index
13411
+ #else
13412
+ let vatLoopBlend = vertexInputs.g_vatAnim1.w;
13413
+ #endif
13054
13414
  if (vatFrameCount > 0.5) {
13055
13415
  // WGSL path derives the VAT texel coordinate from the vertex index instead
13056
13416
  // of consuming a dedicated uv3 vertex-buffer slot.
@@ -13119,8 +13479,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13119
13479
  let vatNormFrame = vertexInputs.g_vatAnim0.x;
13120
13480
  let vatNormFrameCount = vertexInputs.g_vatAnim0.y;
13121
13481
  let vatNormFromRow = vertexInputs.g_vatAnim0.z;
13122
- let vatNormPackedSkinLoop = vertexInputs.g_vatAnim1.w;
13123
- let vatNormLoopBlend = vatNormPackedSkinLoop % 2.0;
13482
+ #ifdef VAT_SKIN_ARRAY
13483
+ let vatNormLoopBlend = vertexInputs.g_vatAnim1.w % 2.0;
13484
+ #else
13485
+ let vatNormLoopBlend = vertexInputs.g_vatAnim1.w;
13486
+ #endif
13124
13487
  if (vatNormFrameCount > 0.5) {
13125
13488
  // WGSL path derives the VAT texel coordinate from the vertex index instead
13126
13489
  // of consuming a dedicated uv3 vertex-buffer slot.
@@ -13157,130 +13520,187 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13157
13520
  #endif
13158
13521
  `;
13159
13522
  }
13160
- getGLSLVertexMainEnd() {
13523
+ getGLSLSkinLayerVaryingCode() {
13161
13524
  return `
13162
- #ifdef VERTEXANIMATIONMATERIAL
13163
- {
13164
- float _rsPackedSkinLoop = g_vatAnim1.w;
13165
- vRuntimeSkinIndex = floor(_rsPackedSkinLoop * 0.5 + 0.0001);
13166
- #ifdef UV1
13167
- vRuntimeSkinUV = uv;
13168
- #else
13169
- vRuntimeSkinUV = vec2(0.0, 0.0);
13170
- #endif
13171
- }
13525
+ #ifdef VAT_SKIN_ARRAY
13526
+ #ifdef UV1
13527
+ vVatSkinLayer = floor(g_vatAnim1.w * 0.5 + 0.0001);
13528
+ vVatSkinUV = uv;
13529
+ #else
13530
+ vVatSkinLayer = 0.0;
13531
+ vVatSkinUV = vec2(0.0);
13532
+ #endif
13172
13533
  #endif
13173
13534
  `;
13174
13535
  }
13175
- getWGSLVertexMainEnd() {
13536
+ getGLSLSkinFragmentDefs() {
13176
13537
  return `
13177
- #ifdef VERTEXANIMATIONMATERIAL
13538
+ #ifdef VAT_SKIN_ARRAY
13539
+ varying float vVatSkinLayer;
13540
+ varying vec2 vVatSkinUV;
13541
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13542
+ uniform sampler2DArray tkAlbedoArray;
13543
+ #endif
13544
+ #ifdef VAT_SKIN_ARRAY_NORMAL
13545
+ uniform sampler2DArray tkNormalArray;
13546
+ #endif
13547
+ #ifdef VAT_SKIN_ARRAY_MR
13548
+ uniform sampler2DArray tkMetalRoughArray;
13549
+ vec2 _tkMR;
13550
+ #endif
13551
+ #ifdef VAT_SKIN_ARRAY_EMISSIVE
13552
+ uniform sampler2DArray tkEmissiveArray;
13553
+ #endif
13554
+ #endif
13555
+ `;
13556
+ }
13557
+ getGLSLSkinAlbedoCode() {
13558
+ return `
13559
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13178
13560
  {
13179
- let _rsPackedSkinLoop = vertexInputs.g_vatAnim1.w;
13180
- vertexOutputs.vRuntimeSkinIndex = floor(_rsPackedSkinLoop * 0.5 + 0.0001);
13181
- #ifdef UV1
13182
- vertexOutputs.vRuntimeSkinUV = vertexInputs.uv;
13561
+ vec4 _vatSkinAlbedo = texture(tkAlbedoArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5)));
13562
+ // Match Babylon's native albedo: sRGB -> linear when the albedo is gamma-space.
13563
+ #ifdef GAMMAALBEDO
13564
+ surfaceAlbedo = toLinearSpace(_vatSkinAlbedo.rgb) * vAlbedoColor.rgb;
13183
13565
  #else
13184
- vertexOutputs.vRuntimeSkinUV = vec2f(0.0, 0.0);
13566
+ surfaceAlbedo = _vatSkinAlbedo.rgb * vAlbedoColor.rgb;
13567
+ #endif
13568
+ #ifdef ALPHATEST
13569
+ if (_vatSkinAlbedo.a < alphaCutOff) discard;
13185
13570
  #endif
13186
13571
  }
13187
13572
  #endif
13188
13573
  `;
13189
13574
  }
13190
- getGLSLFragmentDefinitions() {
13575
+ getGLSLSkinNormalCode() {
13191
13576
  return `
13192
- #ifndef TOOLKIT_VAT_RUNTIMESKIN_F_UNIFORMS
13193
- #define TOOLKIT_VAT_RUNTIMESKIN_F_UNIFORMS
13194
- varying float vRuntimeSkinIndex;
13195
- varying vec2 vRuntimeSkinUV;
13196
- uniform highp sampler2DArray runtimeAlbedoSkins;
13197
- uniform highp sampler2DArray runtimeNormalSkins;
13577
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
13578
+ {
13579
+ vec3 _vatNrmTexel = texture(tkNormalArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5))).xyz;
13580
+ normalW = perturbNormal(vTBN, _vatNrmTexel, vBumpInfos.y);
13581
+ }
13198
13582
  #endif
13199
13583
  `;
13200
13584
  }
13201
- getGLSLFragmentAlbedoCode() {
13585
+ getGLSLSkinMetalRoughSampleCode() {
13202
13586
  return `
13203
- #ifdef RUNTIME_SKINS_ALBEDO
13587
+ #ifdef VAT_SKIN_ARRAY_MR
13204
13588
  {
13205
- float _rsLayer = max(0.0, floor(vRuntimeSkinIndex + 0.5));
13206
- vec4 _rsAlbedo = texture(runtimeAlbedoSkins, vec3(vRuntimeSkinUV, _rsLayer));
13207
- surfaceAlbedo = _rsAlbedo.rgb * vAlbedoColor.rgb;
13208
- #ifdef ALPHATEST
13209
- if (_rsAlbedo.a < alphaCutOff) discard;
13210
- #endif
13589
+ vec4 _vatMRtexel = texture(tkMetalRoughArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5)));
13590
+ _tkMR = vec2(_vatMRtexel.b, _vatMRtexel.g);
13211
13591
  }
13212
13592
  #endif
13213
13593
  `;
13214
13594
  }
13215
- getGLSLFragmentNormalCode() {
13595
+ getGLSLSkinMetalRoughApplyCode() {
13216
13596
  return `
13217
- #ifdef RUNTIME_SKINS_NORMAL
13597
+ #if defined(VAT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
13598
+ metallicRoughness.r = _tkMR.x;
13599
+ metallicRoughness.g = _tkMR.y;
13600
+ #endif
13601
+ `;
13602
+ }
13603
+ getGLSLSkinEmissiveCode() {
13604
+ return `
13605
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_EMISSIVE)
13218
13606
  {
13219
- float _rsLayer = max(0.0, floor(vRuntimeSkinIndex + 0.5));
13220
- vec3 _rsNormalSample = texture(runtimeNormalSkins, vec3(vRuntimeSkinUV, _rsLayer)).xyz;
13221
- vec3 _rsNormalTS = _rsNormalSample * 2.0 - 1.0;
13222
- vec3 _rsP = vPositionW;
13223
- vec3 _rsN = normalize(vNormalW);
13224
- vec3 _rsDp1 = dFdx(_rsP);
13225
- vec3 _rsDp2 = dFdy(_rsP);
13226
- vec2 _rsDuv1 = dFdx(vRuntimeSkinUV);
13227
- vec2 _rsDuv2 = dFdy(vRuntimeSkinUV);
13228
- vec3 _rsDp2perp = cross(_rsDp2, _rsN);
13229
- vec3 _rsDp1perp = cross(_rsN, _rsDp1);
13230
- vec3 _rsT = _rsDp2perp * _rsDuv1.x + _rsDp1perp * _rsDuv2.x;
13231
- vec3 _rsB = _rsDp2perp * _rsDuv1.y + _rsDp1perp * _rsDuv2.y;
13232
- float _rsInvMax = inversesqrt(max(dot(_rsT, _rsT), dot(_rsB, _rsB)));
13233
- mat3 _rsTBN = mat3(_rsT * _rsInvMax, _rsB * _rsInvMax, _rsN);
13234
- normalW = normalize(_rsTBN * _rsNormalTS);
13607
+ vec3 _vatEmis = texture(tkEmissiveArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5))).rgb;
13608
+ finalEmissive = toLinearSpace(_vatEmis);
13235
13609
  }
13236
13610
  #endif
13237
13611
  `;
13238
13612
  }
13239
- getWGSLFragmentDefinitions() {
13613
+ getWGSLSkinLayerVaryingCode() {
13240
13614
  return `
13241
- varying vRuntimeSkinIndex: f32;
13242
- varying vRuntimeSkinUV: vec2<f32>;
13243
- var runtimeAlbedoSkins: texture_2d_array<f32>;
13244
- var runtimeAlbedoSkinsSampler: sampler;
13245
- var runtimeNormalSkins: texture_2d_array<f32>;
13246
- var runtimeNormalSkinsSampler: sampler;
13615
+ #ifdef VAT_SKIN_ARRAY
13616
+ #ifdef UV1
13617
+ vertexOutputs.vVatSkinLayer = floor(vertexInputs.g_vatAnim1.w * 0.5 + 0.0001);
13618
+ vertexOutputs.vVatSkinUV = vertexInputs.uv;
13619
+ #else
13620
+ vertexOutputs.vVatSkinLayer = 0.0;
13621
+ vertexOutputs.vVatSkinUV = vec2f(0.0);
13622
+ #endif
13623
+ #endif
13247
13624
  `;
13248
13625
  }
13249
- getWGSLFragmentAlbedoCode() {
13626
+ getWGSLSkinFragmentDefs() {
13250
13627
  return `
13251
- #ifdef RUNTIME_SKINS_ALBEDO
13628
+ #ifdef VAT_SKIN_ARRAY
13629
+ varying vVatSkinLayer: f32;
13630
+ varying vVatSkinUV: vec2<f32>;
13631
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13632
+ var tkAlbedoArray: texture_2d_array<f32>;
13633
+ var tkAlbedoArraySampler: sampler;
13634
+ #endif
13635
+ #ifdef VAT_SKIN_ARRAY_NORMAL
13636
+ var tkNormalArray: texture_2d_array<f32>;
13637
+ var tkNormalArraySampler: sampler;
13638
+ #endif
13639
+ #ifdef VAT_SKIN_ARRAY_MR
13640
+ var tkMetalRoughArray: texture_2d_array<f32>;
13641
+ var tkMetalRoughArraySampler: sampler;
13642
+ var<private> _tkMR: vec2<f32>;
13643
+ #endif
13644
+ #ifdef VAT_SKIN_ARRAY_EMISSIVE
13645
+ var tkEmissiveArray: texture_2d_array<f32>;
13646
+ var tkEmissiveArraySampler: sampler;
13647
+ #endif
13648
+ #endif
13649
+ `;
13650
+ }
13651
+ getWGSLSkinAlbedoCode() {
13652
+ return `
13653
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13252
13654
  {
13253
- let _rsLayer = i32(max(0.0, floor(fragmentInputs.vRuntimeSkinIndex + 0.5)));
13254
- let _rsAlbedo = textureSample(runtimeAlbedoSkins, runtimeAlbedoSkinsSampler, fragmentInputs.vRuntimeSkinUV, _rsLayer);
13255
- surfaceAlbedo = _rsAlbedo.rgb * vAlbedoColor.rgb;
13655
+ let _vatSkinAlbedo = textureSample(tkAlbedoArray, tkAlbedoArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5));
13656
+ // Match Babylon's native albedo: sRGB -> linear when the albedo is gamma-space.
13657
+ #ifdef GAMMAALBEDO
13658
+ surfaceAlbedo = toLinearSpaceVec3(_vatSkinAlbedo.rgb) * vAlbedoColor.rgb;
13659
+ #else
13660
+ surfaceAlbedo = _vatSkinAlbedo.rgb * vAlbedoColor.rgb;
13661
+ #endif
13256
13662
  #ifdef ALPHATEST
13257
- if (_rsAlbedo.a < alphaCutOff) { discard; }
13663
+ if (_vatSkinAlbedo.a < alphaCutOff) { discard; }
13258
13664
  #endif
13259
13665
  }
13260
13666
  #endif
13261
13667
  `;
13262
13668
  }
13263
- getWGSLFragmentNormalCode() {
13669
+ getWGSLSkinNormalCode() {
13670
+ return `
13671
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
13672
+ {
13673
+ let _vatNrmTexel = textureSample(tkNormalArray, tkNormalArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5)).rgb;
13674
+ let _vatNrmTBN = mat3x3<f32>(fragmentInputs.vTBN0, fragmentInputs.vTBN1, fragmentInputs.vTBN2);
13675
+ normalW = perturbNormal(_vatNrmTBN, _vatNrmTexel, uniforms.vBumpInfos.y);
13676
+ }
13677
+ #endif
13678
+ `;
13679
+ }
13680
+ getWGSLSkinMetalRoughSampleCode() {
13264
13681
  return `
13265
- #ifdef RUNTIME_SKINS_NORMAL
13682
+ #ifdef VAT_SKIN_ARRAY_MR
13266
13683
  {
13267
- let _rsLayer = i32(max(0.0, floor(fragmentInputs.vRuntimeSkinIndex + 0.5)));
13268
- let _rsNormalSample = textureSample(runtimeNormalSkins, runtimeNormalSkinsSampler, fragmentInputs.vRuntimeSkinUV, _rsLayer).xyz;
13269
- let _rsNormalTS = _rsNormalSample * 2.0 - vec3f(1.0);
13270
- let _rsP = fragmentInputs.vPositionW;
13271
- let _rsN = normalize(fragmentInputs.vNormalW);
13272
- let _rsDp1 = dpdx(_rsP);
13273
- let _rsDp2 = dpdy(_rsP);
13274
- let _rsDuv1 = dpdx(fragmentInputs.vRuntimeSkinUV);
13275
- let _rsDuv2 = dpdy(fragmentInputs.vRuntimeSkinUV);
13276
- let _rsDp2perp = cross(_rsDp2, _rsN);
13277
- let _rsDp1perp = cross(_rsN, _rsDp1);
13278
- let _rsT = _rsDp2perp * _rsDuv1.x + _rsDp1perp * _rsDuv2.x;
13279
- let _rsB = _rsDp2perp * _rsDuv1.y + _rsDp1perp * _rsDuv2.y;
13280
- let _rsInvMax = inverseSqrt(max(dot(_rsT, _rsT), dot(_rsB, _rsB)));
13281
- let _rsTcol = _rsT * _rsInvMax;
13282
- let _rsBcol = _rsB * _rsInvMax;
13283
- normalW = normalize(_rsTcol * _rsNormalTS.x + _rsBcol * _rsNormalTS.y + _rsN * _rsNormalTS.z);
13684
+ let _vatMRtexel = textureSample(tkMetalRoughArray, tkMetalRoughArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5));
13685
+ _tkMR = vec2<f32>(_vatMRtexel.b, _vatMRtexel.g);
13686
+ }
13687
+ #endif
13688
+ `;
13689
+ }
13690
+ getWGSLSkinMetalRoughApplyCode() {
13691
+ return `
13692
+ #if defined(VAT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
13693
+ metallicRoughness.r = _tkMR.x;
13694
+ metallicRoughness.g = _tkMR.y;
13695
+ #endif
13696
+ `;
13697
+ }
13698
+ getWGSLSkinEmissiveCode() {
13699
+ return `
13700
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_EMISSIVE)
13701
+ {
13702
+ let _vatEmis = textureSample(tkEmissiveArray, tkEmissiveArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5)).rgb;
13703
+ finalEmissive = toLinearSpaceVec3(_vatEmis);
13284
13704
  }
13285
13705
  #endif
13286
13706
  `;
@@ -27680,6 +28100,138 @@ ShurikenParticles.UNITY_TO_BABYLON_LIFETIME_RATIO = 0.65;
27680
28100
  ShurikenParticles.UNITY_TO_BABYLON_DEATH_FADE_ALPHA = 0.2;
27681
28101
  ShurikenParticles.EMITTER_POSITION_OFFSET = new Vector3(0, 0, 0);
27682
28102
  ShurikenParticles.EMITTER_ROTATION_OFFSET = new Vector3(90, 0, 0);
28103
+ export class SkinTextureArray {
28104
+ static Build(scene, urls, options = {}) {
28105
+ const requested = (urls || []).filter((u) => u != null && String(u).length > 0);
28106
+ if (requested.length === 0)
28107
+ return Promise.resolve(null);
28108
+ const maxLayers = SkinTextureArray._maxArrayLayers(scene.getEngine());
28109
+ let list = requested;
28110
+ if (requested.length > maxLayers) {
28111
+ console.warn("SkinTextureArray: " + requested.length + " slices exceeds GPU maxTextureArrayLayers ("
28112
+ + maxLayers + ") — clamping to " + maxLayers + ". Skin indices >= " + maxLayers + " will be unavailable.");
28113
+ list = requested.slice(0, maxLayers);
28114
+ }
28115
+ const optKey = "m" + (options.mipmaps === false ? 0 : 1)
28116
+ + "|y" + (options.invertY === true ? 1 : 0)
28117
+ + "|s" + (options.samplingMode != null ? options.samplingMode : "d");
28118
+ const key = list.join("|") + "||" + optKey;
28119
+ if (SkinTextureArray._cache[key] != null)
28120
+ return SkinTextureArray._cache[key];
28121
+ const built = SkinTextureArray._loadImages(list).then((images) => {
28122
+ if (images == null || images.length === 0)
28123
+ return null;
28124
+ const w = images[0].width, h = images[0].height, depth = images.length;
28125
+ const canvas = document.createElement("canvas");
28126
+ canvas.width = w;
28127
+ canvas.height = h;
28128
+ const ctx = canvas.getContext("2d");
28129
+ const data = new Uint8Array(w * h * 4 * depth);
28130
+ const flipY = (options.invertY != null) ? options.invertY : false;
28131
+ for (let i = 0; i < depth; i++) {
28132
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
28133
+ ctx.clearRect(0, 0, w, h);
28134
+ if (flipY)
28135
+ ctx.setTransform(1, 0, 0, -1, 0, h);
28136
+ ctx.drawImage(images[i], 0, 0, w, h);
28137
+ const id = ctx.getImageData(0, 0, w, h).data;
28138
+ data.set(id, i * w * h * 4);
28139
+ }
28140
+ const engine = scene.getEngine();
28141
+ const isWebGPU = (engine != null && engine.isWebGPU === true);
28142
+ const genMips = (options.mipmaps !== false);
28143
+ const mipCount = genMips ? (Math.floor(Math.log2(Math.max(w, h))) + 1) : 1;
28144
+ const samplingMode = (options.samplingMode != null) ? options.samplingMode
28145
+ : (genMips ? Texture.TRILINEAR_SAMPLINGMODE : Texture.BILINEAR_SAMPLINGMODE);
28146
+ const arr = new RawTexture2DArray(data, w, h, depth, Constants.TEXTUREFORMAT_RGBA, scene, genMips, false, samplingMode, Constants.TEXTURETYPE_UNSIGNED_BYTE, 0, mipCount);
28147
+ if (genMips && isWebGPU && mipCount > 1)
28148
+ SkinTextureArray._uploadMipChain(scene, arr, data, w, h, depth);
28149
+ arr.wrapU = Texture.CLAMP_ADDRESSMODE;
28150
+ arr.wrapV = Texture.CLAMP_ADDRESSMODE;
28151
+ arr.anisotropicFilteringLevel = (options.aniso != null) ? options.aniso : 8;
28152
+ return arr;
28153
+ }).catch((e) => { console.warn("SkinTextureArray: build failed", e); delete SkinTextureArray._cache[key]; return null; });
28154
+ SkinTextureArray._cache[key] = built;
28155
+ return built;
28156
+ }
28157
+ static _uploadMipChain(scene, arr, base, w, h, depth) {
28158
+ const engine = scene.getEngine();
28159
+ const itex = arr.getInternalTexture();
28160
+ if (itex == null || typeof engine.updateRawTexture2DArray !== "function")
28161
+ return;
28162
+ const count = Math.floor(Math.log2(Math.max(w, h))) + 1;
28163
+ try {
28164
+ let prev = base, pw = w, ph = h;
28165
+ for (let m = 1; m < count; m++) {
28166
+ const nw = Math.max(1, pw >> 1), nh = Math.max(1, ph >> 1);
28167
+ const mip = SkinTextureArray._downsampleLayers(prev, pw, ph, depth, nw, nh);
28168
+ engine.updateRawTexture2DArray(itex, mip, Constants.TEXTUREFORMAT_RGBA, false, null, Constants.TEXTURETYPE_UNSIGNED_BYTE, m);
28169
+ prev = mip;
28170
+ pw = nw;
28171
+ ph = nh;
28172
+ }
28173
+ }
28174
+ catch (e) {
28175
+ console.warn("SkinTextureArray: mip-chain upload failed, using base level only", e);
28176
+ }
28177
+ }
28178
+ static _downsampleLayers(src, sw, sh, depth, dw, dh) {
28179
+ const out = new Uint8Array(dw * dh * 4 * depth);
28180
+ for (let L = 0; L < depth; L++) {
28181
+ const so = L * sw * sh * 4, doo = L * dw * dh * 4;
28182
+ for (let y = 0; y < dh; y++) {
28183
+ for (let x = 0; x < dw; x++) {
28184
+ const sx = x * 2, sy = y * 2;
28185
+ const sx1 = Math.min(sx + 1, sw - 1), sy1 = Math.min(sy + 1, sh - 1);
28186
+ for (let c = 0; c < 4; c++) {
28187
+ const a = src[so + (sy * sw + sx) * 4 + c], b = src[so + (sy * sw + sx1) * 4 + c], e = src[so + (sy1 * sw + sx) * 4 + c], d = src[so + (sy1 * sw + sx1) * 4 + c];
28188
+ out[doo + (y * dw + x) * 4 + c] = (a + b + e + d) >> 2;
28189
+ }
28190
+ }
28191
+ }
28192
+ }
28193
+ return out;
28194
+ }
28195
+ static _maxArrayLayers(engine) {
28196
+ try {
28197
+ const dev = (engine != null) ? (engine._device || (typeof engine.getDevice === "function" ? engine.getDevice() : null)) : null;
28198
+ if (dev != null && dev.limits != null && typeof dev.limits.maxTextureArrayLayers === "number" && dev.limits.maxTextureArrayLayers > 0) {
28199
+ return dev.limits.maxTextureArrayLayers;
28200
+ }
28201
+ const gl = (engine != null) ? engine._gl : null;
28202
+ if (gl != null && typeof gl.getParameter === "function") {
28203
+ const MAX_ARRAY_TEXTURE_LAYERS = 0x88FF;
28204
+ const v = gl.getParameter(MAX_ARRAY_TEXTURE_LAYERS);
28205
+ if (typeof v === "number" && v > 0)
28206
+ return v;
28207
+ }
28208
+ }
28209
+ catch (e) { }
28210
+ return 256;
28211
+ }
28212
+ static _loadImages(urls) {
28213
+ return Promise.all(urls.map((u) => new Promise((resolve, reject) => {
28214
+ const img = document.createElement("img");
28215
+ img.crossOrigin = "anonymous";
28216
+ img.onload = () => resolve(img);
28217
+ img.onerror = () => reject(new Error("SkinTextureArray: failed to load " + u));
28218
+ img.src = u;
28219
+ })));
28220
+ }
28221
+ static Clear() {
28222
+ const entries = [];
28223
+ for (const k in SkinTextureArray._cache) {
28224
+ if (SkinTextureArray._cache[k] != null)
28225
+ entries.push(SkinTextureArray._cache[k]);
28226
+ }
28227
+ SkinTextureArray._cache = {};
28228
+ for (const p of entries) {
28229
+ p.then((arr) => { if (arr != null)
28230
+ arr.dispose(); }).catch(() => { });
28231
+ }
28232
+ }
28233
+ }
28234
+ SkinTextureArray._cache = {};
27683
28235
  export class MeshNormalProxy {
27684
28236
  constructor() {
27685
28237
  this.blendWithPhysicsNormal = 0.0;
@@ -29254,6 +29806,410 @@ TerrainBuilder.detailChunkMaxTotalChunks = 256;
29254
29806
  TerrainBuilder.meshDetailChunkMode = 1;
29255
29807
  TerrainBuilder.meshDetailChunkTargetInstances = 20000;
29256
29808
  TerrainBuilder.meshDetailChunkWorldSize = 64;
29809
+ export class TextureAtlasSkin extends ScriptComponent {
29810
+ constructor(transform, scene, properties = {}, alias = "TextureAtlasSkin") {
29811
+ super(transform, scene, properties, alias);
29812
+ this.abtractMesh = null;
29813
+ this.textureData = null;
29814
+ this.defaultIndex = 0;
29815
+ this.skinnedParts = [];
29816
+ this.albedoSlices = null;
29817
+ this.normalSlices = null;
29818
+ this.metallicSlices = null;
29819
+ this.occlusionSlices = null;
29820
+ this.emissiveSlices = null;
29821
+ this._applied = false;
29822
+ this._manifest = null;
29823
+ this._currentIndex = 0;
29824
+ this._targets = [];
29825
+ this._clonedMultiMaterial = null;
29826
+ }
29827
+ awake() {
29828
+ this.abtractMesh = this.getAbstractMesh();
29829
+ this.albedoSlices = this.getProperty("albedoSlices", null);
29830
+ this.normalSlices = this.getProperty("normalSlices", null);
29831
+ this.metallicSlices = this.getProperty("metallicSlices", null);
29832
+ this.occlusionSlices = this.getProperty("occlusionSlices", null);
29833
+ this.emissiveSlices = this.getProperty("emissiveSlices", null);
29834
+ window["__" + this.transform.name] = this;
29835
+ }
29836
+ start() {
29837
+ this.applySkins();
29838
+ }
29839
+ applySkins() {
29840
+ if (this._applied)
29841
+ return;
29842
+ const mesh = this.abtractMesh || this.getAbstractMesh();
29843
+ if (mesh == null) {
29844
+ console.warn("TextureAtlasSkin: no abstract mesh on " + this.transform.name);
29845
+ return;
29846
+ }
29847
+ let manifest = this.parseManifest();
29848
+ if (manifest == null) {
29849
+ const sliceCount = this.maxChannelSliceCount();
29850
+ if (sliceCount > 0) {
29851
+ const skins = [];
29852
+ for (let i = 0; i < sliceCount; i++)
29853
+ skins.push({ index: i, name: "skin_" + i });
29854
+ manifest = { skinArray: true, count: sliceCount, skins: skins };
29855
+ }
29856
+ else {
29857
+ console.warn("TextureAtlasSkin: no textureData manifest and no channel slices on " + this.transform.name);
29858
+ return;
29859
+ }
29860
+ }
29861
+ this._manifest = manifest;
29862
+ this._currentIndex = this.clampIndex(this.toInt(this.defaultIndex, 0), "defaultIndex");
29863
+ const isInstanced = this.isInstancedSetup(mesh);
29864
+ const slots = this.getMeshSlots(mesh);
29865
+ const parts = this.skinnedParts || [];
29866
+ if (parts.length === 0) {
29867
+ console.warn("TextureAtlasSkin: no skinnedParts on " + this.transform.name);
29868
+ return;
29869
+ }
29870
+ for (const part of parts) {
29871
+ const targetMat = this.resolvePartMaterial(part);
29872
+ const slot = this.matchSlot(slots, targetMat, part);
29873
+ if (slot == null || !(slot.material instanceof CustomShaderMaterial)) {
29874
+ console.warn("TextureAtlasSkin: skinned part not found / not a CustomShaderMaterial on " + this.transform.name, part);
29875
+ continue;
29876
+ }
29877
+ let material = slot.material;
29878
+ const isVat = (material instanceof VertexAnimationMaterial);
29879
+ const kind = isVat ? "vat" : (isInstanced ? "instanced" : "regular");
29880
+ if (kind === "regular") {
29881
+ material = this.cloneForRegular(mesh, slot, material);
29882
+ }
29883
+ this.configureMaterial(material, manifest, kind);
29884
+ this._targets.push({ material: material, kind: kind });
29885
+ console.warn(">>> TextureAtlasSkin: " + this.transform.name + " — part '" + material.name + "' → " + this.describeKind(kind) + " setup");
29886
+ }
29887
+ this.applyIndexToTargets(mesh, this._currentIndex);
29888
+ this._applied = true;
29889
+ }
29890
+ setSkinIndex(index) {
29891
+ const requested = this.toInt(index, 0);
29892
+ this._currentIndex = this.clampIndex(requested, "skin index");
29893
+ const mesh = this.abtractMesh || this.getAbstractMesh();
29894
+ this.applyIndexToTargets(mesh, this._currentIndex);
29895
+ return this._currentIndex;
29896
+ }
29897
+ clampIndex(index, label) {
29898
+ const count = this.skinCount();
29899
+ const max = Math.max(0, count - 1);
29900
+ const clamped = (index < 0) ? 0 : (index > max ? max : index);
29901
+ if (clamped !== index) {
29902
+ console.warn("TextureAtlasSkin: " + label + " " + index + " out of range [0, " + max + "] on " + this.transform.name + " — clamped to " + clamped);
29903
+ }
29904
+ return clamped;
29905
+ }
29906
+ skinCount() {
29907
+ const m = this._manifest;
29908
+ if (m == null)
29909
+ return this.maxChannelSliceCount();
29910
+ const skins = m.skins || [];
29911
+ if (skins.length > 0)
29912
+ return skins.length;
29913
+ const c = this.toInt(m.count, 0);
29914
+ if (c > 0)
29915
+ return c;
29916
+ return this.maxChannelSliceCount();
29917
+ }
29918
+ maxChannelSliceCount() {
29919
+ const lens = [this.albedoSlices, this.normalSlices, this.metallicSlices, this.occlusionSlices, this.emissiveSlices]
29920
+ .map((s) => (s != null) ? s.length : 0);
29921
+ return Math.max(0, lens[0], lens[1], lens[2], lens[3], lens[4]);
29922
+ }
29923
+ getSkinIndex() {
29924
+ return this._currentIndex;
29925
+ }
29926
+ configureMaterial(material, manifest, kind) {
29927
+ const shared = (kind !== "regular");
29928
+ if (shared && this.isConfigured(material))
29929
+ return;
29930
+ if (shared)
29931
+ this.markConfigured(material);
29932
+ if (kind === "instanced")
29933
+ this.ensureSkinLayerBuffer(this.abtractMesh || this.getAbstractMesh());
29934
+ const albedoUrls = this.albedoChannelUrls(manifest);
29935
+ const normalUrls = this.normalChannelUrls(manifest);
29936
+ const mrUrls = this.metalRoughChannelUrls(manifest);
29937
+ const emissiveUrls = this.emissiveChannelUrls(manifest);
29938
+ const has = (u) => (u != null && u.length > 0);
29939
+ if (!has(albedoUrls) && !has(normalUrls) && !has(mrUrls) && !has(emissiveUrls)) {
29940
+ console.warn("TextureAtlasSkin: no channel slices to build a skin array on " + this.transform.name);
29941
+ return;
29942
+ }
29943
+ if (kind !== "vat")
29944
+ material.setSkinArrayLayerMode(kind === "regular");
29945
+ const build = (urls, mipped) => has(urls) ? SkinTextureArray.Build(this.scene, urls, mipped ? {} : { mipmaps: false }) : Promise.resolve(null);
29946
+ Promise.all([build(albedoUrls, true), build(normalUrls, false), build(mrUrls, true), build(emissiveUrls, true)])
29947
+ .then(([albTex, nrmTex, mrTex, emisTex]) => {
29948
+ const vmat = (kind === "vat") ? material : null;
29949
+ if (albTex != null) {
29950
+ material.addTextureArrayUniform("tkAlbedoArray", albTex);
29951
+ if (vmat != null)
29952
+ vmat.enableVatSkinArray();
29953
+ else
29954
+ material.enableSkinArray(kind === "regular");
29955
+ }
29956
+ if (nrmTex != null) {
29957
+ material.addTextureArrayUniform("tkNormalArray", nrmTex);
29958
+ if (vmat != null)
29959
+ vmat.enableVatSkinArrayNormal();
29960
+ else
29961
+ material.enableSkinArrayNormal();
29962
+ }
29963
+ if (mrTex != null) {
29964
+ material.addTextureArrayUniform("tkMetalRoughArray", mrTex);
29965
+ if (vmat != null)
29966
+ vmat.enableVatSkinArrayMetalRough();
29967
+ else
29968
+ material.enableSkinArrayMetalRough();
29969
+ }
29970
+ if (emisTex != null) {
29971
+ material.addTextureArrayUniform("tkEmissiveArray", emisTex);
29972
+ if (vmat != null)
29973
+ vmat.enableVatSkinArrayEmissive();
29974
+ else
29975
+ material.enableSkinArrayEmissive();
29976
+ }
29977
+ if (albTex == null && nrmTex == null && mrTex == null && emisTex == null) {
29978
+ console.warn("TextureAtlasSkin: all channel arrays failed to build on " + this.transform.name);
29979
+ return;
29980
+ }
29981
+ this.applyLayerToMaterial(material, kind, this._currentIndex);
29982
+ });
29983
+ }
29984
+ albedoChannelUrls(manifest) {
29985
+ const fromSlices = this.sliceUrls(this.albedoSlices);
29986
+ if (fromSlices != null)
29987
+ return fromSlices;
29988
+ const layers = (manifest != null) ? manifest.albedoLayers : null;
29989
+ if (Array.isArray(layers)) {
29990
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
29991
+ return (out.length > 0) ? out : null;
29992
+ }
29993
+ return null;
29994
+ }
29995
+ normalChannelUrls(manifest) {
29996
+ const fromSlices = this.sliceUrls(this.normalSlices);
29997
+ if (fromSlices != null)
29998
+ return fromSlices;
29999
+ const layers = (manifest != null) ? manifest.normalLayers : null;
30000
+ if (Array.isArray(layers)) {
30001
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
30002
+ return (out.length > 0) ? out : null;
30003
+ }
30004
+ return null;
30005
+ }
30006
+ metalRoughChannelUrls(manifest) {
30007
+ const fromSlices = this.sliceUrls(this.metallicSlices);
30008
+ if (fromSlices != null)
30009
+ return fromSlices;
30010
+ const layers = (manifest != null) ? manifest.metalRoughLayers : null;
30011
+ if (Array.isArray(layers)) {
30012
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
30013
+ return (out.length > 0) ? out : null;
30014
+ }
30015
+ return null;
30016
+ }
30017
+ emissiveChannelUrls(manifest) {
30018
+ const fromSlices = this.sliceUrls(this.emissiveSlices);
30019
+ if (fromSlices != null)
30020
+ return fromSlices;
30021
+ const layers = (manifest != null) ? manifest.emissiveLayers : null;
30022
+ if (Array.isArray(layers)) {
30023
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
30024
+ return (out.length > 0) ? out : null;
30025
+ }
30026
+ return null;
30027
+ }
30028
+ sliceUrls(slices) {
30029
+ if (slices == null || slices.length === 0)
30030
+ return null;
30031
+ const root = SceneManager.GetRootUrl(this.scene) || "";
30032
+ const urls = [];
30033
+ for (const s of slices) {
30034
+ if (s == null)
30035
+ continue;
30036
+ const anyS = s;
30037
+ let u = null;
30038
+ if (anyS.url != null && String(anyS.url).length > 0)
30039
+ u = String(anyS.url);
30040
+ else if (anyS.filename != null && String(anyS.filename).length > 0)
30041
+ u = root + String(anyS.filename);
30042
+ if (u != null && u.length > 0)
30043
+ urls.push(u);
30044
+ }
30045
+ return (urls.length > 0) ? urls : null;
30046
+ }
30047
+ resolveLayerUrl(filename) {
30048
+ if (filename == null)
30049
+ return "";
30050
+ const f = String(filename);
30051
+ if (/^(https?:|blob:|data:|\/)/i.test(f))
30052
+ return f;
30053
+ const root = SceneManager.GetRootUrl(this.scene) || "";
30054
+ return root + f;
30055
+ }
30056
+ applyIndexToTargets(mesh, index) {
30057
+ if (mesh == null)
30058
+ return;
30059
+ for (const t of this._targets) {
30060
+ this.applyLayerToMaterial(t.material, t.kind, index);
30061
+ }
30062
+ }
30063
+ applyLayerToMaterial(material, kind, index) {
30064
+ if (kind === "vat") {
30065
+ VertexAnimationController.SetAtlasCellIndex(this.abtractMesh || this.getAbstractMesh(), index);
30066
+ }
30067
+ else if (kind === "instanced") {
30068
+ const mesh = this.abtractMesh || this.getAbstractMesh();
30069
+ this.ensureSkinLayerBuffer(mesh);
30070
+ mesh.instancedBuffers.tkSkinLayer = index;
30071
+ }
30072
+ else {
30073
+ material.setSkinLayer(index);
30074
+ }
30075
+ }
30076
+ isInstancedSetup(mesh) {
30077
+ if (mesh instanceof InstancedMesh)
30078
+ return true;
30079
+ const src = (mesh instanceof Mesh) ? mesh : null;
30080
+ return (src != null && src.instances != null && src.instances.length > 0);
30081
+ }
30082
+ ensureSkinLayerBuffer(mesh) {
30083
+ const inst = mesh;
30084
+ const src = (inst != null && inst.sourceMesh != null) ? inst.sourceMesh : mesh;
30085
+ const target = (src != null) ? src : mesh;
30086
+ if (target != null && typeof target.registerInstancedBuffer === "function") {
30087
+ const ib = target.instancedBuffers;
30088
+ if (ib == null || ib.tkSkinLayer === undefined) {
30089
+ target.registerInstancedBuffer("tkSkinLayer", 1);
30090
+ target.instancedBuffers = target.instancedBuffers || {};
30091
+ target.instancedBuffers.tkSkinLayer = 0;
30092
+ }
30093
+ }
30094
+ mesh.instancedBuffers = mesh.instancedBuffers || {};
30095
+ }
30096
+ getMeshSlots(mesh) {
30097
+ const slots = [];
30098
+ const mat = mesh.material;
30099
+ if (mat instanceof MultiMaterial) {
30100
+ const subs = mat.subMaterials;
30101
+ for (let i = 0; i < subs.length; i++) {
30102
+ if (subs[i] != null)
30103
+ slots.push({ material: subs[i], mm: mat, index: i });
30104
+ }
30105
+ }
30106
+ else if (mat != null) {
30107
+ slots.push({ material: mat, mm: null, index: -1 });
30108
+ }
30109
+ return slots;
30110
+ }
30111
+ matchSlot(slots, targetMat, part) {
30112
+ if (targetMat != null) {
30113
+ for (const s of slots)
30114
+ if (s.material === targetMat)
30115
+ return s;
30116
+ }
30117
+ const name = this.partName(part) || (targetMat != null ? targetMat.name : null);
30118
+ if (name != null && name.length > 0) {
30119
+ for (const s of slots)
30120
+ if (s.material.name === name)
30121
+ return s;
30122
+ for (const s of slots)
30123
+ if (s.material.name.indexOf(name) !== -1)
30124
+ return s;
30125
+ }
30126
+ if (slots.length === 1)
30127
+ return slots[0];
30128
+ return null;
30129
+ }
30130
+ resolvePartMaterial(part) {
30131
+ if (part == null)
30132
+ return null;
30133
+ if (part instanceof Material)
30134
+ return part;
30135
+ if (part.skinPart != null)
30136
+ return this.resolvePartMaterial(part.skinPart);
30137
+ const name = this.partName(part);
30138
+ if (name == null)
30139
+ return null;
30140
+ let mat = this.scene.getMaterialByName(name);
30141
+ if (mat == null && SceneManager.GetMaterialWithName != null)
30142
+ mat = SceneManager.GetMaterialWithName(this.scene, name);
30143
+ return mat;
30144
+ }
30145
+ partName(part) {
30146
+ if (part == null)
30147
+ return null;
30148
+ if (typeof part === "string")
30149
+ return part;
30150
+ if (part instanceof Material)
30151
+ return part.name;
30152
+ if (part.skinPart != null)
30153
+ return this.partName(part.skinPart);
30154
+ if (part.name != null)
30155
+ return part.name;
30156
+ if (part.material != null)
30157
+ return this.partName(part.material);
30158
+ return null;
30159
+ }
30160
+ cloneForRegular(mesh, slot, material) {
30161
+ const tag = "_skin_" + (this.transform != null ? this.transform.name : "skin");
30162
+ const cloned = material.clone(material.name + tag);
30163
+ if (!(cloned instanceof CustomShaderMaterial)) {
30164
+ console.warn("TextureAtlasSkin: clone did not preserve CustomShaderMaterial — using original (skin may be shared)", cloned);
30165
+ return material;
30166
+ }
30167
+ if (slot.mm != null) {
30168
+ if (this._clonedMultiMaterial == null) {
30169
+ this._clonedMultiMaterial = slot.mm.clone(slot.mm.name + tag);
30170
+ mesh.material = this._clonedMultiMaterial;
30171
+ }
30172
+ this._clonedMultiMaterial.subMaterials[slot.index] = cloned;
30173
+ }
30174
+ else {
30175
+ mesh.material = cloned;
30176
+ }
30177
+ return cloned;
30178
+ }
30179
+ parseManifest() {
30180
+ let data = this.textureData;
30181
+ if (data == null)
30182
+ return null;
30183
+ if (typeof data === "string") {
30184
+ try {
30185
+ data = JSON.parse(data);
30186
+ }
30187
+ catch (e) {
30188
+ console.warn("TextureAtlasSkin: bad textureData JSON", e);
30189
+ return null;
30190
+ }
30191
+ }
30192
+ return (data != null && data.skins != null) ? data : null;
30193
+ }
30194
+ toInt(v, def) {
30195
+ const n = (typeof v === "number") ? v : parseInt(v, 10);
30196
+ return isNaN(n) ? def : Math.floor(n);
30197
+ }
30198
+ isConfigured(material) {
30199
+ return !!(material && material.metadata && material.metadata.atlasSkinConfigured === true);
30200
+ }
30201
+ markConfigured(material) {
30202
+ material.metadata = material.metadata || {};
30203
+ material.metadata.atlasSkinConfigured = true;
30204
+ }
30205
+ describeKind(kind) {
30206
+ if (kind === "vat")
30207
+ return "VAT Mesh (packed cell index)";
30208
+ if (kind === "instanced")
30209
+ return "Instanced Mesh (per-instance UV buffer)";
30210
+ return "Babylon Mesh (cloned material uniform)";
30211
+ }
30212
+ }
29257
30213
  export class UserInterface extends ScriptComponent {
29258
30214
  constructor(transform, scene, properties = {}, alias = "UserInterface") {
29259
30215
  super(transform, scene, properties, alias);
@@ -30775,6 +31731,7 @@ SceneManager.RegisterClass("CustomShaderMaterial", CustomShaderMaterial);
30775
31731
  SceneManager.RegisterClass("UniversalShaderMaterial", UniversalShaderMaterial);
30776
31732
  SceneManager.RegisterClass("UnityStyleLightingPlugin", UnityStyleLightingPlugin);
30777
31733
  SceneManager.RegisterClass("CustomShaderMaterialPlugin", CustomShaderMaterialPlugin);
31734
+ SceneManager.RegisterClass("SkinArraySwitchingPlugin", SkinArraySwitchingPlugin);
30778
31735
  SceneManager.RegisterClass("StandardShaderMaterial", StandardShaderMaterial);
30779
31736
  SceneManager.RegisterClass("StandardShaderMaterialPlugin", StandardShaderMaterialPlugin);
30780
31737
  SceneManager.RegisterClass("UniversalTerrainMaterial", UniversalTerrainMaterial);
@@ -41407,7 +42364,9 @@ SceneManager.RegisterClass("NavigationAgent", NavigationAgent);
41407
42364
  SceneManager.RegisterClass("PostProcessor", PostProcessor);
41408
42365
  SceneManager.RegisterClass("RigidbodyPhysics", RigidbodyPhysics);
41409
42366
  SceneManager.RegisterClass("ShurikenParticles", ShurikenParticles);
42367
+ SceneManager.RegisterClass("SkinTextureArray", SkinTextureArray);
41410
42368
  SceneManager.RegisterClass("TerrainBuilder", TerrainBuilder);
42369
+ SceneManager.RegisterClass("TextureAtlasSkin", TextureAtlasSkin);
41411
42370
  SceneManager.RegisterClass("UnitySlider", UnitySlider);
41412
42371
  SceneManager.RegisterClass("UnityScrollBar", UnityScrollBar);
41413
42372
  SceneManager.RegisterClass("UnityDropdownMenu", UnityDropdownMenu);