@babylonjs-toolkit/next 9.12.1 → 9.12.101

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.101 - 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);
@@ -5530,10 +5543,18 @@ export class CustomShaderMaterial extends PBRMaterial {
5530
5543
  this.shader = null;
5531
5544
  this.plugin = null;
5532
5545
  this._unityLightingPlugin = null;
5546
+ this._skinArray = false;
5547
+ this._skinArrayNormal = false;
5548
+ this._skinArrayMetalRough = false;
5549
+ this._skinArrayEmissive = false;
5550
+ this._skinLayerUniform = false;
5551
+ this._skinArraySwitchingPlugin = null;
5533
5552
  this.shader = "CustomShaderMaterial";
5534
5553
  this.addFloatUniform("tkIblDiffuseScale", 1.0);
5535
5554
  this.addFloatUniform("tkIblSpecularScale", 1.0);
5555
+ this.addFloatUniform("tkSkinLayerUni", 0.0);
5536
5556
  this._unityLightingPlugin = new UnityStyleLightingPlugin(this, this.shader);
5557
+ this._skinArraySwitchingPlugin = new SkinArraySwitchingPlugin(this);
5537
5558
  }
5538
5559
  initMaterial() {
5539
5560
  if (this["awake"])
@@ -5567,6 +5588,108 @@ export class CustomShaderMaterial extends PBRMaterial {
5567
5588
  this.plugin?.markAllDefinesAsDirty();
5568
5589
  this._unityLightingPlugin?.markAllDefinesAsDirty();
5569
5590
  }
5591
+ enableSkinArray(layerUniform = false) {
5592
+ this._skinArray = true;
5593
+ this._skinLayerUniform = layerUniform;
5594
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5595
+ this.plugin?.markAllDefinesAsDirty();
5596
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5597
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5598
+ return this;
5599
+ }
5600
+ setSkinLayer(layer) {
5601
+ this._skinLayerUniform = true;
5602
+ this.setFloatValue("tkSkinLayerUni", Math.max(0, Math.floor(layer)));
5603
+ return this;
5604
+ }
5605
+ enableSkinArrayNormal(enabled = true) {
5606
+ this._skinArrayNormal = enabled;
5607
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5608
+ this.plugin?.markAllDefinesAsDirty();
5609
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5610
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5611
+ return this;
5612
+ }
5613
+ enableSkinArrayMetalRough(enabled = true) {
5614
+ this._skinArrayMetalRough = enabled;
5615
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5616
+ this.plugin?.markAllDefinesAsDirty();
5617
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5618
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5619
+ return this;
5620
+ }
5621
+ enableSkinArrayEmissive(enabled = true) {
5622
+ this._skinArrayEmissive = enabled;
5623
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5624
+ this.plugin?.markAllDefinesAsDirty();
5625
+ this._unityLightingPlugin?.markAllDefinesAsDirty();
5626
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5627
+ return this;
5628
+ }
5629
+ setSkinArrayLayerMode(uniform) {
5630
+ this._skinLayerUniform = uniform;
5631
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5632
+ this._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5633
+ return this;
5634
+ }
5635
+ addTextureArrayUniform(name, texture) {
5636
+ if (this._samplers.indexOf(name) === -1)
5637
+ this._samplers.push(name);
5638
+ this._textures[name] = texture;
5639
+ this.markTextureAsRaw(name);
5640
+ return this;
5641
+ }
5642
+ get skinArray() { return this._skinArray; }
5643
+ get skinArrayNormal() { return this._skinArrayNormal; }
5644
+ get skinArrayMetalRough() { return this._skinArrayMetalRough; }
5645
+ get skinArrayEmissive() { return this._skinArrayEmissive; }
5646
+ get skinArrayActive() {
5647
+ return this._skinArray || this._skinArrayNormal || this._skinArrayMetalRough || this._skinArrayEmissive;
5648
+ }
5649
+ get skinLayerUniform() { return this._skinLayerUniform; }
5650
+ clone(name, cloneTexturesOnlyOnce = true, rootUrl = "") {
5651
+ const scene = this.getScene();
5652
+ const ctor = this.constructor;
5653
+ const result = SerializationHelper.Clone(() => new ctor(name, scene), this, { cloneTexturesOnlyOnce });
5654
+ if (result == null)
5655
+ return result;
5656
+ result.id = name;
5657
+ result.name = name;
5658
+ if (this.stencil && result.stencil)
5659
+ this.stencil.copyTo(result.stencil);
5660
+ this.copyCustomStateTo(result);
5661
+ return result;
5662
+ }
5663
+ copyCustomStateTo(result) {
5664
+ result.universalMaterial = this.universalMaterial;
5665
+ result._defines = Object.assign({}, this._defines);
5666
+ result._uniforms = this._uniforms.slice();
5667
+ result._samplers = this._samplers.slice();
5668
+ result._attributes = this._attributes.slice();
5669
+ result._ubos = (this._ubos != null) ? this._ubos.map((u) => ({ name: u.name, size: u.size, type: u.type, arraySize: u.arraySize })) : null;
5670
+ result._textures = Object.assign({}, this._textures);
5671
+ result._noMatrixTextures = (this._noMatrixTextures != null) ? new Set(this._noMatrixTextures) : null;
5672
+ result._vectors4 = {};
5673
+ for (const n in this._vectors4)
5674
+ result._vectors4[n] = this._vectors4[n].clone();
5675
+ result._vectors3 = {};
5676
+ for (const n in this._vectors3)
5677
+ result._vectors3[n] = this._vectors3[n].clone();
5678
+ result._vectors2 = {};
5679
+ for (const n in this._vectors2)
5680
+ result._vectors2[n] = this._vectors2[n].clone();
5681
+ result._floats = Object.assign({}, this._floats);
5682
+ result._bools = Object.assign({}, this._bools);
5683
+ result._skinArray = this._skinArray;
5684
+ result._skinArrayNormal = this._skinArrayNormal;
5685
+ result._skinArrayMetalRough = this._skinArrayMetalRough;
5686
+ result._skinArrayEmissive = this._skinArrayEmissive;
5687
+ result._skinLayerUniform = this._skinLayerUniform;
5688
+ result.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
5689
+ result.plugin?.markAllDefinesAsDirty();
5690
+ result._unityLightingPlugin?.markAllDefinesAsDirty();
5691
+ result._skinArraySwitchingPlugin?.markAllDefinesAsDirty();
5692
+ }
5570
5693
  addTextureUniform(name, texture) {
5571
5694
  this.checkSampler(name, texture);
5572
5695
  this._textures[name] = texture;
@@ -6009,6 +6132,301 @@ export class UnityStyleLightingPlugin extends CustomShaderMaterialPlugin {
6009
6132
  this.getCustomShaderMaterial().updateCustomBindings(uniformBuffer);
6010
6133
  }
6011
6134
  }
6135
+ export class SkinArraySwitchingPlugin extends CustomShaderMaterialPlugin {
6136
+ constructor(material) {
6137
+ 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);
6138
+ }
6139
+ isCompatible(shaderLanguage) {
6140
+ return (shaderLanguage === ShaderLanguage.WGSL || shaderLanguage === ShaderLanguage.GLSL);
6141
+ }
6142
+ getClassName() {
6143
+ return "SkinArraySwitchingPlugin";
6144
+ }
6145
+ getAttributes(attributes, scene, mesh) {
6146
+ const mat = this.getCustomShaderMaterial();
6147
+ if (!mat.skinArrayActive)
6148
+ return;
6149
+ if (mat.skinLayerUniform !== true && attributes.indexOf("tkSkinLayer") === -1)
6150
+ attributes.push("tkSkinLayer");
6151
+ }
6152
+ getSamplers(samplers) {
6153
+ const mat = this.getCustomShaderMaterial();
6154
+ if (!mat.skinArrayActive)
6155
+ return;
6156
+ const customSamplers = mat.getCustomSamplers();
6157
+ if (customSamplers != null) {
6158
+ for (const s of customSamplers)
6159
+ if (samplers.indexOf(s) === -1)
6160
+ samplers.push(s);
6161
+ }
6162
+ }
6163
+ prepareDefines(defines, scene, mesh) {
6164
+ const mat = this.getCustomShaderMaterial();
6165
+ defines.TOOLKIT_SKIN_ARRAY = !!mat.skinArrayActive;
6166
+ defines.TOOLKIT_SKIN_LAYER_UNIFORM = !!mat.skinLayerUniform;
6167
+ defines.TOOLKIT_SKIN_ARRAY_ALBEDO = !!mat.skinArray;
6168
+ defines.TOOLKIT_SKIN_ARRAY_NORMAL = !!mat.skinArrayNormal;
6169
+ defines.TOOLKIT_SKIN_ARRAY_MR = !!mat.skinArrayMetalRough;
6170
+ defines.TOOLKIT_SKIN_ARRAY_EMISSIVE = !!mat.skinArrayEmissive;
6171
+ }
6172
+ getCustomCode(shaderType, shaderLanguage) {
6173
+ const wgsl = (shaderLanguage === ShaderLanguage.WGSL);
6174
+ if (shaderType === "vertex") {
6175
+ if (wgsl) {
6176
+ return {
6177
+ CUSTOM_VERTEX_DEFINITIONS: this.getWGSLVertexDefinitions(),
6178
+ CUSTOM_VERTEX_MAIN_END: this.getWGSLVertexMainEnd()
6179
+ };
6180
+ }
6181
+ return {
6182
+ CUSTOM_VERTEX_DEFINITIONS: this.getGLSLVertexDefinitions(),
6183
+ CUSTOM_VERTEX_MAIN_END: this.getGLSLVertexMainEnd()
6184
+ };
6185
+ }
6186
+ if (shaderType === "fragment") {
6187
+ if (wgsl) {
6188
+ return {
6189
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getWGSLFragmentDefinitions(),
6190
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getWGSLAlbedoCode() + this.getWGSLMetalRoughSampleCode(),
6191
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getWGSLNormalCode(),
6192
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getWGSLMetalRoughApplyCode(),
6193
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getWGSLEmissiveCode()
6194
+ };
6195
+ }
6196
+ return {
6197
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getGLSLFragmentDefinitions(),
6198
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getGLSLAlbedoCode() + this.getGLSLMetalRoughSampleCode(),
6199
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getGLSLNormalCode(),
6200
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getGLSLMetalRoughApplyCode(),
6201
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getGLSLEmissiveCode()
6202
+ };
6203
+ }
6204
+ return null;
6205
+ }
6206
+ getGLSLVertexDefinitions() {
6207
+ return `
6208
+ #ifdef TOOLKIT_SKIN_ARRAY
6209
+ #ifndef TOOLKIT_SKIN_V_DEFINED
6210
+ #define TOOLKIT_SKIN_V_DEFINED
6211
+ #ifdef TOOLKIT_SKIN_LAYER_UNIFORM
6212
+ uniform float tkSkinLayerUni;
6213
+ #else
6214
+ attribute float tkSkinLayer;
6215
+ #endif
6216
+ varying float vSkinLayer;
6217
+ varying vec2 vSkinUV;
6218
+ #endif
6219
+ #endif
6220
+ `;
6221
+ }
6222
+ getGLSLVertexMainEnd() {
6223
+ return `
6224
+ #ifdef TOOLKIT_SKIN_ARRAY
6225
+ #ifdef UV1
6226
+ #ifdef TOOLKIT_SKIN_LAYER_UNIFORM
6227
+ vSkinLayer = tkSkinLayerUni;
6228
+ #else
6229
+ vSkinLayer = tkSkinLayer;
6230
+ #endif
6231
+ vSkinUV = uv;
6232
+ #else
6233
+ vSkinLayer = 0.0;
6234
+ vSkinUV = vec2(0.0);
6235
+ #endif
6236
+ #endif
6237
+ `;
6238
+ }
6239
+ getGLSLFragmentDefinitions() {
6240
+ return `
6241
+ #ifdef TOOLKIT_SKIN_ARRAY
6242
+ #ifndef TOOLKIT_SKIN_F_DEFINED
6243
+ #define TOOLKIT_SKIN_F_DEFINED
6244
+ varying float vSkinLayer;
6245
+ varying vec2 vSkinUV;
6246
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6247
+ uniform sampler2DArray tkAlbedoArray;
6248
+ #endif
6249
+ #ifdef TOOLKIT_SKIN_ARRAY_NORMAL
6250
+ uniform sampler2DArray tkNormalArray;
6251
+ #endif
6252
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6253
+ uniform sampler2DArray tkMetalRoughArray;
6254
+ vec2 _tkMR;
6255
+ #endif
6256
+ #ifdef TOOLKIT_SKIN_ARRAY_EMISSIVE
6257
+ uniform sampler2DArray tkEmissiveArray;
6258
+ #endif
6259
+ #endif
6260
+ #endif
6261
+ `;
6262
+ }
6263
+ getGLSLAlbedoCode() {
6264
+ return `
6265
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6266
+ {
6267
+ float _tkLayer = floor(vSkinLayer + 0.5);
6268
+ vec4 _tkAlb = texture(tkAlbedoArray, vec3(vSkinUV, _tkLayer));
6269
+ #ifdef GAMMAALBEDO
6270
+ surfaceAlbedo = toLinearSpace(_tkAlb.rgb) * vAlbedoColor.rgb;
6271
+ #else
6272
+ surfaceAlbedo = _tkAlb.rgb * vAlbedoColor.rgb;
6273
+ #endif
6274
+ #ifdef ALPHATEST
6275
+ if (_tkAlb.a < alphaCutOff) discard;
6276
+ #endif
6277
+ }
6278
+ #endif
6279
+ `;
6280
+ }
6281
+ getGLSLNormalCode() {
6282
+ return `
6283
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
6284
+ {
6285
+ vec3 _tkNrmTexel = texture(tkNormalArray, vec3(vSkinUV, floor(vSkinLayer + 0.5))).xyz;
6286
+ normalW = perturbNormal(vTBN, _tkNrmTexel, vBumpInfos.y);
6287
+ }
6288
+ #endif
6289
+ `;
6290
+ }
6291
+ getGLSLMetalRoughSampleCode() {
6292
+ return `
6293
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6294
+ {
6295
+ vec4 _tkMRtexel = texture(tkMetalRoughArray, vec3(vSkinUV, floor(vSkinLayer + 0.5)));
6296
+ _tkMR = vec2(_tkMRtexel.b, _tkMRtexel.g);
6297
+ }
6298
+ #endif
6299
+ `;
6300
+ }
6301
+ getGLSLMetalRoughApplyCode() {
6302
+ return `
6303
+ #if defined(TOOLKIT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
6304
+ metallicRoughness.r = _tkMR.x;
6305
+ metallicRoughness.g = _tkMR.y;
6306
+ #endif
6307
+ `;
6308
+ }
6309
+ getGLSLEmissiveCode() {
6310
+ return `
6311
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_EMISSIVE)
6312
+ {
6313
+ vec3 _tkEmis = texture(tkEmissiveArray, vec3(vSkinUV, floor(vSkinLayer + 0.5))).rgb;
6314
+ finalEmissive = toLinearSpace(_tkEmis);
6315
+ }
6316
+ #endif
6317
+ `;
6318
+ }
6319
+ getWGSLVertexDefinitions() {
6320
+ return `
6321
+ #ifdef TOOLKIT_SKIN_ARRAY
6322
+ #ifndef TOOLKIT_SKIN_LAYER_UNIFORM
6323
+ attribute tkSkinLayer: f32;
6324
+ #endif
6325
+ varying vSkinLayer: f32;
6326
+ varying vSkinUV: vec2<f32>;
6327
+ #endif
6328
+ `;
6329
+ }
6330
+ getWGSLVertexMainEnd() {
6331
+ return `
6332
+ #ifdef TOOLKIT_SKIN_ARRAY
6333
+ #ifdef UV1
6334
+ #ifdef TOOLKIT_SKIN_LAYER_UNIFORM
6335
+ vertexOutputs.vSkinLayer = uniforms.tkSkinLayerUni;
6336
+ #else
6337
+ vertexOutputs.vSkinLayer = vertexInputs.tkSkinLayer;
6338
+ #endif
6339
+ vertexOutputs.vSkinUV = vertexInputs.uv;
6340
+ #else
6341
+ vertexOutputs.vSkinLayer = 0.0;
6342
+ vertexOutputs.vSkinUV = vec2f(0.0);
6343
+ #endif
6344
+ #endif
6345
+ `;
6346
+ }
6347
+ getWGSLFragmentDefinitions() {
6348
+ return `
6349
+ #ifdef TOOLKIT_SKIN_ARRAY
6350
+ varying vSkinLayer: f32;
6351
+ varying vSkinUV: vec2<f32>;
6352
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6353
+ var tkAlbedoArray: texture_2d_array<f32>;
6354
+ var tkAlbedoArraySampler: sampler;
6355
+ #endif
6356
+ #ifdef TOOLKIT_SKIN_ARRAY_NORMAL
6357
+ var tkNormalArray: texture_2d_array<f32>;
6358
+ var tkNormalArraySampler: sampler;
6359
+ #endif
6360
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6361
+ var tkMetalRoughArray: texture_2d_array<f32>;
6362
+ var tkMetalRoughArraySampler: sampler;
6363
+ var<private> _tkMR: vec2<f32>;
6364
+ #endif
6365
+ #ifdef TOOLKIT_SKIN_ARRAY_EMISSIVE
6366
+ var tkEmissiveArray: texture_2d_array<f32>;
6367
+ var tkEmissiveArraySampler: sampler;
6368
+ #endif
6369
+ #endif
6370
+ `;
6371
+ }
6372
+ getWGSLAlbedoCode() {
6373
+ return `
6374
+ #ifdef TOOLKIT_SKIN_ARRAY_ALBEDO
6375
+ {
6376
+ let _tkLayer = i32(fragmentInputs.vSkinLayer + 0.5);
6377
+ let _tkAlb = textureSample(tkAlbedoArray, tkAlbedoArraySampler, fragmentInputs.vSkinUV, _tkLayer);
6378
+ #ifdef GAMMAALBEDO
6379
+ surfaceAlbedo = toLinearSpaceVec3(_tkAlb.rgb) * vAlbedoColor.rgb;
6380
+ #else
6381
+ surfaceAlbedo = _tkAlb.rgb * vAlbedoColor.rgb;
6382
+ #endif
6383
+ #ifdef ALPHATEST
6384
+ if (_tkAlb.a < alphaCutOff) { discard; }
6385
+ #endif
6386
+ }
6387
+ #endif
6388
+ `;
6389
+ }
6390
+ getWGSLNormalCode() {
6391
+ return `
6392
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
6393
+ {
6394
+ let _tkNrmTexel = textureSample(tkNormalArray, tkNormalArraySampler, fragmentInputs.vSkinUV, i32(fragmentInputs.vSkinLayer + 0.5)).rgb;
6395
+ let _tkNrmTBN = mat3x3<f32>(fragmentInputs.vTBN0, fragmentInputs.vTBN1, fragmentInputs.vTBN2);
6396
+ normalW = perturbNormal(_tkNrmTBN, _tkNrmTexel, uniforms.vBumpInfos.y);
6397
+ }
6398
+ #endif
6399
+ `;
6400
+ }
6401
+ getWGSLMetalRoughSampleCode() {
6402
+ return `
6403
+ #ifdef TOOLKIT_SKIN_ARRAY_MR
6404
+ {
6405
+ let _tkMRtexel = textureSample(tkMetalRoughArray, tkMetalRoughArraySampler, fragmentInputs.vSkinUV, i32(fragmentInputs.vSkinLayer + 0.5));
6406
+ _tkMR = vec2<f32>(_tkMRtexel.b, _tkMRtexel.g);
6407
+ }
6408
+ #endif
6409
+ `;
6410
+ }
6411
+ getWGSLMetalRoughApplyCode() {
6412
+ return `
6413
+ #if defined(TOOLKIT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
6414
+ metallicRoughness.r = _tkMR.x;
6415
+ metallicRoughness.g = _tkMR.y;
6416
+ #endif
6417
+ `;
6418
+ }
6419
+ getWGSLEmissiveCode() {
6420
+ return `
6421
+ #if defined(TOOLKIT_SKIN_ARRAY) && defined(TOOLKIT_SKIN_ARRAY_EMISSIVE)
6422
+ {
6423
+ let _tkEmis = textureSample(tkEmissiveArray, tkEmissiveArraySampler, fragmentInputs.vSkinUV, i32(fragmentInputs.vSkinLayer + 0.5)).rgb;
6424
+ finalEmissive = toLinearSpaceVec3(_tkEmis);
6425
+ }
6426
+ #endif
6427
+ `;
6428
+ }
6429
+ }
6012
6430
  export class UniversalShaderMaterial extends CustomShaderMaterial {
6013
6431
  constructor(name, scene) {
6014
6432
  super(name, scene);
@@ -11736,8 +12154,10 @@ export class VertexAnimationMaterial extends CustomShaderMaterial {
11736
12154
  constructor(name, scene) {
11737
12155
  super(name, scene);
11738
12156
  this.controller = null;
11739
- this.runtimeAlbedoSkins = null;
11740
- this.runtimeNormalSkins = null;
12157
+ this._vatSkinArray = false;
12158
+ this._vatSkinArrayNormal = false;
12159
+ this._vatSkinArrayMetalRough = false;
12160
+ this._vatSkinArrayEmissive = false;
11741
12161
  this.shader = this.getShaderName();
11742
12162
  this.addAttribute("g_vatAnim0");
11743
12163
  this.addAttribute("g_vatAnim1");
@@ -11793,97 +12213,90 @@ export class VertexAnimationMaterial extends CustomShaderMaterial {
11793
12213
  cloned.alpha = this.alpha;
11794
12214
  cloned.alphaMode = this.alphaMode;
11795
12215
  cloned.transparencyMode = this.transparencyMode;
11796
- cloned.runtimeAlbedoSkins = this.runtimeAlbedoSkins;
11797
- cloned.runtimeNormalSkins = this.runtimeNormalSkins;
11798
- cloned._notifyPluginOfRuntimeSkinChange();
12216
+ if (this._vatSkinArray) {
12217
+ const arr = this.getTextureValue("tkAlbedoArray");
12218
+ if (arr != null)
12219
+ cloned.addTextureArrayUniform("tkAlbedoArray", arr);
12220
+ cloned.enableVatSkinArray();
12221
+ }
12222
+ if (this._vatSkinArrayNormal) {
12223
+ const narr = this.getTextureValue("tkNormalArray");
12224
+ if (narr != null)
12225
+ cloned.addTextureArrayUniform("tkNormalArray", narr);
12226
+ cloned.enableVatSkinArrayNormal();
12227
+ }
12228
+ if (this._vatSkinArrayMetalRough) {
12229
+ const mrarr = this.getTextureValue("tkMetalRoughArray");
12230
+ if (mrarr != null)
12231
+ cloned.addTextureArrayUniform("tkMetalRoughArray", mrarr);
12232
+ cloned.enableVatSkinArrayMetalRough();
12233
+ }
12234
+ if (this._vatSkinArrayEmissive) {
12235
+ const earr = this.getTextureValue("tkEmissiveArray");
12236
+ if (earr != null)
12237
+ cloned.addTextureArrayUniform("tkEmissiveArray", earr);
12238
+ cloned.enableVatSkinArrayEmissive();
12239
+ }
11799
12240
  return cloned;
11800
12241
  }
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();
12242
+ enableVatSkinArray() {
12243
+ this._vatSkinArray = true;
12244
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12245
+ if (this.plugin)
12246
+ this.plugin.markAllDefinesAsDirty();
12247
+ return this;
11809
12248
  }
11810
- clearRuntimeSkinArrays() {
11811
- this.runtimeAlbedoSkins = null;
11812
- this.runtimeNormalSkins = null;
11813
- this._notifyPluginOfRuntimeSkinChange();
12249
+ disableVatSkinArray() {
12250
+ this._vatSkinArray = false;
12251
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12252
+ if (this.plugin)
12253
+ this.plugin.markAllDefinesAsDirty();
11814
12254
  }
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
- }
12255
+ enableVatSkinArrayNormal() {
12256
+ this._vatSkinArrayNormal = true;
12257
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12258
+ if (this.plugin)
12259
+ this.plugin.markAllDefinesAsDirty();
12260
+ return this;
12261
+ }
12262
+ disableVatSkinArrayNormal() {
12263
+ this._vatSkinArrayNormal = false;
11886
12264
  this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12265
+ if (this.plugin)
12266
+ this.plugin.markAllDefinesAsDirty();
12267
+ }
12268
+ enableVatSkinArrayMetalRough() {
12269
+ this._vatSkinArrayMetalRough = true;
12270
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12271
+ if (this.plugin)
12272
+ this.plugin.markAllDefinesAsDirty();
12273
+ return this;
12274
+ }
12275
+ disableVatSkinArrayMetalRough() {
12276
+ this._vatSkinArrayMetalRough = false;
12277
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12278
+ if (this.plugin)
12279
+ this.plugin.markAllDefinesAsDirty();
12280
+ }
12281
+ enableVatSkinArrayEmissive() {
12282
+ this._vatSkinArrayEmissive = true;
12283
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12284
+ if (this.plugin)
12285
+ this.plugin.markAllDefinesAsDirty();
12286
+ return this;
12287
+ }
12288
+ disableVatSkinArrayEmissive() {
12289
+ this._vatSkinArrayEmissive = false;
12290
+ this.markAsDirty(Constants.MATERIAL_AllDirtyFlag);
12291
+ if (this.plugin)
12292
+ this.plugin.markAllDefinesAsDirty();
12293
+ }
12294
+ get vatSkinArray() { return this._vatSkinArray; }
12295
+ get vatSkinArrayNormal() { return this._vatSkinArrayNormal; }
12296
+ get vatSkinArrayMetalRough() { return this._vatSkinArrayMetalRough; }
12297
+ get vatSkinArrayEmissive() { return this._vatSkinArrayEmissive; }
12298
+ get vatSkinArrayActive() {
12299
+ return this._vatSkinArray || this._vatSkinArrayNormal || this._vatSkinArrayMetalRough || this._vatSkinArrayEmissive;
11887
12300
  }
11888
12301
  }
11889
12302
  export class VertexAnimationController {
@@ -11918,15 +12331,15 @@ export class VertexAnimationController {
11918
12331
  }
11919
12332
  return result;
11920
12333
  }
11921
- static SetSkinIndex(mesh, skinIndex) {
12334
+ static SetAtlasCellIndex(mesh, cellIndex) {
11922
12335
  if (!mesh)
11923
12336
  return;
11924
- mesh.runtimeSkinIndex = Math.max(0, Math.floor(skinIndex || 0));
12337
+ mesh.atlasCellIndex = Math.max(0, Math.floor(cellIndex || 0));
11925
12338
  }
11926
- static GetSkinIndex(mesh) {
12339
+ static GetAtlasCellIndex(mesh) {
11927
12340
  if (!mesh)
11928
12341
  return 0;
11929
- const v = mesh.runtimeSkinIndex;
12342
+ const v = mesh.atlasCellIndex;
11930
12343
  return (typeof v === "number" && isFinite(v) && v >= 0) ? Math.floor(v) : 0;
11931
12344
  }
11932
12345
  static DisposeAll(scene) {
@@ -12010,39 +12423,6 @@ export class VertexAnimationController {
12010
12423
  }
12011
12424
  setSpeed(speed) { this._speed = speed; }
12012
12425
  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
12426
  setCurrentTime(time) {
12047
12427
  if (!this._currentClip)
12048
12428
  return;
@@ -12252,9 +12632,9 @@ export class VertexAnimationController {
12252
12632
  const s = current.settings;
12253
12633
  anim0.set(this._time * s.vertexfps, s.vertexframes, current.fromRow, this._blendWeight);
12254
12634
  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;
12635
+ const cellRaw = imesh.atlasCellIndex;
12636
+ const cell = (typeof cellRaw === "number" && cellRaw >= 0) ? Math.floor(cellRaw) : 0;
12637
+ anim1.w = cell * 2.0 + loopFlag;
12258
12638
  const prev = this.getPreviousRendererClip(rendererGuid);
12259
12639
  if (prev && this._previousClip && this._blendWeight < 0.999 && this._isRendererPositionReady(prev)) {
12260
12640
  const ps = prev.settings;
@@ -12424,17 +12804,17 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12424
12804
  super(customMaterial, shaderName, 100, {
12425
12805
  VERTEXANIMATIONMATERIAL: false,
12426
12806
  VAT_NORMAL_SEPARATE: false,
12427
- RUNTIME_SKINS_ALBEDO: false,
12428
- RUNTIME_SKINS_NORMAL: false,
12429
- RUNTIME_SKINS_NORMAL_DERIVE_TBN: false
12807
+ VAT_SKIN_ARRAY: false,
12808
+ VAT_SKIN_ARRAY_ALBEDO: false,
12809
+ VAT_SKIN_ARRAY_NORMAL: false,
12810
+ VAT_SKIN_ARRAY_MR: false,
12811
+ VAT_SKIN_ARRAY_EMISSIVE: false
12430
12812
  });
12431
12813
  this.vertexAnimations = null;
12432
12814
  this.controller = null;
12433
12815
  this._rendererTargets = [];
12434
12816
  this._targetRendererGuid = "";
12435
12817
  this._placeholderTexture = null;
12436
- this._placeholderAlbedoArray = null;
12437
- this._placeholderNormalArray = null;
12438
12818
  this._cc_posTex = null;
12439
12819
  this._cc_normTex = null;
12440
12820
  this._cc_prevPosTex = null;
@@ -12456,24 +12836,12 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12456
12836
  customMaterial.setTextureValue("vatNormal", this._placeholderTexture);
12457
12837
  customMaterial.setTextureValue("vatPrevPosition", this._placeholderTexture);
12458
12838
  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
12839
  }
12464
12840
  dispose() {
12465
12841
  if (this._placeholderTexture) {
12466
12842
  this._placeholderTexture.dispose();
12467
12843
  this._placeholderTexture = null;
12468
12844
  }
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
12845
  super.dispose();
12478
12846
  }
12479
12847
  isCompatible(shaderLanguage) {
@@ -12487,28 +12855,32 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12487
12855
  CUSTOM_VERTEX_DEFINITIONS: this.getWGSLVertexDefinitions(),
12488
12856
  CUSTOM_VERTEX_UPDATE_POSITION: this.getWGSLVertexPositionCode(),
12489
12857
  CUSTOM_VERTEX_UPDATE_NORMAL: this.getWGSLVertexNormalCode(),
12490
- CUSTOM_VERTEX_MAIN_END: this.getWGSLVertexMainEnd()
12858
+ CUSTOM_VERTEX_MAIN_END: this.getWGSLSkinLayerVaryingCode()
12491
12859
  };
12492
12860
  }
12493
12861
  return {
12494
12862
  CUSTOM_VERTEX_DEFINITIONS: this.getGLSLVertexDefinitions(),
12495
12863
  CUSTOM_VERTEX_UPDATE_POSITION: this.getGLSLVertexPositionCode(),
12496
12864
  CUSTOM_VERTEX_UPDATE_NORMAL: this.getGLSLVertexNormalCode(),
12497
- CUSTOM_VERTEX_MAIN_END: this.getGLSLVertexMainEnd()
12865
+ CUSTOM_VERTEX_MAIN_END: this.getGLSLSkinLayerVaryingCode()
12498
12866
  };
12499
12867
  }
12500
12868
  if (shaderType === "fragment") {
12501
12869
  if (wgsl) {
12502
12870
  return {
12503
- CUSTOM_FRAGMENT_DEFINITIONS: this.getWGSLFragmentDefinitions(),
12504
- CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getWGSLFragmentAlbedoCode(),
12505
- CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getWGSLFragmentNormalCode()
12871
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getWGSLSkinFragmentDefs(),
12872
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getWGSLSkinAlbedoCode() + this.getWGSLSkinMetalRoughSampleCode(),
12873
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getWGSLSkinNormalCode(),
12874
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getWGSLSkinMetalRoughApplyCode(),
12875
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getWGSLSkinEmissiveCode()
12506
12876
  };
12507
12877
  }
12508
12878
  return {
12509
- CUSTOM_FRAGMENT_DEFINITIONS: this.getGLSLFragmentDefinitions(),
12510
- CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getGLSLFragmentAlbedoCode(),
12511
- CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getGLSLFragmentNormalCode()
12879
+ CUSTOM_FRAGMENT_DEFINITIONS: this.getGLSLSkinFragmentDefs(),
12880
+ CUSTOM_FRAGMENT_UPDATE_ALBEDO: this.getGLSLSkinAlbedoCode() + this.getGLSLSkinMetalRoughSampleCode(),
12881
+ CUSTOM_FRAGMENT_BEFORE_LIGHTS: this.getGLSLSkinNormalCode(),
12882
+ CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS: this.getGLSLSkinMetalRoughApplyCode(),
12883
+ CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION: this.getGLSLSkinEmissiveCode()
12512
12884
  };
12513
12885
  }
12514
12886
  return null;
@@ -12523,13 +12895,6 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12523
12895
  const customSamplers = this.getCustomShaderMaterial().getCustomSamplers();
12524
12896
  if (customSamplers != null && customSamplers.length > 0)
12525
12897
  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
12898
  }
12534
12899
  getAttributes(attributes, scene, mesh) {
12535
12900
  const customAttributes = this.getCustomShaderMaterial().getCustomAttributes();
@@ -12545,15 +12910,12 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12545
12910
  defines.VAT_NORMAL_SEPARATE = (mode === "separate");
12546
12911
  const engine = scene ? scene.getEngine() : null;
12547
12912
  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();
12913
+ const matSkin = this.getCustomShaderMaterial();
12914
+ defines.VAT_SKIN_ARRAY = !!(matSkin && matSkin.vatSkinArrayActive);
12915
+ defines.VAT_SKIN_ARRAY_ALBEDO = !!(matSkin && matSkin.vatSkinArray);
12916
+ defines.VAT_SKIN_ARRAY_NORMAL = !!(matSkin && matSkin.vatSkinArrayNormal);
12917
+ defines.VAT_SKIN_ARRAY_MR = !!(matSkin && matSkin.vatSkinArrayMetalRough);
12918
+ defines.VAT_SKIN_ARRAY_EMISSIVE = !!(matSkin && matSkin.vatSkinArrayEmissive);
12557
12919
  }
12558
12920
  bindForSubMesh(uniformBuffer, scene, engine, subMesh) {
12559
12921
  if (!this.getIsEnabled())
@@ -12561,16 +12923,6 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12561
12923
  const renderMesh = subMesh && subMesh.getRenderingMesh ? subMesh.getRenderingMesh() : null;
12562
12924
  this._captureRendererTarget(renderMesh || (subMesh ? subMesh.getMesh() : null));
12563
12925
  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
12926
  }
12575
12927
  setupAnimations(settings, controllerId, rootUrl = "", lazyLoadTextures = false) {
12576
12928
  this.vertexAnimations = settings;
@@ -12823,7 +13175,6 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12823
13175
  }
12824
13176
  bindVATToEffect(effect) {
12825
13177
  this._pushVATUniforms(effect, true);
12826
- this._pushRuntimeSkinSamplers(effect);
12827
13178
  }
12828
13179
  _pushVATUniforms(buf, isEffect) {
12829
13180
  const fb = this._placeholderTexture;
@@ -12896,13 +13247,12 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12896
13247
  uniform sampler2D vatPrevPosition;
12897
13248
  uniform sampler2D vatPrevNormal;
12898
13249
  #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;
13250
+ #ifdef VAT_SKIN_ARRAY
13251
+ #ifndef TOOLKIT_VAT_SKIN_V_DEFINED
13252
+ #define TOOLKIT_VAT_SKIN_V_DEFINED
13253
+ varying float vVatSkinLayer;
13254
+ varying vec2 vVatSkinUV;
13255
+ #endif
12906
13256
  #endif
12907
13257
  `;
12908
13258
  }
@@ -12916,8 +13266,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12916
13266
  float vatPrevFrame = g_vatAnim1.x;
12917
13267
  float vatPrevFrameCount = g_vatAnim1.y;
12918
13268
  float vatPrevFromRow = g_vatAnim1.z;
12919
- float vatPackedSkinLoop = g_vatAnim1.w;
12920
- float vatLoopBlend = mod(vatPackedSkinLoop, 2.0);
13269
+ #ifdef VAT_SKIN_ARRAY
13270
+ float vatLoopBlend = mod(g_vatAnim1.w, 2.0); // low bit is loop-blend, high bits carry the skin layer index
13271
+ #else
13272
+ float vatLoopBlend = g_vatAnim1.w;
13273
+ #endif
12921
13274
  if (vatFrameCount > 0.5) {
12922
13275
  // GLSL path uses the third UV channel:
12923
13276
  // Unity uv3 -> glTF TEXCOORD_2 -> Babylon shader uv3
@@ -12985,9 +13338,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
12985
13338
  float vatNormFrame = g_vatAnim0.x;
12986
13339
  float vatNormFrameCount = g_vatAnim0.y;
12987
13340
  float vatNormFromRow = g_vatAnim0.z;
12988
-
12989
- float vatNormPackedSkinLoop = g_vatAnim1.w;
12990
- float vatNormLoopBlend = mod(vatNormPackedSkinLoop, 2.0);
13341
+ #ifdef VAT_SKIN_ARRAY
13342
+ float vatNormLoopBlend = mod(g_vatAnim1.w, 2.0);
13343
+ #else
13344
+ float vatNormLoopBlend = g_vatAnim1.w;
13345
+ #endif
12991
13346
 
12992
13347
  if (vatNormFrameCount > 0.5) {
12993
13348
  // GLSL path uses the third UV channel:
@@ -13033,9 +13388,10 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13033
13388
  var vatPrevPositionSampler: sampler;
13034
13389
  var vatPrevNormal: texture_2d<f32>;
13035
13390
  var vatPrevNormalSampler: sampler;
13036
- // Runtime-skin varyings — packed skin index and UV0, passed to fragment.
13037
- varying vRuntimeSkinIndex: f32;
13038
- varying vRuntimeSkinUV: vec2<f32>;
13391
+ #endif
13392
+ #ifdef VAT_SKIN_ARRAY
13393
+ varying vVatSkinLayer: f32;
13394
+ varying vVatSkinUV: vec2<f32>;
13039
13395
  #endif
13040
13396
  `;
13041
13397
  }
@@ -13049,8 +13405,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13049
13405
  let vatPrevFrame = vertexInputs.g_vatAnim1.x;
13050
13406
  let vatPrevFrameCount = vertexInputs.g_vatAnim1.y;
13051
13407
  let vatPrevFromRow = vertexInputs.g_vatAnim1.z;
13052
- let packedSkinLoop = vertexInputs.g_vatAnim1.w;
13053
- let vatLoopBlend = packedSkinLoop % 2.0;
13408
+ #ifdef VAT_SKIN_ARRAY
13409
+ let vatLoopBlend = vertexInputs.g_vatAnim1.w % 2.0; // low bit is loop-blend, high bits carry the skin layer index
13410
+ #else
13411
+ let vatLoopBlend = vertexInputs.g_vatAnim1.w;
13412
+ #endif
13054
13413
  if (vatFrameCount > 0.5) {
13055
13414
  // WGSL path derives the VAT texel coordinate from the vertex index instead
13056
13415
  // of consuming a dedicated uv3 vertex-buffer slot.
@@ -13119,8 +13478,11 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13119
13478
  let vatNormFrame = vertexInputs.g_vatAnim0.x;
13120
13479
  let vatNormFrameCount = vertexInputs.g_vatAnim0.y;
13121
13480
  let vatNormFromRow = vertexInputs.g_vatAnim0.z;
13122
- let vatNormPackedSkinLoop = vertexInputs.g_vatAnim1.w;
13123
- let vatNormLoopBlend = vatNormPackedSkinLoop % 2.0;
13481
+ #ifdef VAT_SKIN_ARRAY
13482
+ let vatNormLoopBlend = vertexInputs.g_vatAnim1.w % 2.0;
13483
+ #else
13484
+ let vatNormLoopBlend = vertexInputs.g_vatAnim1.w;
13485
+ #endif
13124
13486
  if (vatNormFrameCount > 0.5) {
13125
13487
  // WGSL path derives the VAT texel coordinate from the vertex index instead
13126
13488
  // of consuming a dedicated uv3 vertex-buffer slot.
@@ -13157,130 +13519,187 @@ export class VertexAnimationMaterialPlugin extends CustomShaderMaterialPlugin {
13157
13519
  #endif
13158
13520
  `;
13159
13521
  }
13160
- getGLSLVertexMainEnd() {
13522
+ getGLSLSkinLayerVaryingCode() {
13161
13523
  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
- }
13524
+ #ifdef VAT_SKIN_ARRAY
13525
+ #ifdef UV1
13526
+ vVatSkinLayer = floor(g_vatAnim1.w * 0.5 + 0.0001);
13527
+ vVatSkinUV = uv;
13528
+ #else
13529
+ vVatSkinLayer = 0.0;
13530
+ vVatSkinUV = vec2(0.0);
13531
+ #endif
13172
13532
  #endif
13173
13533
  `;
13174
13534
  }
13175
- getWGSLVertexMainEnd() {
13535
+ getGLSLSkinFragmentDefs() {
13176
13536
  return `
13177
- #ifdef VERTEXANIMATIONMATERIAL
13537
+ #ifdef VAT_SKIN_ARRAY
13538
+ varying float vVatSkinLayer;
13539
+ varying vec2 vVatSkinUV;
13540
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13541
+ uniform sampler2DArray tkAlbedoArray;
13542
+ #endif
13543
+ #ifdef VAT_SKIN_ARRAY_NORMAL
13544
+ uniform sampler2DArray tkNormalArray;
13545
+ #endif
13546
+ #ifdef VAT_SKIN_ARRAY_MR
13547
+ uniform sampler2DArray tkMetalRoughArray;
13548
+ vec2 _tkMR;
13549
+ #endif
13550
+ #ifdef VAT_SKIN_ARRAY_EMISSIVE
13551
+ uniform sampler2DArray tkEmissiveArray;
13552
+ #endif
13553
+ #endif
13554
+ `;
13555
+ }
13556
+ getGLSLSkinAlbedoCode() {
13557
+ return `
13558
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13178
13559
  {
13179
- let _rsPackedSkinLoop = vertexInputs.g_vatAnim1.w;
13180
- vertexOutputs.vRuntimeSkinIndex = floor(_rsPackedSkinLoop * 0.5 + 0.0001);
13181
- #ifdef UV1
13182
- vertexOutputs.vRuntimeSkinUV = vertexInputs.uv;
13560
+ vec4 _vatSkinAlbedo = texture(tkAlbedoArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5)));
13561
+ // Match Babylon's native albedo: sRGB -> linear when the albedo is gamma-space.
13562
+ #ifdef GAMMAALBEDO
13563
+ surfaceAlbedo = toLinearSpace(_vatSkinAlbedo.rgb) * vAlbedoColor.rgb;
13183
13564
  #else
13184
- vertexOutputs.vRuntimeSkinUV = vec2f(0.0, 0.0);
13565
+ surfaceAlbedo = _vatSkinAlbedo.rgb * vAlbedoColor.rgb;
13566
+ #endif
13567
+ #ifdef ALPHATEST
13568
+ if (_vatSkinAlbedo.a < alphaCutOff) discard;
13185
13569
  #endif
13186
13570
  }
13187
13571
  #endif
13188
13572
  `;
13189
13573
  }
13190
- getGLSLFragmentDefinitions() {
13574
+ getGLSLSkinNormalCode() {
13191
13575
  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;
13576
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
13577
+ {
13578
+ vec3 _vatNrmTexel = texture(tkNormalArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5))).xyz;
13579
+ normalW = perturbNormal(vTBN, _vatNrmTexel, vBumpInfos.y);
13580
+ }
13198
13581
  #endif
13199
13582
  `;
13200
13583
  }
13201
- getGLSLFragmentAlbedoCode() {
13584
+ getGLSLSkinMetalRoughSampleCode() {
13202
13585
  return `
13203
- #ifdef RUNTIME_SKINS_ALBEDO
13586
+ #ifdef VAT_SKIN_ARRAY_MR
13204
13587
  {
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
13588
+ vec4 _vatMRtexel = texture(tkMetalRoughArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5)));
13589
+ _tkMR = vec2(_vatMRtexel.b, _vatMRtexel.g);
13211
13590
  }
13212
13591
  #endif
13213
13592
  `;
13214
13593
  }
13215
- getGLSLFragmentNormalCode() {
13594
+ getGLSLSkinMetalRoughApplyCode() {
13216
13595
  return `
13217
- #ifdef RUNTIME_SKINS_NORMAL
13596
+ #if defined(VAT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
13597
+ metallicRoughness.r = _tkMR.x;
13598
+ metallicRoughness.g = _tkMR.y;
13599
+ #endif
13600
+ `;
13601
+ }
13602
+ getGLSLSkinEmissiveCode() {
13603
+ return `
13604
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_EMISSIVE)
13218
13605
  {
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);
13606
+ vec3 _vatEmis = texture(tkEmissiveArray, vec3(vVatSkinUV, floor(vVatSkinLayer + 0.5))).rgb;
13607
+ finalEmissive = toLinearSpace(_vatEmis);
13235
13608
  }
13236
13609
  #endif
13237
13610
  `;
13238
13611
  }
13239
- getWGSLFragmentDefinitions() {
13612
+ getWGSLSkinLayerVaryingCode() {
13240
13613
  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;
13614
+ #ifdef VAT_SKIN_ARRAY
13615
+ #ifdef UV1
13616
+ vertexOutputs.vVatSkinLayer = floor(vertexInputs.g_vatAnim1.w * 0.5 + 0.0001);
13617
+ vertexOutputs.vVatSkinUV = vertexInputs.uv;
13618
+ #else
13619
+ vertexOutputs.vVatSkinLayer = 0.0;
13620
+ vertexOutputs.vVatSkinUV = vec2f(0.0);
13621
+ #endif
13622
+ #endif
13247
13623
  `;
13248
13624
  }
13249
- getWGSLFragmentAlbedoCode() {
13625
+ getWGSLSkinFragmentDefs() {
13250
13626
  return `
13251
- #ifdef RUNTIME_SKINS_ALBEDO
13627
+ #ifdef VAT_SKIN_ARRAY
13628
+ varying vVatSkinLayer: f32;
13629
+ varying vVatSkinUV: vec2<f32>;
13630
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13631
+ var tkAlbedoArray: texture_2d_array<f32>;
13632
+ var tkAlbedoArraySampler: sampler;
13633
+ #endif
13634
+ #ifdef VAT_SKIN_ARRAY_NORMAL
13635
+ var tkNormalArray: texture_2d_array<f32>;
13636
+ var tkNormalArraySampler: sampler;
13637
+ #endif
13638
+ #ifdef VAT_SKIN_ARRAY_MR
13639
+ var tkMetalRoughArray: texture_2d_array<f32>;
13640
+ var tkMetalRoughArraySampler: sampler;
13641
+ var<private> _tkMR: vec2<f32>;
13642
+ #endif
13643
+ #ifdef VAT_SKIN_ARRAY_EMISSIVE
13644
+ var tkEmissiveArray: texture_2d_array<f32>;
13645
+ var tkEmissiveArraySampler: sampler;
13646
+ #endif
13647
+ #endif
13648
+ `;
13649
+ }
13650
+ getWGSLSkinAlbedoCode() {
13651
+ return `
13652
+ #ifdef VAT_SKIN_ARRAY_ALBEDO
13252
13653
  {
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;
13654
+ let _vatSkinAlbedo = textureSample(tkAlbedoArray, tkAlbedoArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5));
13655
+ // Match Babylon's native albedo: sRGB -> linear when the albedo is gamma-space.
13656
+ #ifdef GAMMAALBEDO
13657
+ surfaceAlbedo = toLinearSpaceVec3(_vatSkinAlbedo.rgb) * vAlbedoColor.rgb;
13658
+ #else
13659
+ surfaceAlbedo = _vatSkinAlbedo.rgb * vAlbedoColor.rgb;
13660
+ #endif
13256
13661
  #ifdef ALPHATEST
13257
- if (_rsAlbedo.a < alphaCutOff) { discard; }
13662
+ if (_vatSkinAlbedo.a < alphaCutOff) { discard; }
13258
13663
  #endif
13259
13664
  }
13260
13665
  #endif
13261
13666
  `;
13262
13667
  }
13263
- getWGSLFragmentNormalCode() {
13668
+ getWGSLSkinNormalCode() {
13669
+ return `
13670
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_NORMAL) && defined(BUMP) && defined(TANGENT) && defined(NORMAL)
13671
+ {
13672
+ let _vatNrmTexel = textureSample(tkNormalArray, tkNormalArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5)).rgb;
13673
+ let _vatNrmTBN = mat3x3<f32>(fragmentInputs.vTBN0, fragmentInputs.vTBN1, fragmentInputs.vTBN2);
13674
+ normalW = perturbNormal(_vatNrmTBN, _vatNrmTexel, uniforms.vBumpInfos.y);
13675
+ }
13676
+ #endif
13677
+ `;
13678
+ }
13679
+ getWGSLSkinMetalRoughSampleCode() {
13264
13680
  return `
13265
- #ifdef RUNTIME_SKINS_NORMAL
13681
+ #ifdef VAT_SKIN_ARRAY_MR
13266
13682
  {
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);
13683
+ let _vatMRtexel = textureSample(tkMetalRoughArray, tkMetalRoughArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5));
13684
+ _tkMR = vec2<f32>(_vatMRtexel.b, _vatMRtexel.g);
13685
+ }
13686
+ #endif
13687
+ `;
13688
+ }
13689
+ getWGSLSkinMetalRoughApplyCode() {
13690
+ return `
13691
+ #if defined(VAT_SKIN_ARRAY_MR) && defined(METALLICWORKFLOW)
13692
+ metallicRoughness.r = _tkMR.x;
13693
+ metallicRoughness.g = _tkMR.y;
13694
+ #endif
13695
+ `;
13696
+ }
13697
+ getWGSLSkinEmissiveCode() {
13698
+ return `
13699
+ #if defined(VAT_SKIN_ARRAY) && defined(VAT_SKIN_ARRAY_EMISSIVE)
13700
+ {
13701
+ let _vatEmis = textureSample(tkEmissiveArray, tkEmissiveArraySampler, fragmentInputs.vVatSkinUV, i32(fragmentInputs.vVatSkinLayer + 0.5)).rgb;
13702
+ finalEmissive = toLinearSpaceVec3(_vatEmis);
13284
13703
  }
13285
13704
  #endif
13286
13705
  `;
@@ -27680,6 +28099,138 @@ ShurikenParticles.UNITY_TO_BABYLON_LIFETIME_RATIO = 0.65;
27680
28099
  ShurikenParticles.UNITY_TO_BABYLON_DEATH_FADE_ALPHA = 0.2;
27681
28100
  ShurikenParticles.EMITTER_POSITION_OFFSET = new Vector3(0, 0, 0);
27682
28101
  ShurikenParticles.EMITTER_ROTATION_OFFSET = new Vector3(90, 0, 0);
28102
+ export class SkinTextureArray {
28103
+ static Build(scene, urls, options = {}) {
28104
+ const requested = (urls || []).filter((u) => u != null && String(u).length > 0);
28105
+ if (requested.length === 0)
28106
+ return Promise.resolve(null);
28107
+ const maxLayers = SkinTextureArray._maxArrayLayers(scene.getEngine());
28108
+ let list = requested;
28109
+ if (requested.length > maxLayers) {
28110
+ console.warn("SkinTextureArray: " + requested.length + " slices exceeds GPU maxTextureArrayLayers ("
28111
+ + maxLayers + ") — clamping to " + maxLayers + ". Skin indices >= " + maxLayers + " will be unavailable.");
28112
+ list = requested.slice(0, maxLayers);
28113
+ }
28114
+ const optKey = "m" + (options.mipmaps === false ? 0 : 1)
28115
+ + "|y" + (options.invertY === true ? 1 : 0)
28116
+ + "|s" + (options.samplingMode != null ? options.samplingMode : "d");
28117
+ const key = list.join("|") + "||" + optKey;
28118
+ if (SkinTextureArray._cache[key] != null)
28119
+ return SkinTextureArray._cache[key];
28120
+ const built = SkinTextureArray._loadImages(list).then((images) => {
28121
+ if (images == null || images.length === 0)
28122
+ return null;
28123
+ const w = images[0].width, h = images[0].height, depth = images.length;
28124
+ const canvas = document.createElement("canvas");
28125
+ canvas.width = w;
28126
+ canvas.height = h;
28127
+ const ctx = canvas.getContext("2d");
28128
+ const data = new Uint8Array(w * h * 4 * depth);
28129
+ const flipY = (options.invertY != null) ? options.invertY : false;
28130
+ for (let i = 0; i < depth; i++) {
28131
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
28132
+ ctx.clearRect(0, 0, w, h);
28133
+ if (flipY)
28134
+ ctx.setTransform(1, 0, 0, -1, 0, h);
28135
+ ctx.drawImage(images[i], 0, 0, w, h);
28136
+ const id = ctx.getImageData(0, 0, w, h).data;
28137
+ data.set(id, i * w * h * 4);
28138
+ }
28139
+ const engine = scene.getEngine();
28140
+ const isWebGPU = (engine != null && engine.isWebGPU === true);
28141
+ const genMips = (options.mipmaps !== false);
28142
+ const mipCount = genMips ? (Math.floor(Math.log2(Math.max(w, h))) + 1) : 1;
28143
+ const samplingMode = (options.samplingMode != null) ? options.samplingMode
28144
+ : (genMips ? Texture.TRILINEAR_SAMPLINGMODE : Texture.BILINEAR_SAMPLINGMODE);
28145
+ const arr = new RawTexture2DArray(data, w, h, depth, Constants.TEXTUREFORMAT_RGBA, scene, genMips, false, samplingMode, Constants.TEXTURETYPE_UNSIGNED_BYTE, 0, mipCount);
28146
+ if (genMips && isWebGPU && mipCount > 1)
28147
+ SkinTextureArray._uploadMipChain(scene, arr, data, w, h, depth);
28148
+ arr.wrapU = Texture.CLAMP_ADDRESSMODE;
28149
+ arr.wrapV = Texture.CLAMP_ADDRESSMODE;
28150
+ arr.anisotropicFilteringLevel = (options.aniso != null) ? options.aniso : 8;
28151
+ return arr;
28152
+ }).catch((e) => { console.warn("SkinTextureArray: build failed", e); delete SkinTextureArray._cache[key]; return null; });
28153
+ SkinTextureArray._cache[key] = built;
28154
+ return built;
28155
+ }
28156
+ static _uploadMipChain(scene, arr, base, w, h, depth) {
28157
+ const engine = scene.getEngine();
28158
+ const itex = arr.getInternalTexture();
28159
+ if (itex == null || typeof engine.updateRawTexture2DArray !== "function")
28160
+ return;
28161
+ const count = Math.floor(Math.log2(Math.max(w, h))) + 1;
28162
+ try {
28163
+ let prev = base, pw = w, ph = h;
28164
+ for (let m = 1; m < count; m++) {
28165
+ const nw = Math.max(1, pw >> 1), nh = Math.max(1, ph >> 1);
28166
+ const mip = SkinTextureArray._downsampleLayers(prev, pw, ph, depth, nw, nh);
28167
+ engine.updateRawTexture2DArray(itex, mip, Constants.TEXTUREFORMAT_RGBA, false, null, Constants.TEXTURETYPE_UNSIGNED_BYTE, m);
28168
+ prev = mip;
28169
+ pw = nw;
28170
+ ph = nh;
28171
+ }
28172
+ }
28173
+ catch (e) {
28174
+ console.warn("SkinTextureArray: mip-chain upload failed, using base level only", e);
28175
+ }
28176
+ }
28177
+ static _downsampleLayers(src, sw, sh, depth, dw, dh) {
28178
+ const out = new Uint8Array(dw * dh * 4 * depth);
28179
+ for (let L = 0; L < depth; L++) {
28180
+ const so = L * sw * sh * 4, doo = L * dw * dh * 4;
28181
+ for (let y = 0; y < dh; y++) {
28182
+ for (let x = 0; x < dw; x++) {
28183
+ const sx = x * 2, sy = y * 2;
28184
+ const sx1 = Math.min(sx + 1, sw - 1), sy1 = Math.min(sy + 1, sh - 1);
28185
+ for (let c = 0; c < 4; c++) {
28186
+ 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];
28187
+ out[doo + (y * dw + x) * 4 + c] = (a + b + e + d) >> 2;
28188
+ }
28189
+ }
28190
+ }
28191
+ }
28192
+ return out;
28193
+ }
28194
+ static _maxArrayLayers(engine) {
28195
+ try {
28196
+ const dev = (engine != null) ? (engine._device || (typeof engine.getDevice === "function" ? engine.getDevice() : null)) : null;
28197
+ if (dev != null && dev.limits != null && typeof dev.limits.maxTextureArrayLayers === "number" && dev.limits.maxTextureArrayLayers > 0) {
28198
+ return dev.limits.maxTextureArrayLayers;
28199
+ }
28200
+ const gl = (engine != null) ? engine._gl : null;
28201
+ if (gl != null && typeof gl.getParameter === "function") {
28202
+ const MAX_ARRAY_TEXTURE_LAYERS = 0x88FF;
28203
+ const v = gl.getParameter(MAX_ARRAY_TEXTURE_LAYERS);
28204
+ if (typeof v === "number" && v > 0)
28205
+ return v;
28206
+ }
28207
+ }
28208
+ catch (e) { }
28209
+ return 256;
28210
+ }
28211
+ static _loadImages(urls) {
28212
+ return Promise.all(urls.map((u) => new Promise((resolve, reject) => {
28213
+ const img = document.createElement("img");
28214
+ img.crossOrigin = "anonymous";
28215
+ img.onload = () => resolve(img);
28216
+ img.onerror = () => reject(new Error("SkinTextureArray: failed to load " + u));
28217
+ img.src = u;
28218
+ })));
28219
+ }
28220
+ static Clear() {
28221
+ const entries = [];
28222
+ for (const k in SkinTextureArray._cache) {
28223
+ if (SkinTextureArray._cache[k] != null)
28224
+ entries.push(SkinTextureArray._cache[k]);
28225
+ }
28226
+ SkinTextureArray._cache = {};
28227
+ for (const p of entries) {
28228
+ p.then((arr) => { if (arr != null)
28229
+ arr.dispose(); }).catch(() => { });
28230
+ }
28231
+ }
28232
+ }
28233
+ SkinTextureArray._cache = {};
27683
28234
  export class MeshNormalProxy {
27684
28235
  constructor() {
27685
28236
  this.blendWithPhysicsNormal = 0.0;
@@ -29254,6 +29805,410 @@ TerrainBuilder.detailChunkMaxTotalChunks = 256;
29254
29805
  TerrainBuilder.meshDetailChunkMode = 1;
29255
29806
  TerrainBuilder.meshDetailChunkTargetInstances = 20000;
29256
29807
  TerrainBuilder.meshDetailChunkWorldSize = 64;
29808
+ export class TextureAtlasSkin extends ScriptComponent {
29809
+ constructor(transform, scene, properties = {}, alias = "TextureAtlasSkin") {
29810
+ super(transform, scene, properties, alias);
29811
+ this.abtractMesh = null;
29812
+ this.textureData = null;
29813
+ this.defaultIndex = 0;
29814
+ this.skinnedParts = [];
29815
+ this.albedoSlices = null;
29816
+ this.normalSlices = null;
29817
+ this.metallicSlices = null;
29818
+ this.occlusionSlices = null;
29819
+ this.emissiveSlices = null;
29820
+ this._applied = false;
29821
+ this._manifest = null;
29822
+ this._currentIndex = 0;
29823
+ this._targets = [];
29824
+ this._clonedMultiMaterial = null;
29825
+ }
29826
+ awake() {
29827
+ this.abtractMesh = this.getAbstractMesh();
29828
+ this.albedoSlices = this.getProperty("albedoSlices", null);
29829
+ this.normalSlices = this.getProperty("normalSlices", null);
29830
+ this.metallicSlices = this.getProperty("metallicSlices", null);
29831
+ this.occlusionSlices = this.getProperty("occlusionSlices", null);
29832
+ this.emissiveSlices = this.getProperty("emissiveSlices", null);
29833
+ window["__" + this.transform.name] = this;
29834
+ }
29835
+ start() {
29836
+ this.applySkins();
29837
+ }
29838
+ applySkins() {
29839
+ if (this._applied)
29840
+ return;
29841
+ const mesh = this.abtractMesh || this.getAbstractMesh();
29842
+ if (mesh == null) {
29843
+ console.warn("TextureAtlasSkin: no abstract mesh on " + this.transform.name);
29844
+ return;
29845
+ }
29846
+ let manifest = this.parseManifest();
29847
+ if (manifest == null) {
29848
+ const sliceCount = this.maxChannelSliceCount();
29849
+ if (sliceCount > 0) {
29850
+ const skins = [];
29851
+ for (let i = 0; i < sliceCount; i++)
29852
+ skins.push({ index: i, name: "skin_" + i });
29853
+ manifest = { skinArray: true, count: sliceCount, skins: skins };
29854
+ }
29855
+ else {
29856
+ console.warn("TextureAtlasSkin: no textureData manifest and no channel slices on " + this.transform.name);
29857
+ return;
29858
+ }
29859
+ }
29860
+ this._manifest = manifest;
29861
+ this._currentIndex = this.clampIndex(this.toInt(this.defaultIndex, 0), "defaultIndex");
29862
+ const isInstanced = this.isInstancedSetup(mesh);
29863
+ const slots = this.getMeshSlots(mesh);
29864
+ const parts = this.skinnedParts || [];
29865
+ if (parts.length === 0) {
29866
+ console.warn("TextureAtlasSkin: no skinnedParts on " + this.transform.name);
29867
+ return;
29868
+ }
29869
+ for (const part of parts) {
29870
+ const targetMat = this.resolvePartMaterial(part);
29871
+ const slot = this.matchSlot(slots, targetMat, part);
29872
+ if (slot == null || !(slot.material instanceof CustomShaderMaterial)) {
29873
+ console.warn("TextureAtlasSkin: skinned part not found / not a CustomShaderMaterial on " + this.transform.name, part);
29874
+ continue;
29875
+ }
29876
+ let material = slot.material;
29877
+ const isVat = (material instanceof VertexAnimationMaterial);
29878
+ const kind = isVat ? "vat" : (isInstanced ? "instanced" : "regular");
29879
+ if (kind === "regular") {
29880
+ material = this.cloneForRegular(mesh, slot, material);
29881
+ }
29882
+ this.configureMaterial(material, manifest, kind);
29883
+ this._targets.push({ material: material, kind: kind });
29884
+ console.warn(">>> TextureAtlasSkin: " + this.transform.name + " — part '" + material.name + "' → " + this.describeKind(kind) + " setup");
29885
+ }
29886
+ this.applyIndexToTargets(mesh, this._currentIndex);
29887
+ this._applied = true;
29888
+ }
29889
+ setSkinIndex(index) {
29890
+ const requested = this.toInt(index, 0);
29891
+ this._currentIndex = this.clampIndex(requested, "skin index");
29892
+ const mesh = this.abtractMesh || this.getAbstractMesh();
29893
+ this.applyIndexToTargets(mesh, this._currentIndex);
29894
+ return this._currentIndex;
29895
+ }
29896
+ clampIndex(index, label) {
29897
+ const count = this.skinCount();
29898
+ const max = Math.max(0, count - 1);
29899
+ const clamped = (index < 0) ? 0 : (index > max ? max : index);
29900
+ if (clamped !== index) {
29901
+ console.warn("TextureAtlasSkin: " + label + " " + index + " out of range [0, " + max + "] on " + this.transform.name + " — clamped to " + clamped);
29902
+ }
29903
+ return clamped;
29904
+ }
29905
+ skinCount() {
29906
+ const m = this._manifest;
29907
+ if (m == null)
29908
+ return this.maxChannelSliceCount();
29909
+ const skins = m.skins || [];
29910
+ if (skins.length > 0)
29911
+ return skins.length;
29912
+ const c = this.toInt(m.count, 0);
29913
+ if (c > 0)
29914
+ return c;
29915
+ return this.maxChannelSliceCount();
29916
+ }
29917
+ maxChannelSliceCount() {
29918
+ const lens = [this.albedoSlices, this.normalSlices, this.metallicSlices, this.occlusionSlices, this.emissiveSlices]
29919
+ .map((s) => (s != null) ? s.length : 0);
29920
+ return Math.max(0, lens[0], lens[1], lens[2], lens[3], lens[4]);
29921
+ }
29922
+ getSkinIndex() {
29923
+ return this._currentIndex;
29924
+ }
29925
+ configureMaterial(material, manifest, kind) {
29926
+ const shared = (kind !== "regular");
29927
+ if (shared && this.isConfigured(material))
29928
+ return;
29929
+ if (shared)
29930
+ this.markConfigured(material);
29931
+ if (kind === "instanced")
29932
+ this.ensureSkinLayerBuffer(this.abtractMesh || this.getAbstractMesh());
29933
+ const albedoUrls = this.albedoChannelUrls(manifest);
29934
+ const normalUrls = this.normalChannelUrls(manifest);
29935
+ const mrUrls = this.metalRoughChannelUrls(manifest);
29936
+ const emissiveUrls = this.emissiveChannelUrls(manifest);
29937
+ const has = (u) => (u != null && u.length > 0);
29938
+ if (!has(albedoUrls) && !has(normalUrls) && !has(mrUrls) && !has(emissiveUrls)) {
29939
+ console.warn("TextureAtlasSkin: no channel slices to build a skin array on " + this.transform.name);
29940
+ return;
29941
+ }
29942
+ if (kind !== "vat")
29943
+ material.setSkinArrayLayerMode(kind === "regular");
29944
+ const build = (urls, mipped) => has(urls) ? SkinTextureArray.Build(this.scene, urls, mipped ? {} : { mipmaps: false }) : Promise.resolve(null);
29945
+ Promise.all([build(albedoUrls, true), build(normalUrls, false), build(mrUrls, true), build(emissiveUrls, true)])
29946
+ .then(([albTex, nrmTex, mrTex, emisTex]) => {
29947
+ const vmat = (kind === "vat") ? material : null;
29948
+ if (albTex != null) {
29949
+ material.addTextureArrayUniform("tkAlbedoArray", albTex);
29950
+ if (vmat != null)
29951
+ vmat.enableVatSkinArray();
29952
+ else
29953
+ material.enableSkinArray(kind === "regular");
29954
+ }
29955
+ if (nrmTex != null) {
29956
+ material.addTextureArrayUniform("tkNormalArray", nrmTex);
29957
+ if (vmat != null)
29958
+ vmat.enableVatSkinArrayNormal();
29959
+ else
29960
+ material.enableSkinArrayNormal();
29961
+ }
29962
+ if (mrTex != null) {
29963
+ material.addTextureArrayUniform("tkMetalRoughArray", mrTex);
29964
+ if (vmat != null)
29965
+ vmat.enableVatSkinArrayMetalRough();
29966
+ else
29967
+ material.enableSkinArrayMetalRough();
29968
+ }
29969
+ if (emisTex != null) {
29970
+ material.addTextureArrayUniform("tkEmissiveArray", emisTex);
29971
+ if (vmat != null)
29972
+ vmat.enableVatSkinArrayEmissive();
29973
+ else
29974
+ material.enableSkinArrayEmissive();
29975
+ }
29976
+ if (albTex == null && nrmTex == null && mrTex == null && emisTex == null) {
29977
+ console.warn("TextureAtlasSkin: all channel arrays failed to build on " + this.transform.name);
29978
+ return;
29979
+ }
29980
+ this.applyLayerToMaterial(material, kind, this._currentIndex);
29981
+ });
29982
+ }
29983
+ albedoChannelUrls(manifest) {
29984
+ const fromSlices = this.sliceUrls(this.albedoSlices);
29985
+ if (fromSlices != null)
29986
+ return fromSlices;
29987
+ const layers = (manifest != null) ? manifest.albedoLayers : null;
29988
+ if (Array.isArray(layers)) {
29989
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
29990
+ return (out.length > 0) ? out : null;
29991
+ }
29992
+ return null;
29993
+ }
29994
+ normalChannelUrls(manifest) {
29995
+ const fromSlices = this.sliceUrls(this.normalSlices);
29996
+ if (fromSlices != null)
29997
+ return fromSlices;
29998
+ const layers = (manifest != null) ? manifest.normalLayers : null;
29999
+ if (Array.isArray(layers)) {
30000
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
30001
+ return (out.length > 0) ? out : null;
30002
+ }
30003
+ return null;
30004
+ }
30005
+ metalRoughChannelUrls(manifest) {
30006
+ const fromSlices = this.sliceUrls(this.metallicSlices);
30007
+ if (fromSlices != null)
30008
+ return fromSlices;
30009
+ const layers = (manifest != null) ? manifest.metalRoughLayers : null;
30010
+ if (Array.isArray(layers)) {
30011
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
30012
+ return (out.length > 0) ? out : null;
30013
+ }
30014
+ return null;
30015
+ }
30016
+ emissiveChannelUrls(manifest) {
30017
+ const fromSlices = this.sliceUrls(this.emissiveSlices);
30018
+ if (fromSlices != null)
30019
+ return fromSlices;
30020
+ const layers = (manifest != null) ? manifest.emissiveLayers : null;
30021
+ if (Array.isArray(layers)) {
30022
+ const out = layers.filter((x) => x != null && String(x).length > 0).map((x) => this.resolveLayerUrl(String(x)));
30023
+ return (out.length > 0) ? out : null;
30024
+ }
30025
+ return null;
30026
+ }
30027
+ sliceUrls(slices) {
30028
+ if (slices == null || slices.length === 0)
30029
+ return null;
30030
+ const root = SceneManager.GetRootUrl(this.scene) || "";
30031
+ const urls = [];
30032
+ for (const s of slices) {
30033
+ if (s == null)
30034
+ continue;
30035
+ const anyS = s;
30036
+ let u = null;
30037
+ if (anyS.url != null && String(anyS.url).length > 0)
30038
+ u = String(anyS.url);
30039
+ else if (anyS.filename != null && String(anyS.filename).length > 0)
30040
+ u = root + String(anyS.filename);
30041
+ if (u != null && u.length > 0)
30042
+ urls.push(u);
30043
+ }
30044
+ return (urls.length > 0) ? urls : null;
30045
+ }
30046
+ resolveLayerUrl(filename) {
30047
+ if (filename == null)
30048
+ return "";
30049
+ const f = String(filename);
30050
+ if (/^(https?:|blob:|data:|\/)/i.test(f))
30051
+ return f;
30052
+ const root = SceneManager.GetRootUrl(this.scene) || "";
30053
+ return root + f;
30054
+ }
30055
+ applyIndexToTargets(mesh, index) {
30056
+ if (mesh == null)
30057
+ return;
30058
+ for (const t of this._targets) {
30059
+ this.applyLayerToMaterial(t.material, t.kind, index);
30060
+ }
30061
+ }
30062
+ applyLayerToMaterial(material, kind, index) {
30063
+ if (kind === "vat") {
30064
+ VertexAnimationController.SetAtlasCellIndex(this.abtractMesh || this.getAbstractMesh(), index);
30065
+ }
30066
+ else if (kind === "instanced") {
30067
+ const mesh = this.abtractMesh || this.getAbstractMesh();
30068
+ this.ensureSkinLayerBuffer(mesh);
30069
+ mesh.instancedBuffers.tkSkinLayer = index;
30070
+ }
30071
+ else {
30072
+ material.setSkinLayer(index);
30073
+ }
30074
+ }
30075
+ isInstancedSetup(mesh) {
30076
+ if (mesh instanceof InstancedMesh)
30077
+ return true;
30078
+ const src = (mesh instanceof Mesh) ? mesh : null;
30079
+ return (src != null && src.instances != null && src.instances.length > 0);
30080
+ }
30081
+ ensureSkinLayerBuffer(mesh) {
30082
+ const inst = mesh;
30083
+ const src = (inst != null && inst.sourceMesh != null) ? inst.sourceMesh : mesh;
30084
+ const target = (src != null) ? src : mesh;
30085
+ if (target != null && typeof target.registerInstancedBuffer === "function") {
30086
+ const ib = target.instancedBuffers;
30087
+ if (ib == null || ib.tkSkinLayer === undefined) {
30088
+ target.registerInstancedBuffer("tkSkinLayer", 1);
30089
+ target.instancedBuffers = target.instancedBuffers || {};
30090
+ target.instancedBuffers.tkSkinLayer = 0;
30091
+ }
30092
+ }
30093
+ mesh.instancedBuffers = mesh.instancedBuffers || {};
30094
+ }
30095
+ getMeshSlots(mesh) {
30096
+ const slots = [];
30097
+ const mat = mesh.material;
30098
+ if (mat instanceof MultiMaterial) {
30099
+ const subs = mat.subMaterials;
30100
+ for (let i = 0; i < subs.length; i++) {
30101
+ if (subs[i] != null)
30102
+ slots.push({ material: subs[i], mm: mat, index: i });
30103
+ }
30104
+ }
30105
+ else if (mat != null) {
30106
+ slots.push({ material: mat, mm: null, index: -1 });
30107
+ }
30108
+ return slots;
30109
+ }
30110
+ matchSlot(slots, targetMat, part) {
30111
+ if (targetMat != null) {
30112
+ for (const s of slots)
30113
+ if (s.material === targetMat)
30114
+ return s;
30115
+ }
30116
+ const name = this.partName(part) || (targetMat != null ? targetMat.name : null);
30117
+ if (name != null && name.length > 0) {
30118
+ for (const s of slots)
30119
+ if (s.material.name === name)
30120
+ return s;
30121
+ for (const s of slots)
30122
+ if (s.material.name.indexOf(name) !== -1)
30123
+ return s;
30124
+ }
30125
+ if (slots.length === 1)
30126
+ return slots[0];
30127
+ return null;
30128
+ }
30129
+ resolvePartMaterial(part) {
30130
+ if (part == null)
30131
+ return null;
30132
+ if (part instanceof Material)
30133
+ return part;
30134
+ if (part.skinPart != null)
30135
+ return this.resolvePartMaterial(part.skinPart);
30136
+ const name = this.partName(part);
30137
+ if (name == null)
30138
+ return null;
30139
+ let mat = this.scene.getMaterialByName(name);
30140
+ if (mat == null && SceneManager.GetMaterialWithName != null)
30141
+ mat = SceneManager.GetMaterialWithName(this.scene, name);
30142
+ return mat;
30143
+ }
30144
+ partName(part) {
30145
+ if (part == null)
30146
+ return null;
30147
+ if (typeof part === "string")
30148
+ return part;
30149
+ if (part instanceof Material)
30150
+ return part.name;
30151
+ if (part.skinPart != null)
30152
+ return this.partName(part.skinPart);
30153
+ if (part.name != null)
30154
+ return part.name;
30155
+ if (part.material != null)
30156
+ return this.partName(part.material);
30157
+ return null;
30158
+ }
30159
+ cloneForRegular(mesh, slot, material) {
30160
+ const tag = "_skin_" + (this.transform != null ? this.transform.name : "skin");
30161
+ const cloned = material.clone(material.name + tag);
30162
+ if (!(cloned instanceof CustomShaderMaterial)) {
30163
+ console.warn("TextureAtlasSkin: clone did not preserve CustomShaderMaterial — using original (skin may be shared)", cloned);
30164
+ return material;
30165
+ }
30166
+ if (slot.mm != null) {
30167
+ if (this._clonedMultiMaterial == null) {
30168
+ this._clonedMultiMaterial = slot.mm.clone(slot.mm.name + tag);
30169
+ mesh.material = this._clonedMultiMaterial;
30170
+ }
30171
+ this._clonedMultiMaterial.subMaterials[slot.index] = cloned;
30172
+ }
30173
+ else {
30174
+ mesh.material = cloned;
30175
+ }
30176
+ return cloned;
30177
+ }
30178
+ parseManifest() {
30179
+ let data = this.textureData;
30180
+ if (data == null)
30181
+ return null;
30182
+ if (typeof data === "string") {
30183
+ try {
30184
+ data = JSON.parse(data);
30185
+ }
30186
+ catch (e) {
30187
+ console.warn("TextureAtlasSkin: bad textureData JSON", e);
30188
+ return null;
30189
+ }
30190
+ }
30191
+ return (data != null && data.skins != null) ? data : null;
30192
+ }
30193
+ toInt(v, def) {
30194
+ const n = (typeof v === "number") ? v : parseInt(v, 10);
30195
+ return isNaN(n) ? def : Math.floor(n);
30196
+ }
30197
+ isConfigured(material) {
30198
+ return !!(material && material.metadata && material.metadata.atlasSkinConfigured === true);
30199
+ }
30200
+ markConfigured(material) {
30201
+ material.metadata = material.metadata || {};
30202
+ material.metadata.atlasSkinConfigured = true;
30203
+ }
30204
+ describeKind(kind) {
30205
+ if (kind === "vat")
30206
+ return "VAT Mesh (packed cell index)";
30207
+ if (kind === "instanced")
30208
+ return "Instanced Mesh (per-instance UV buffer)";
30209
+ return "Babylon Mesh (cloned material uniform)";
30210
+ }
30211
+ }
29257
30212
  export class UserInterface extends ScriptComponent {
29258
30213
  constructor(transform, scene, properties = {}, alias = "UserInterface") {
29259
30214
  super(transform, scene, properties, alias);
@@ -30775,6 +31730,7 @@ SceneManager.RegisterClass("CustomShaderMaterial", CustomShaderMaterial);
30775
31730
  SceneManager.RegisterClass("UniversalShaderMaterial", UniversalShaderMaterial);
30776
31731
  SceneManager.RegisterClass("UnityStyleLightingPlugin", UnityStyleLightingPlugin);
30777
31732
  SceneManager.RegisterClass("CustomShaderMaterialPlugin", CustomShaderMaterialPlugin);
31733
+ SceneManager.RegisterClass("SkinArraySwitchingPlugin", SkinArraySwitchingPlugin);
30778
31734
  SceneManager.RegisterClass("StandardShaderMaterial", StandardShaderMaterial);
30779
31735
  SceneManager.RegisterClass("StandardShaderMaterialPlugin", StandardShaderMaterialPlugin);
30780
31736
  SceneManager.RegisterClass("UniversalTerrainMaterial", UniversalTerrainMaterial);
@@ -41407,7 +42363,9 @@ SceneManager.RegisterClass("NavigationAgent", NavigationAgent);
41407
42363
  SceneManager.RegisterClass("PostProcessor", PostProcessor);
41408
42364
  SceneManager.RegisterClass("RigidbodyPhysics", RigidbodyPhysics);
41409
42365
  SceneManager.RegisterClass("ShurikenParticles", ShurikenParticles);
42366
+ SceneManager.RegisterClass("SkinTextureArray", SkinTextureArray);
41410
42367
  SceneManager.RegisterClass("TerrainBuilder", TerrainBuilder);
42368
+ SceneManager.RegisterClass("TextureAtlasSkin", TextureAtlasSkin);
41411
42369
  SceneManager.RegisterClass("UnitySlider", UnitySlider);
41412
42370
  SceneManager.RegisterClass("UnityScrollBar", UnityScrollBar);
41413
42371
  SceneManager.RegisterClass("UnityDropdownMenu", UnityDropdownMenu);