babylonjs-toolkit 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.
@@ -7,7 +7,7 @@ declare namespace TOOLKIT {
7
7
  * @class SceneManager - All rights reserved (c) 2024 Mackey Kinard
8
8
  */
9
9
  class SceneManager {
10
- /** Gets the toolkit framework version string (9.12.1 - R1) */
10
+ /** Gets the toolkit framework version string (9.12.1011 - R1) */
11
11
  static get Version(): string;
12
12
  /** Gets the toolkit framework copyright notice */
13
13
  static get Copyright(): string;
@@ -1066,9 +1066,9 @@ declare namespace TOOLKIT {
1066
1066
  hasSkinnedMesh(): boolean;
1067
1067
  /** Gets the safe transform skinned mesh entity */
1068
1068
  getSkinnedMesh(): BABYLON.AbstractMesh;
1069
- /** Gets the safe transform mesh entity */
1069
+ /** Gets the safe transform mesh entity (Fast Skinned Mesh Check) */
1070
1070
  getTransformMesh(): BABYLON.Mesh;
1071
- /** Gets the safe transform abstract mesh entity */
1071
+ /** Gets the safe transform abstract mesh entity (Fast Skinned Mesh Check) */
1072
1072
  getAbstractMesh(): BABYLON.AbstractMesh;
1073
1073
  /** Gets the safe transform instanced mesh entity */
1074
1074
  getInstancedMesh(): BABYLON.InstancedMesh;
@@ -1974,6 +1974,38 @@ declare namespace TOOLKIT {
1974
1974
  * nulling the value alone is not enough - we must also flag it as computed to stop regeneration.
1975
1975
  */
1976
1976
  static SuppressReflectionProbeDiffuse(texture: BABYLON.BaseTexture): void;
1977
+ /**
1978
+ * Applies the scene's global Unity ambient spherical-harmonics irradiance to a local reflection probe so the
1979
+ * probe contributes Unity-style specular reflections while its diffuse ambient comes from the SAME global SH
1980
+ * the rest of the scene uses - instead of being derived from (or suppressed on) the probe's own cube faces.
1981
+ * Babylon reads diffuse irradiance from the material's own reflectionTexture, so without this a probe-bearing
1982
+ * material would lose all baked IBL ambient. When no global SH is available we fall back to specular-only
1983
+ * (suppressed diffuse), preserving the previous Unity behavior.
1984
+ */
1985
+ static ApplyGlobalReflectionProbeDiffuse(texture: BABYLON.BaseTexture, scene: BABYLON.Scene, createPolynomialsFromFaces?: boolean): void;
1986
+ /** Tracks whether the global box-projection reflection shader fix has been installed (install-once). */
1987
+ private static _boxProjectionShaderPatched;
1988
+ /**
1989
+ * Installs a Unity-faithful per-pixel containment test into Babylon's shared `parallaxCorrectNormal`
1990
+ * box-projection helper (patches both the GLSL and WGSL copies of the `helperFunctions` shader include).
1991
+ *
1992
+ * Babylon applies box (local cubemap) projection to EVERY pixel of a material that has a reflection
1993
+ * bounding box, with no skybox fallback. Surfaces that sit OUTSIDE the probe box - e.g. a tall building
1994
+ * reflecting a ground-level racetrack probe - therefore hit the slab-intersection math in its invalid
1995
+ * regime (exit distance <= 0) and sample the dark box floor, turning glossy/metallic surfaces black.
1996
+ *
1997
+ * Unity instead evaluates probe contribution per-pixel: "When a pixel of an object is outside of any
1998
+ * reflection probe volume, Unity uses the skybox reflection." This injects exactly that rule - if the
1999
+ * shaded world position is outside the probe box on any axis, return the un-projected (infinite)
2000
+ * reflection vector so the pixel reflects the environment (the probe's sky hemisphere) instead of the
2001
+ * dark face. Pixels INSIDE the box are byte-for-byte unchanged, so ground reflections keep their
2002
+ * parallax correction.
2003
+ *
2004
+ * The patch is install-once, idempotent (sentinel guarded) and FAIL-SAFE: if a future Babylon release
2005
+ * renames or reformats the helper so the anchor is not found, it logs a warning and leaves the stock
2006
+ * shader untouched (reverting to Babylon's default behavior) rather than emitting a broken shader.
2007
+ * See README.md ("Box-Projected Reflection Shader Patch") for the full rationale.
2008
+ */
1977
2009
  /** Defines whether this extension is enabled. */
1978
2010
  enabled: boolean;
1979
2011
  private _webgpu;
@@ -2154,6 +2186,33 @@ declare namespace TOOLKIT {
2154
2186
  protected shader: string;
2155
2187
  protected plugin: BABYLON.MaterialPluginBase;
2156
2188
  private _unityLightingPlugin;
2189
+ /** Per-skin Texture2DArray switching — opt-in via enableSkinArray(). One shared mesh + an N-layer
2190
+ * albedo array renders a different "skin" per layer, selected by tkSkinLayer. Unlike the atlas path
2191
+ * there is NO UV remap: the surface UVs are used as-is and the layer index picks the slice, so every
2192
+ * skin keeps full resolution with its OWN clean mip chain (no atlas cross-cell bleed). */
2193
+ private _skinArray;
2194
+ /** Per-skin NORMAL Texture2DArray switching — opt-in via enableSkinArrayNormal() once a tkNormalArray
2195
+ * is assigned. The fragment overrides normalW at CUSTOM_FRAGMENT_BEFORE_LIGHTS by running Babylon's
2196
+ * OWN perturbNormal() on its OWN interpolated vTBN with our slice texel substituted for the bump-
2197
+ * sampler fetch — i.e. byte-for-byte the native tangent-space bump, no reinvented/derivative TBN
2198
+ * (that derivative frame is what specular-speckled glossy metal). The normal array is built WITHOUT
2199
+ * mips (box-downsampled normals de-normalize and grain on WebGPU) and kept linear. */
2200
+ private _skinArrayNormal;
2201
+ /** Per-skin METALLIC-ROUGHNESS Texture2DArray switching — opt-in via enableSkinArrayMetalRough() once a
2202
+ * tkMetalRoughArray is assigned. Its hook (CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS) is INSIDE Babylon's
2203
+ * reflectivityBlock where UVs/samplers aren't in scope, so the slice is sampled in main() (UPDATE_ALBEDO)
2204
+ * into a module-global and READ at the hook: metallicRoughness.r=metallic (slice .B), .g=roughness
2205
+ * (slice .G), matching the glTF MR layout. Built MIPPED + linear (roughness wants mip filtering). */
2206
+ private _skinArrayMetalRough;
2207
+ /** Per-skin EMISSIVE Texture2DArray switching — opt-in via enableSkinArrayEmissive() once a tkEmissiveArray
2208
+ * is assigned. Overrides finalEmissive at CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION with
2209
+ * toLinearSpace(slice) (slices are sRGB-authored, mipped). finalEmissive exists even with NO base emissive
2210
+ * on the material, so this drives emissive-only swaps (e.g. brake lights on/off) with no albedo needed. */
2211
+ private _skinArrayEmissive;
2212
+ /** When true tkSkinLayer is a shared material uniform (one skin per mesh); otherwise a per-instance
2213
+ * vertex attribute (per-instance skins on a hardware-instanced mesh). Independent of which channels are on. */
2214
+ private _skinLayerUniform;
2215
+ private _skinArraySwitchingPlugin;
2157
2216
  getPlugin(): BABYLON.MaterialPluginBase;
2158
2217
  getClassName(): string;
2159
2218
  constructor(name: string, scene: BABYLON.Scene);
@@ -2166,6 +2225,102 @@ declare namespace TOOLKIT {
2166
2225
  checkSampler(samplerName: string, texture?: any): void;
2167
2226
  /** Splits the IBL lighting contributions */
2168
2227
  splitLighting(diffuseIbl: number, specularIbl: number): void;
2228
+ /**
2229
+ * Enable (or disable) per-skin Texture2DArray switching for this material (albedo channel).
2230
+ *
2231
+ * One shared mesh + an N-layer albedo array renders a different "skin" per layer. The surface UVs are
2232
+ * used as-is (NO atlas remap); a single layer index selects the slice:
2233
+ *
2234
+ * surfaceAlbedo = texture(tkAlbedoArray, vec3(uv, tkSkinLayer))
2235
+ *
2236
+ * Each layer is a full-resolution slice with its OWN clean mip chain — no atlas cross-cell bleed.
2237
+ * Assign the array first with addTextureArrayUniform("tkAlbedoArray", array), then call this to gate
2238
+ * the shader. Disabled by default — a material that never opts in compiles to identical code (every
2239
+ * injected line is gated behind TOOLKIT_SKIN_ARRAY).
2240
+ *
2241
+ * Layer source:
2242
+ * - Shared uniform (layerUniform = true): tkSkinLayer is one material uniform (setSkinLayer) — the
2243
+ * only mode that works on a NON-instanced mesh (one skin per mesh).
2244
+ * - Per-instance (layerUniform = false): tkSkinLayer is a per-instance vertex attribute — register
2245
+ * mesh.registerInstancedBuffer("tkSkinLayer", 1) and set inst.instancedBuffers.tkSkinLayer = i.
2246
+ */
2247
+ enableSkinArray(layerUniform?: boolean): TOOLKIT.CustomShaderMaterial;
2248
+ /** Set the shared per-skin array layer (uniform mode — one skin for the whole mesh). No shader
2249
+ * rebuild; just updates the uniform. For per-instance skins set inst.instancedBuffers.tkSkinLayer. */
2250
+ setSkinLayer(layer: number): TOOLKIT.CustomShaderMaterial;
2251
+ /**
2252
+ * Enable (or disable) per-skin NORMAL Texture2DArray switching for this material.
2253
+ *
2254
+ * Assign the array first with addTextureArrayUniform("tkNormalArray", array), then call this. The
2255
+ * fragment overrides normalW at CUSTOM_FRAGMENT_BEFORE_LIGHTS using Babylon's native tangent-space
2256
+ * bump EXACTLY — its own perturbNormal() on its own vTBN, only the sampled texel swapped for our
2257
+ * slice. It is therefore only active where the native tangent frame exists: BUMP && TANGENT && NORMAL
2258
+ * (i.e. the base material has a normal/bump map and the geometry has tangents — true for skinned
2259
+ * characters). Where that frame is absent the override compiles out and the native normal stands.
2260
+ * Fully INDEPENDENT of the other channels: the shared vSkinLayer/vSkinUV varyings are forwarded whenever
2261
+ * ANY skin channel is enabled, so normal can be used with NO albedo array (and vice-versa).
2262
+ */
2263
+ enableSkinArrayNormal(enabled?: boolean): TOOLKIT.CustomShaderMaterial;
2264
+ /**
2265
+ * Enable (or disable) per-skin METALLIC-ROUGHNESS Texture2DArray switching. Assign the array first with
2266
+ * addTextureArrayUniform("tkMetalRoughArray", array), then call this. The slice is sampled in main() and
2267
+ * applied at CUSTOM_FRAGMENT_UPDATE_METALLICROUGHNESS as metallicRoughness.r=metallic (slice .B) and
2268
+ * .g=roughness (slice .G) — the glTF MR channel layout. Only active under METALLICWORKFLOW (PBR metallic).
2269
+ * Independent of every other channel (shared varyings forward whenever any channel is on).
2270
+ */
2271
+ enableSkinArrayMetalRough(enabled?: boolean): TOOLKIT.CustomShaderMaterial;
2272
+ /**
2273
+ * Enable (or disable) per-skin EMISSIVE Texture2DArray switching. Assign the array first with
2274
+ * addTextureArrayUniform("tkEmissiveArray", array), then call this. Overrides finalEmissive at
2275
+ * CUSTOM_FRAGMENT_BEFORE_FINALCOLORCOMPOSITION with toLinearSpace(slice). Works with NO base emissive on
2276
+ * the material, so it can drive emissive-only swaps (e.g. brake lights on/off) standalone. Independent of
2277
+ * every other channel.
2278
+ */
2279
+ enableSkinArrayEmissive(enabled?: boolean): TOOLKIT.CustomShaderMaterial;
2280
+ /**
2281
+ * Set the per-skin layer DELIVERY mode independently of which channels are enabled: uniform = true drives
2282
+ * a single shared layer for the whole mesh (tkSkinLayerUni, set via setSkinLayer) — the only mode that
2283
+ * works on a non-instanced mesh; uniform = false reads a per-instance tkSkinLayer vertex attribute. Call
2284
+ * this once for the mesh (regardless of channel mix) so e.g. an emissive-only mesh still selects layers.
2285
+ */
2286
+ setSkinArrayLayerMode(uniform: boolean): TOOLKIT.CustomShaderMaterial;
2287
+ /** Register a Texture2DArray sampler (sampler2DArray / texture_2d_array) for skin switching. The
2288
+ * declaration is emitted by SkinArraySwitchingPlugin (co-gated with its usage); here we only register
2289
+ * the name for binding and store the texture for upload. Marked raw — arrays carry no UV transform. */
2290
+ addTextureArrayUniform(name: string, texture: BABYLON.BaseTexture): TOOLKIT.CustomShaderMaterial;
2291
+ /** True when per-skin ALBEDO Texture2DArray switching is enabled (see enableSkinArray). */
2292
+ get skinArray(): boolean;
2293
+ /** True when per-skin NORMAL Texture2DArray switching is enabled (see enableSkinArrayNormal). */
2294
+ get skinArrayNormal(): boolean;
2295
+ /** True when per-skin METALLIC-ROUGHNESS switching is enabled (see enableSkinArrayMetalRough). */
2296
+ get skinArrayMetalRough(): boolean;
2297
+ /** True when per-skin EMISSIVE switching is enabled (see enableSkinArrayEmissive). */
2298
+ get skinArrayEmissive(): boolean;
2299
+ /** True when ANY per-skin channel is enabled — the master gate for the shared layer/UV varyings + the
2300
+ * per-instance tkSkinLayer attribute, so any single channel works without the others. */
2301
+ get skinArrayActive(): boolean;
2302
+ /** True when tkSkinLayer is a shared material uniform rather than a per-instance vertex attribute. */
2303
+ get skinLayerUniform(): boolean;
2304
+ /**
2305
+ * Clone this material, preserving the CustomShaderMaterial SUBCLASS and ALL toolkit state — the
2306
+ * custom uniforms / samplers / attributes, the IBL split, and the per-skin Texture2DArray switching
2307
+ * configuration. Each clone is independent: it can hold its own skin (layer index) without affecting
2308
+ * the source material, which makes per-mesh skin selection on NON-instanced meshes safe.
2309
+ *
2310
+ * BabylonJS PBRMaterial.clone() builds a plain PBRMaterial through its own factory, which would
2311
+ * drop the toolkit shader hooks and the skin-array API. Here we instead:
2312
+ * 1. construct the correct subclass via `this.constructor` so the two material plugins
2313
+ * (UnityStyleLightingPlugin, SkinArraySwitchingPlugin) and the base UBO uniforms are recreated
2314
+ * FRESH and bound to the clone — we deliberately do NOT plugin-clone the source's plugins;
2315
+ * 2. copy the standard PBR surface properties with SerializationHelper (shares texture refs);
2316
+ * 3. copy the toolkit-private collections + skin-array config (see copyCustomStateTo).
2317
+ *
2318
+ * NOTE: VertexAnimationMaterial carries extra VAT state and provides its own cloneForInstance();
2319
+ * prefer that for VAT materials.
2320
+ */
2321
+ clone(name: string, cloneTexturesOnlyOnce?: boolean, rootUrl?: string): TOOLKIT.CustomShaderMaterial;
2322
+ /** Copies all toolkit-private state (custom shader collections + skin-array config) onto a clone. */
2323
+ protected copyCustomStateTo(result: TOOLKIT.CustomShaderMaterial): void;
2169
2324
  /** Adds a texture uniform property */
2170
2325
  addTextureUniform(name: string, texture: BABYLON.Texture): TOOLKIT.CustomShaderMaterial;
2171
2326
  /** Sets the texture uniform value */
@@ -2280,6 +2435,55 @@ declare namespace TOOLKIT {
2280
2435
  prepareDefines(defines: BABYLON.MaterialDefines, scene: BABYLON.Scene, mesh: BABYLON.AbstractMesh): void;
2281
2436
  bindForSubMesh(uniformBuffer: BABYLON.UniformBuffer, scene: BABYLON.Scene, engine: BABYLON.AbstractEngine, subMesh: BABYLON.SubMesh): void;
2282
2437
  }
2438
+ /**
2439
+ * Per-Skin Texture2DArray Switching Plugin (BABYLON.MaterialPluginBase)
2440
+ *
2441
+ * Opt-in base-material feature that selects a per-skin SLICE from a Texture2DArray albedo channel by a
2442
+ * single layer index — one shared mesh + an N-layer array presents a different "skin" per mesh or per
2443
+ * instance (e.g. ten jockeys, each a layer). Unlike the texture atlas there is NO UV remap: the surface
2444
+ * UVs are used as-is and the layer index picks the slice, so every skin gets a full-resolution slice with
2445
+ * its OWN clean mip chain (no cross-cell bleed).
2446
+ *
2447
+ * surfaceAlbedo = texture(tkAlbedoArray, vec3(uv, layer)) // albedo only (this phase)
2448
+ *
2449
+ * The layer comes from a per-instance vertex attribute (tkSkinLayer) OR a shared material uniform
2450
+ * (tkSkinLayerUni, the only mode that works on a NON-instanced mesh — one skin per mesh). The layer
2451
+ * value is constant across every vertex of a draw (uniform, or a per-instance attribute), so the
2452
+ * interpolated varying is constant and floor(v + 0.5) recovers the exact integer with no flat qualifier.
2453
+ *
2454
+ * All injected lines are gated behind TOOLKIT_SKIN_ARRAY, so a material that never calls
2455
+ * enableSkinArray() compiles to identical code.
2456
+ * @class SkinArraySwitchingPlugin - All rights reserved (c) 2024 Mackey Kinard
2457
+ */
2458
+ class SkinArraySwitchingPlugin extends TOOLKIT.CustomShaderMaterialPlugin {
2459
+ constructor(material: TOOLKIT.CustomShaderMaterial);
2460
+ isCompatible(shaderLanguage: BABYLON.ShaderLanguage): boolean;
2461
+ getClassName(): string;
2462
+ /** Advertise the per-instance layer attribute to the vertex shader (skipped in shared-uniform mode).
2463
+ * Gated on ANY channel being active so an emissive-only / normal-only mesh still gets the layer. */
2464
+ getAttributes(attributes: string[], scene: BABYLON.Scene, mesh: BABYLON.AbstractMesh): void;
2465
+ /** Register the array sampler(s) with the effect so setTexture() can bind them. The actual sampler
2466
+ * DECLARATIONS are emitted by this plugin's CUSTOM_FRAGMENT_DEFINITIONS (co-gated with their usage). */
2467
+ getSamplers(samplers: string[]): void;
2468
+ prepareDefines(defines: BABYLON.MaterialDefines, scene: BABYLON.Scene, mesh: BABYLON.AbstractMesh): void;
2469
+ getCustomCode(shaderType: string, shaderLanguage: BABYLON.ShaderLanguage): any;
2470
+ private getGLSLVertexDefinitions;
2471
+ private getGLSLVertexMainEnd;
2472
+ private getGLSLFragmentDefinitions;
2473
+ private getGLSLAlbedoCode;
2474
+ private getGLSLNormalCode;
2475
+ private getGLSLMetalRoughSampleCode;
2476
+ private getGLSLMetalRoughApplyCode;
2477
+ private getGLSLEmissiveCode;
2478
+ private getWGSLVertexDefinitions;
2479
+ private getWGSLVertexMainEnd;
2480
+ private getWGSLFragmentDefinitions;
2481
+ private getWGSLAlbedoCode;
2482
+ private getWGSLNormalCode;
2483
+ private getWGSLMetalRoughSampleCode;
2484
+ private getWGSLMetalRoughApplyCode;
2485
+ private getWGSLEmissiveCode;
2486
+ }
2283
2487
  /**
2284
2488
  * Babylon custom uniform items (GLTF)
2285
2489
  */
@@ -3245,13 +3449,19 @@ declare namespace TOOLKIT {
3245
3449
  */
3246
3450
  class VertexAnimationMaterial extends TOOLKIT.CustomShaderMaterial {
3247
3451
  controller: VertexAnimationController;
3248
- /** Optional per-material runtime skin texture arrays. When present, the fragment
3249
- * shader samples runtimeAlbedoSkins[skinLayer] / runtimeNormalSkins[skinLayer]
3250
- * instead of (or on top of) the standard albedo/bump samplers. Layer 0 is the
3251
- * default skin — convention is that callers seed it with the original albedo/normal.
3252
- * Bound by the plugin in bindForSubMesh(). */
3253
- runtimeAlbedoSkins: BABYLON.BaseTexture;
3254
- runtimeNormalSkins: BABYLON.BaseTexture;
3452
+ /** VAT per-skin Texture2DArray switching state — opt-in via enableVatSkinArray(). The shared albedo
3453
+ * array (tkAlbedoArray) is sampled at the layer decoded from the cell index packed in g_vatAnim1.w. */
3454
+ private _vatSkinArray;
3455
+ /** VAT per-skin NORMAL Texture2DArray switching — opt-in via enableVatSkinArrayNormal() once a
3456
+ * tkNormalArray is assigned. Overrides normalW with Babylon's native perturbNormal()/vTBN (only the
3457
+ * texel swapped for our slice), at the layer decoded from g_vatAnim1.w. Built unmipped + linear. */
3458
+ private _vatSkinArrayNormal;
3459
+ /** VAT per-skin METALLIC-ROUGHNESS switching — opt-in via enableVatSkinArrayMetalRough() once a
3460
+ * tkMetalRoughArray is assigned. metallic=slice.B, roughness=slice.G; mipped + linear. */
3461
+ private _vatSkinArrayMetalRough;
3462
+ /** VAT per-skin EMISSIVE switching — opt-in via enableVatSkinArrayEmissive() once a tkEmissiveArray is
3463
+ * assigned. Overrides finalEmissive with toLinearSpace(slice); works with no base emissive. */
3464
+ private _vatSkinArrayEmissive;
3255
3465
  constructor(name: string, scene: BABYLON.Scene);
3256
3466
  awake(): void;
3257
3467
  update(): void;
@@ -3267,44 +3477,55 @@ declare namespace TOOLKIT {
3267
3477
  */
3268
3478
  cloneForInstance(instanceName: string): TOOLKIT.VertexAnimationMaterial;
3269
3479
  /**
3270
- * Install runtime skin texture arrays. Layer 0 is treated as the default skin and is
3271
- * what every instance sees when its `runtimeSkinIndex` is 0 (or unset). Pass either
3272
- * `BABYLON.RawTexture2DArray` or any BaseTexture exposing a 2D-array view.
3480
+ * Enable per-skin Texture2DArray "skin" switching on this VAT material WITHOUT adding any vertex
3481
+ * buffer. VAT instances are already at the vertex-buffer ceiling (instanced world matrix +
3482
+ * g_vatAnim0 + g_vatAnim1), so the skin LAYER index is PACKED into g_vatAnim1.w alongside the
3483
+ * loop-blend flag, decoded in-shader and used to sample the shared albedo Texture2DArray:
3273
3484
  *
3274
- * When `nullOutDefaults` is true (default), the standalone `albedoTexture` and
3275
- * `bumpTexture` are nulled so PBR drops their sampler bindings — the plugin writes
3276
- * its own perturbed normal in the fragment when bumpTexture is absent. Pass false
3277
- * to keep the originals around (then runtime arrays act as overrides on top).
3278
- */
3279
- setRuntimeSkinArrays(albedoArray: BABYLON.BaseTexture, normalArray: BABYLON.BaseTexture, nullOutDefaults?: boolean): void;
3280
- /** Remove runtime skin arrays. Does NOT restore previously-nulled albedo/bump textures. */
3281
- clearRuntimeSkinArrays(): void;
3282
- /**
3283
- * Convenience: load a list of {albedo, normal} URL pairs, assemble two RawTexture2DArrays,
3284
- * and install them via setRuntimeSkinArrays(). Layer 0 is whatever you put at index 0 of
3285
- * the input list (the convention is "default skin first").
3485
+ * layer = floor(g_vatAnim1.w * 0.5) // 0,1,2,... selects the array slice
3486
+ * surfaceAlbedo = texture(tkAlbedoArray, vec3(uv, layer))
3286
3487
  *
3287
- * All input textures must share the same `width × height` — texture arrays are not ragged.
3288
- * Missing/malformed images reject the promise.
3488
+ * Surface UVs are used as-is (NO atlas remap) — each layer is a full-resolution slice with its own
3489
+ * clean mip chain. Assign the array first with addTextureArrayUniform("tkAlbedoArray", array), then
3490
+ * call this. Set the per-instance layer with VertexAnimationController.SetAtlasCellIndex(mesh, index).
3289
3491
  *
3290
- * @param layers ordered list of skin layers; index in array == GPU layer index == skinIndex value
3291
- * @param width pixel width every input texture must be (e.g. 1024)
3292
- * @param height pixel height every input texture must be (e.g. 1024)
3293
- * @param options.nullOutDefaults null mat.albedoTexture/bumpTexture after install (default true)
3294
- * @param options.rootUrl base URL for resolving relative paths (default "")
3295
- * @param options.generateMipMaps generate mips on the GPU arrays (default true)
3296
- * @param options.samplingMode BABYLON.Texture.* sampling mode (default TRILINEAR_SAMPLINGMODE)
3297
- */
3298
- installSkinsFromUrlsAsync(layers: {
3299
- albedo: string;
3300
- normal: string;
3301
- }[], width: number, height: number, options?: {
3302
- nullOutDefaults?: boolean;
3303
- rootUrl?: string;
3304
- generateMipMaps?: boolean;
3305
- samplingMode?: number;
3306
- }): Promise<void>;
3307
- private _notifyPluginOfRuntimeSkinChange;
3492
+ * Albedo only for now (normal/MR/AO/emissive fold in later).
3493
+ */
3494
+ enableVatSkinArray(): TOOLKIT.VertexAnimationMaterial;
3495
+ /** Disable VAT skin-array switching (g_vatAnim1.w reverts to the loop-blend flag only). */
3496
+ disableVatSkinArray(): void;
3497
+ /**
3498
+ * Enable per-skin NORMAL Texture2DArray switching on this VAT material. Assign the array first with
3499
+ * addTextureArrayUniform("tkNormalArray", array), then call this. The fragment overrides normalW with
3500
+ * Babylon's native tangent-space bump EXACTLY (its own perturbNormal()/vTBN, our slice texel swapped
3501
+ * in), at the layer decoded from g_vatAnim1.w. Only active where the native tangent frame exists
3502
+ * (BUMP && TANGENT && NORMAL — i.e. the VAT material carries a bumpTexture and the geometry has
3503
+ * tangents); elsewhere it compiles out and the native VAT normal stands. Normal array is unmipped.
3504
+ */
3505
+ enableVatSkinArrayNormal(): TOOLKIT.VertexAnimationMaterial;
3506
+ /** Disable VAT per-skin NORMAL array switching (normalW reverts to the native VAT normal). */
3507
+ disableVatSkinArrayNormal(): void;
3508
+ /** Enable VAT per-skin METALLIC-ROUGHNESS switching (metallic=slice.B, roughness=slice.G), decoded at
3509
+ * the layer packed in g_vatAnim1.w. Assign tkMetalRoughArray first. Only active under METALLICWORKFLOW. */
3510
+ enableVatSkinArrayMetalRough(): TOOLKIT.VertexAnimationMaterial;
3511
+ /** Disable VAT per-skin METALLIC-ROUGHNESS switching. */
3512
+ disableVatSkinArrayMetalRough(): void;
3513
+ /** Enable VAT per-skin EMISSIVE switching (finalEmissive = toLinearSpace(slice)) at the packed layer.
3514
+ * Assign tkEmissiveArray first. Works with no base emissive on the material. */
3515
+ enableVatSkinArrayEmissive(): TOOLKIT.VertexAnimationMaterial;
3516
+ /** Disable VAT per-skin EMISSIVE switching. */
3517
+ disableVatSkinArrayEmissive(): void;
3518
+ /** True when VAT per-skin Texture2DArray switching is enabled (see enableVatSkinArray). */
3519
+ get vatSkinArray(): boolean;
3520
+ /** True when VAT per-skin NORMAL Texture2DArray switching is enabled (see enableVatSkinArrayNormal). */
3521
+ get vatSkinArrayNormal(): boolean;
3522
+ /** True when VAT per-skin METALLIC-ROUGHNESS switching is enabled. */
3523
+ get vatSkinArrayMetalRough(): boolean;
3524
+ /** True when VAT per-skin EMISSIVE switching is enabled. */
3525
+ get vatSkinArrayEmissive(): boolean;
3526
+ /** True when ANY VAT per-skin channel is enabled — master gate for the shared vVatSkin varyings + the
3527
+ * packed cell-index decode, so any single channel works without the others. */
3528
+ get vatSkinArrayActive(): boolean;
3308
3529
  }
3309
3530
  /**
3310
3531
  * Static renderer reference emitted on Animator metadata for each VAT target.
@@ -3409,13 +3630,14 @@ declare namespace TOOLKIT {
3409
3630
  static GetOrCreate(guid: string, scene: BABYLON.Scene, rootUrl?: string, lazyLoadTextures?: boolean): TOOLKIT.VertexAnimationController;
3410
3631
  /** Collect unique renderer targets from the flat VAT settings array emitted by C#. */
3411
3632
  static CollectRendererTargets(settings: TOOLKIT.IVertexAnimationSettings[]): TOOLKIT.IVertexAnimationRendererReference[];
3412
- /** Set a per-mesh runtime skin index. Stored on the mesh as `runtimeSkinIndex`.
3413
- * Each Babylon InstancedMesh under one VAT controller can carry an independent value;
3414
- * the value is packed into g_vatAnim1.w during _tick() and decoded by the shader.
3415
- * Unset / out-of-range values are treated as 0 (the default skin = layer 0 of the runtime arrays). */
3416
- static SetSkinIndex(mesh: BABYLON.AbstractMesh, skinIndex: number): void;
3417
- /** Read a per-mesh runtime skin index. Returns 0 when the mesh has no value set. */
3418
- static GetSkinIndex(mesh: BABYLON.AbstractMesh): number;
3633
+ /** Set a per-mesh skin layer index for VAT per-skin Texture2DArray switching. Stored on the mesh as
3634
+ * `atlasCellIndex` and packed into g_vatAnim1.w during _tick (alongside the loop-blend flag), so
3635
+ * each InstancedMesh under one VAT controller can display a different array layer with no extra
3636
+ * vertex buffer. Requires enableVatSkinArray() on the material. Unset / out-of-range values are
3637
+ * treated as 0 (the first array layer). */
3638
+ static SetAtlasCellIndex(mesh: BABYLON.AbstractMesh, cellIndex: number): void;
3639
+ /** Read a per-mesh atlas cell index. Returns 0 when the mesh has no value set. */
3640
+ static GetAtlasCellIndex(mesh: BABYLON.AbstractMesh): number;
3419
3641
  /** Dispose every controller (optionally only those tied to a given scene). */
3420
3642
  static DisposeAll(scene?: BABYLON.Scene): void;
3421
3643
  readonly guid: string;
@@ -3467,28 +3689,6 @@ declare namespace TOOLKIT {
3467
3689
  getClip(name: string): IVertexAnimationClip | null;
3468
3690
  setSpeed(speed: number): void;
3469
3691
  setLoop(loop: boolean): void;
3470
- /** Install runtime skin texture arrays on every registered VAT material plugin.
3471
- * Convenience fan-out — equivalent to calling material.setRuntimeSkinArrays() on each material. */
3472
- setRuntimeSkinArrays(albedoArray: BABYLON.BaseTexture, normalArray: BABYLON.BaseTexture, nullOutDefaults?: boolean): void;
3473
- /** Remove runtime skin arrays from every registered VAT material plugin. */
3474
- clearRuntimeSkinArrays(): void;
3475
- /**
3476
- * Convenience: load skin texture pairs from URLs into RawTexture2DArrays and install them
3477
- * on every registered VAT material. Builds the arrays exactly once and shares the GPU
3478
- * resources across all materials — no per-material duplication. Uses this controller's
3479
- * `rootUrl` for relative path resolution unless an explicit one is passed in options.
3480
- *
3481
- * See VertexAnimationMaterial.installSkinsFromUrlsAsync for parameter semantics.
3482
- */
3483
- installSkinsFromUrlsAsync(layers: {
3484
- albedo: string;
3485
- normal: string;
3486
- }[], width: number, height: number, options?: {
3487
- nullOutDefaults?: boolean;
3488
- rootUrl?: string;
3489
- generateMipMaps?: boolean;
3490
- samplingMode?: number;
3491
- }): Promise<void>;
3492
3692
  setCurrentTime(time: number): void;
3493
3693
  getCurrentTime(): number;
3494
3694
  addPlugin(plugin: VertexAnimationMaterialPlugin): void;
@@ -3568,11 +3768,6 @@ declare namespace TOOLKIT {
3568
3768
  private _targetRendererGuid;
3569
3769
  /** 1×1 fallback texture — keeps WebGPU bind groups valid before a clip is assigned */
3570
3770
  private _placeholderTexture;
3571
- /** 1×1×1 fallback texture-array — keeps WebGPU bind groups valid before runtime skin
3572
- * arrays are installed. Two variants: a grey (0.5, 0.5, 0.5, 1) for albedo and a
3573
- * flat-up (0.5, 0.5, 1.0, 1) for tangent-space normal. */
3574
- private _placeholderAlbedoArray;
3575
- private _placeholderNormalArray;
3576
3771
  private _cc_posTex;
3577
3772
  private _cc_normTex;
3578
3773
  private _cc_prevPosTex;
@@ -3598,12 +3793,7 @@ declare namespace TOOLKIT {
3598
3793
  getSamplers(samplers: string[]): void;
3599
3794
  getAttributes(attributes: string[], scene: BABYLON.Scene, mesh: BABYLON.AbstractMesh): void;
3600
3795
  prepareDefines(defines: BABYLON.MaterialDefines, scene: BABYLON.Scene, mesh: BABYLON.AbstractMesh): void;
3601
- /** Called by VertexAnimationMaterial when runtime skin arrays change so we can mark
3602
- * defines dirty and force a shader rebuild. */
3603
- onRuntimeSkinArraysChanged(): void;
3604
3796
  bindForSubMesh(uniformBuffer: BABYLON.UniformBuffer, scene: BABYLON.Scene, engine: BABYLON.AbstractEngine, subMesh: BABYLON.SubMesh): void;
3605
- /** Bind runtime skin texture arrays (or their 1-layer placeholder fallbacks). */
3606
- private _pushRuntimeSkinSamplers;
3607
3797
  /**
3608
3798
  * Wire this material to a VertexAnimationController for the given settings.
3609
3799
  * Called from the AnimationState machine at scene-load time.
@@ -3656,14 +3846,20 @@ declare namespace TOOLKIT {
3656
3846
  private getWGSLVertexDefinitions;
3657
3847
  private getWGSLVertexPositionCode;
3658
3848
  private getWGSLVertexNormalCode;
3659
- private getGLSLVertexMainEnd;
3660
- private getWGSLVertexMainEnd;
3661
- private getGLSLFragmentDefinitions;
3662
- private getGLSLFragmentAlbedoCode;
3663
- private getGLSLFragmentNormalCode;
3664
- private getWGSLFragmentDefinitions;
3665
- private getWGSLFragmentAlbedoCode;
3666
- private getWGSLFragmentNormalCode;
3849
+ private getGLSLSkinLayerVaryingCode;
3850
+ private getGLSLSkinFragmentDefs;
3851
+ private getGLSLSkinAlbedoCode;
3852
+ private getGLSLSkinNormalCode;
3853
+ private getGLSLSkinMetalRoughSampleCode;
3854
+ private getGLSLSkinMetalRoughApplyCode;
3855
+ private getGLSLSkinEmissiveCode;
3856
+ private getWGSLSkinLayerVaryingCode;
3857
+ private getWGSLSkinFragmentDefs;
3858
+ private getWGSLSkinAlbedoCode;
3859
+ private getWGSLSkinNormalCode;
3860
+ private getWGSLSkinMetalRoughSampleCode;
3861
+ private getWGSLSkinMetalRoughApplyCode;
3862
+ private getWGSLSkinEmissiveCode;
3667
3863
  }
3668
3864
  }
3669
3865
  declare namespace PROJECT {
@@ -9808,6 +10004,55 @@ declare namespace TOOLKIT {
9808
10004
  customData?: any;
9809
10005
  }
9810
10006
  }
10007
+ declare namespace TOOLKIT {
10008
+ /**
10009
+ * Builds and caches BABYLON.RawTexture2DArray channels for the per-skin Texture2DArray switching
10010
+ * system (see CustomShaderMaterial.enableSkinArray / SkinArraySwitchingPlugin).
10011
+ *
10012
+ * Each PBR channel (albedo, normal, metallic-roughness, occlusion, emissive) becomes one array of N
10013
+ * same-size layers — one layer per skin. The layers are loaded from N separate image URLs and stacked
10014
+ * into a single GPU array texture with mipmaps + trilinear + anisotropic filtering, so every skin gets
10015
+ * a full-resolution slice with its own clean mip chain (no atlas cross-cell bleed). The array is shared
10016
+ * by every mesh/instance that uses the same skin set — the pixel data exists once, and each draw merely
10017
+ * selects a layer index.
10018
+ *
10019
+ * Color space: bytes are stored raw (RGBA8). sRGB channels (albedo, emissive) are linearized in the
10020
+ * shader (toLinearSpace), so this builder is channel-agnostic — pass the URLs and it stacks them.
10021
+ *
10022
+ * @class SkinTextureArray
10023
+ */
10024
+ class SkinTextureArray {
10025
+ /** Cache keyed by the joined URL list. The PROMISE is cached (not just the result) so concurrent
10026
+ * Build() calls for the same skin set (e.g. one per mesh) all await ONE build and share ONE GPU
10027
+ * array — no duplicate VRAM, no duplicate CPU mip work. Failed builds evict so a retry can happen. */
10028
+ private static _cache;
10029
+ /**
10030
+ * Build (or fetch from cache) a Texture2DArray from N image URLs. All layers are resampled to the
10031
+ * FIRST image's dimensions (arrays require uniform layer size). Returns a Promise that resolves to
10032
+ * the array texture (or null when no URLs are supplied / loading fails). Shared across all callers
10033
+ * with the same URL list.
10034
+ */
10035
+ static Build(scene: BABYLON.Scene, urls: string[], options?: {
10036
+ samplingMode?: number;
10037
+ aniso?: number;
10038
+ invertY?: boolean;
10039
+ mipmaps?: boolean;
10040
+ }): Promise<BABYLON.RawTexture2DArray>;
10041
+ /** Upload a CPU-generated mip chain to every layer (works around the broken WebGPU array auto-mip-gen).
10042
+ * Each level is a box-downsample of the previous, uploaded via engine.updateRawTexture2DArray(.., mipLevel). */
10043
+ private static _uploadMipChain;
10044
+ /** Box-average (2x2) downsample every layer of a stacked RGBA8 buffer to (dw x dh). Edge-clamped. */
10045
+ private static _downsampleLayers;
10046
+ /** GPU cap on Texture2DArray layers: WebGPU device limit `maxTextureArrayLayers`, else WebGL2
10047
+ * `MAX_ARRAY_TEXTURE_LAYERS`. Falls back to the spec-guaranteed floor (256) when it can't be read. */
10048
+ private static _maxArrayLayers;
10049
+ /** Load every URL as an HTMLImageElement (cross-origin enabled for canvas readback). */
10050
+ private static _loadImages;
10051
+ /** Dispose and drop every cached array (e.g. on scene teardown). Cached entries are promises, so we
10052
+ * resolve each before disposing (handles arrays still finishing their async build). */
10053
+ static Clear(): void;
10054
+ }
10055
+ }
9811
10056
  /** Babylon Toolkit Namespace */
9812
10057
  declare namespace TOOLKIT {
9813
10058
  /**
@@ -10243,6 +10488,149 @@ declare namespace TOOLKIT {
10243
10488
  private static LinearToGamma;
10244
10489
  }
10245
10490
  }
10491
+ declare namespace TOOLKIT {
10492
+ /**
10493
+ * Babylon Script Component — Texture Atlas Skin (Texture2DArray "skin slices")
10494
+ *
10495
+ * Mesh-first skin controller: ensures THIS component's owner mesh renders at a chosen skin index.
10496
+ * The mesh's type decides the delivery mechanism; every skinned submesh of the mesh uses the SAME
10497
+ * index (one index per mesh, changeable at runtime via setSkinIndex). Backed by a per-skin
10498
+ * Texture2DArray ("skin slices"): the surface UVs are sampled as-is and the layer index picks the
10499
+ * slice (full-res per-skin slices, each with its own clean mip chain — no atlas cross-cell bleed and
10500
+ * no UV remap).
10501
+ *
10502
+ * Authored properties (auto-filled from the Unity editor):
10503
+ * Every channel below is OPTIONAL and INDEPENDENT — supply only the ones you want to swap. The shared
10504
+ * layer index works for whichever channel(s) are present, so e.g. emissive-only (brake lights on/off)
10505
+ * needs no albedo. Absent channels fall through to the material's native map.
10506
+ * - albedoSlices — per-skin albedo (sRGB, mipped) → tkAlbedoArray, overrides surfaceAlbedo.
10507
+ * - normalSlices — per-skin tangent-space normals (linear, UNMIPPED) → tkNormalArray, fed through
10508
+ * Babylon's native perturbNormal()/vTBN so it matches the engine's bump EXACTLY. Only applied where
10509
+ * the mesh has a tangent frame (BUMP+TANGENT). Unmipped because box-downsampled normals grain.
10510
+ * - metallicSlices — per-skin metallic-roughness (glTF B=metallic, G=roughness; linear, mipped) →
10511
+ * tkMetalRoughArray, overrides metallicRoughness under METALLICWORKFLOW.
10512
+ * - emissiveSlices — per-skin emissive (sRGB, mipped) → tkEmissiveArray, overrides finalEmissive (works
10513
+ * with no base emissive — e.g. brake lights).
10514
+ * - occlusionSlices — reserved for a later phase (parsed but NOT yet stacked; AO has no exact PBR hook).
10515
+ * - textureData — optional skin manifest (JSON): { count, skins:[ { index, name } ], albedoLayers:[] }.
10516
+ * Synthesized from the slice count when absent.
10517
+ * - defaultIndex — the skin index this mesh starts at (applied to all skinned parts).
10518
+ * - skinnedParts — the submesh materials (Unity Material[]) to skin-switch on this mesh.
10519
+ *
10520
+ * Mesh-type routing:
10521
+ * - Regular BABYLON.Mesh → CLONE each part material (private to this mesh, preserves the
10522
+ * CustomShaderMaterial subclass + skin-array API) and drive a shared
10523
+ * layer uniform. Clones the MultiMaterial too so the swap stays local.
10524
+ * - BABYLON.InstancedMesh → shared material configured once; per-instance tkSkinLayer buffer.
10525
+ * - VAT (VertexAnimationMaterial) → shared material configured once (enableVatSkinArray); the layer is
10526
+ * decoded in-shader from the cell index packed into g_vatAnim1.w, so each
10527
+ * VAT instance under one controller can show a different slice.
10528
+ *
10529
+ * @class TextureAtlasSkin
10530
+ */
10531
+ class TextureAtlasSkin extends TOOLKIT.ScriptComponent {
10532
+ private abtractMesh;
10533
+ private textureData;
10534
+ private defaultIndex;
10535
+ private skinnedParts;
10536
+ private albedoSlices;
10537
+ private normalSlices;
10538
+ private metallicSlices;
10539
+ private occlusionSlices;
10540
+ private emissiveSlices;
10541
+ /** Guard so wiring runs once. */
10542
+ private _applied;
10543
+ /** Parsed (or synthesized) manifest. */
10544
+ private _manifest;
10545
+ /** The mesh's current skin index (all skinned parts share it). */
10546
+ private _currentIndex;
10547
+ /** Configured skin targets, with the delivery mechanism resolved per material. */
10548
+ private _targets;
10549
+ /** Per-mesh cloned MultiMaterial (regular-mesh path) so submaterial swaps stay local to this mesh. */
10550
+ private _clonedMultiMaterial;
10551
+ constructor(transform: BABYLON.TransformNode, scene: BABYLON.Scene, properties?: any, alias?: string);
10552
+ protected awake(): void;
10553
+ protected start(): void;
10554
+ /** Configure every skinned part for the skin array and apply defaultIndex. Idempotent (runs once). */
10555
+ applySkins(): void;
10556
+ /**
10557
+ * Switch every skinned part of THIS mesh to skin `index` (scoped to this mesh only).
10558
+ * Out-of-range indices are CLAMPED to [0, skinCount-1] with a warning. Returns the index applied.
10559
+ */
10560
+ setSkinIndex(index: number): number;
10561
+ /** Clamp an index to [0, skinCount-1], warning (with the label) when it was out of range. */
10562
+ private clampIndex;
10563
+ /** Number of skins: skins[] length, else manifest count, else the largest channel's slice count. */
10564
+ skinCount(): number;
10565
+ /** Largest per-skin slice count across every supplied channel (albedo/normal/MR/occlusion/emissive) —
10566
+ * so the skin count is correct even when albedo is absent (e.g. an emissive-only material). */
10567
+ private maxChannelSliceCount;
10568
+ /** The mesh's current skin index. */
10569
+ getSkinIndex(): number;
10570
+ /** Build the albedo Texture2DArray (and, when normalSlices are present, an unmipped/linear normal
10571
+ * Texture2DArray) from the slice images, assign them to the material, then enable per-skin array
10572
+ * switching for the mesh type. Async (images load after start()): the layer index is applied again
10573
+ * once the arrays are ready.
10574
+ * - regular → shared layer uniform (setSkinLayer)
10575
+ * - instanced → per-instance tkSkinLayer attribute buffer
10576
+ * - vat → layer decoded in-shader from the cell index packed in g_vatAnim1.w (enableVatSkinArray) */
10577
+ private configureMaterial;
10578
+ /** Resolve the albedo channel slice URLs. PREFERS the exported slice TEXTURE references (albedoSlices)
10579
+ * — auto-parsed and resolved against the scene root — falling back to manifest.albedoLayers filenames. */
10580
+ private albedoChannelUrls;
10581
+ /** Resolve the normal channel slice URLs (one per skin, same order as albedo). PREFERS the exported
10582
+ * slice TEXTURE references (normalSlices), falling back to manifest.normalLayers filenames. Returns
10583
+ * null when no normal slices were authored (normal switching then stays off and the native normal
10584
+ * stands). */
10585
+ private normalChannelUrls;
10586
+ /** Resolve the metallic-roughness channel slice URLs (glTF layout: B=metallic, G=roughness; one per
10587
+ * skin, same order as albedo). PREFERS exported slice references (metallicSlices), else manifest
10588
+ * metalRoughLayers. Null when no MR slices were authored (MR switching stays off). */
10589
+ private metalRoughChannelUrls;
10590
+ /** Resolve the emissive channel slice URLs (one per skin, same order as albedo). PREFERS exported slice
10591
+ * references (emissiveSlices), else manifest emissiveLayers. Null when no emissive slices were authored
10592
+ * (emissive switching stays off). Drives emissive-only swaps such as brake lights on/off. */
10593
+ private emissiveChannelUrls;
10594
+ /** Resolve each exported slice to a URL. The slices are IUnityTexture references (a filename relative
10595
+ * to the scene root — NOT auto-loaded into a BABYLON.Texture, so no duplicate GPU upload):
10596
+ * url = GetRootUrl(scene) + filename. Also tolerates an already-loaded BABYLON.Texture (.url). */
10597
+ private sliceUrls;
10598
+ /** Resolve a manifest layer filename to a loadable URL (absolute passes through; else scene root). */
10599
+ private resolveLayerUrl;
10600
+ /** Apply the current index to every target via its mesh-type-specific path (this mesh only). */
10601
+ private applyIndexToTargets;
10602
+ /** Select the per-skin array layer for one target:
10603
+ * - vat → the per-mesh cell index packed into g_vatAnim1.w by the VAT controller
10604
+ * - instanced → a per-instance tkSkinLayer attribute buffer
10605
+ * - regular → a shared material uniform on the (cloned) regular-mesh material
10606
+ * Safe to call before the array finishes loading — the value persists and takes effect once the
10607
+ * shader is gated on. */
10608
+ private applyLayerToMaterial;
10609
+ /** True when this mesh must use the per-instance buffer: it is an InstancedMesh, OR a source Mesh
10610
+ * that has hardware instances (instances share sourceMesh.material, so a private clone + uniform
10611
+ * layer is impossible — every instance would read the same shared value). */
10612
+ private isInstancedSetup;
10613
+ /** Register the per-instance tkSkinLayer buffer (one float) on the source mesh if not already present. */
10614
+ private ensureSkinLayerBuffer;
10615
+ /** Collect the host mesh's material slots: each MultiMaterial sub-material, or the single material. */
10616
+ private getMeshSlots;
10617
+ /** Match a resolved part material (by identity, then by name) to one of the mesh's slots. */
10618
+ private matchSlot;
10619
+ /** Resolve a skinnedParts entry to a live material (already-a-material, wrapped, by name). */
10620
+ private resolvePartMaterial;
10621
+ /** Extract a material name from a skinnedParts entry (string, live material, or serialized ref). */
10622
+ private partName;
10623
+ /** Clone a regular mesh's part material (and its MultiMaterial) so this mesh's skin is private. */
10624
+ private cloneForRegular;
10625
+ /** textureData may arrive as a JSON string or an object. Returns null when there is no skins[] list. */
10626
+ private parseManifest;
10627
+ private toInt;
10628
+ private isConfigured;
10629
+ private markConfigured;
10630
+ /** Human-readable label for a delivery mechanism (used in setup logging). */
10631
+ private describeKind;
10632
+ }
10633
+ }
10246
10634
  declare namespace TOOLKIT {
10247
10635
  /**
10248
10636
  * Babylon Toolkit User Interface Controls