diff --git a/lab/lite/babylon-ref-scene231.html b/lab/lite/babylon-ref-scene231.html
new file mode 100644
index 0000000000..caa4d8d9a9
--- /dev/null
+++ b/lab/lite/babylon-ref-scene231.html
@@ -0,0 +1,15 @@
+
+
+
+
+ BJS Reference — Scene 231: StandardMaterial Deform Features
+
+
+
+
+
+
+
diff --git a/lab/lite/bundle-scene231.html b/lab/lite/bundle-scene231.html
new file mode 100644
index 0000000000..9d5748d212
--- /dev/null
+++ b/lab/lite/bundle-scene231.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Babylon Lite — Scene 231: StandardMaterial Deform Features (Bundle)
+
+
+
+
+
+
+
+
diff --git a/lab/lite/scene231.html b/lab/lite/scene231.html
new file mode 100644
index 0000000000..ec3d810363
--- /dev/null
+++ b/lab/lite/scene231.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Babylon Lite — Scene 231: StandardMaterial Deform Features
+
+
+
+
+
+
+
+
diff --git a/lab/lite/src/bjs/scene231.ts b/lab/lite/src/bjs/scene231.ts
new file mode 100644
index 0000000000..3bcca27619
--- /dev/null
+++ b/lab/lite/src/bjs/scene231.ts
@@ -0,0 +1,122 @@
+// BJS reference for Scene 231 — StandardMaterial deform features (per-vertex
+// color + skeletal skinning + UV offset) on an in-code beam with programmatically
+// animated bones. Mirrors lab/lite/src/lite/scene231.ts exactly: the geometry,
+// vertex colors, skin weights, checker texture, and per-frame bone matrices all
+// come from the shared engine-agnostic module so the two engines stay in lockstep.
+
+import { ArcRotateCamera } from "@babylonjs/core/Cameras/arcRotateCamera";
+import { WebGPUEngine } from "@babylonjs/core/Engines/webgpuEngine";
+import { HemisphericLight } from "@babylonjs/core/Lights/hemisphericLight";
+import "@babylonjs/core/Materials/standardMaterial";
+import { StandardMaterial } from "@babylonjs/core/Materials/standardMaterial";
+import { RawTexture } from "@babylonjs/core/Materials/Textures/rawTexture";
+import { Texture } from "@babylonjs/core/Materials/Textures/texture";
+import { Color3, Color4 } from "@babylonjs/core/Maths/math.color";
+import { Matrix, Vector3 } from "@babylonjs/core/Maths/math.vector";
+import { Mesh } from "@babylonjs/core/Meshes/mesh";
+import { VertexData } from "@babylonjs/core/Meshes/mesh.vertexData";
+import { Bone } from "@babylonjs/core/Bones/bone";
+import { Skeleton } from "@babylonjs/core/Bones/skeleton";
+import { Scene } from "@babylonjs/core/scene";
+import { boneMatrixData, buildBeamData, buildCheckerPixels, CHECKER_SIZE, UV_OFFSET } from "../shared/scene231-skin.js";
+
+(async function () {
+ const __initStart = performance.now();
+ const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
+ const engine = new WebGPUEngine(canvas, { antialias: true, adaptToDeviceRatio: true });
+ await engine.initAsync();
+ engine.displayLoadingUI = function () {};
+
+ const scene = new Scene(engine);
+ scene.clearColor = new Color4(0.145, 0.165, 0.21, 1);
+
+ const camera = new ArcRotateCamera("camera", -Math.PI / 2, 1.1, 5.5, new Vector3(0, 0, 0), scene);
+ camera.fov = 0.72;
+ camera.minZ = 0.1;
+ camera.maxZ = 100;
+ camera.attachControl(canvas, true);
+ scene.activeCamera = camera;
+
+ const light = new HemisphericLight("hemi", new Vector3(0, 1, 0), scene);
+ light.intensity = 0.9;
+
+ const beam = buildBeamData();
+
+ // Build the mesh from the shared geometry, including per-vertex color and skin attributes.
+ const mesh = new Mesh("scene231-beam", scene);
+ const vd = new VertexData();
+ vd.positions = beam.positions;
+ vd.normals = beam.normals;
+ vd.uvs = beam.uvs;
+ vd.colors = beam.colors;
+ vd.indices = beam.indices;
+ vd.matricesIndices = Array.from(beam.joints);
+ vd.matricesWeights = Array.from(beam.weights);
+ vd.applyToMesh(mesh);
+ mesh.numBoneInfluencers = 4;
+ mesh.hasVertexAlpha = false;
+ mesh.useVertexColors = true;
+
+ // Two independent bones (root + upper), bound at identity so the per-frame
+ // local matrices become the skin palette directly (matching Lite's bone texture).
+ const skeleton = new Skeleton("scene231-skeleton", "scene231-skeleton", scene);
+ const root = new Bone("root", skeleton, null, Matrix.Identity());
+ const upper = new Bone("upper", skeleton, null, Matrix.Identity());
+ mesh.skeleton = skeleton;
+
+ const checker = RawTexture.CreateRGBATexture(buildCheckerPixels(), CHECKER_SIZE, CHECKER_SIZE, scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+ checker.wrapU = Texture.WRAP_ADDRESSMODE;
+ checker.wrapV = Texture.WRAP_ADDRESSMODE;
+ checker.uOffset = UV_OFFSET[0];
+ checker.vOffset = UV_OFFSET[1];
+
+ const material = new StandardMaterial("scene231-mat", scene);
+ material.diffuseColor = new Color3(1, 1, 1);
+ material.diffuseTexture = checker;
+ mesh.material = material;
+
+ // Per-frame programmatic bone animation: recompute the bone matrices as a pure
+ // function of the frame index and push them as the bones' local transforms.
+ const boneData = boneMatrixData(0);
+ const m0 = Matrix.Identity();
+ const m1 = Matrix.Identity();
+ const params = new URLSearchParams(window.location.search);
+ const seek = parseFloat(params.get("seekTime") || "");
+ const freezeFrame = isNaN(seek) ? -1 : Math.round(seek * 60);
+ let frame = 0;
+ let frozen = false;
+
+ const applyBones = (): void => {
+ Matrix.FromArrayToRef(boneData, 0, m0);
+ Matrix.FromArrayToRef(boneData, 16, m1);
+ root.updateMatrix(m0, false, true);
+ upper.updateMatrix(m1, false, true);
+ };
+ applyBones();
+
+ scene.onBeforeRenderObservable.add(() => {
+ if (frozen) {
+ return;
+ }
+ frame++;
+ boneMatrixData(frame, boneData);
+ applyBones();
+ if (freezeFrame >= 0 && frame >= freezeFrame) {
+ frozen = true;
+ canvas.dataset.animationFrozen = "true";
+ }
+ });
+
+ engine.getDeltaTime = function () {
+ return 16;
+ };
+ scene.useConstantAnimationDeltaTime = true;
+
+ await scene.whenReadyAsync();
+ engine.runRenderLoop(() => scene.render());
+ window.addEventListener("resize", () => engine.resize());
+
+ await new Promise((resolve) => scene.onAfterRenderObservable.addOnce(() => resolve()));
+ canvas.dataset.initMs = String(performance.now() - __initStart);
+ canvas.dataset.ready = "true";
+})().catch(console.error);
diff --git a/lab/lite/src/lite/scene231.ts b/lab/lite/src/lite/scene231.ts
new file mode 100644
index 0000000000..b99683d670
--- /dev/null
+++ b/lab/lite/src/lite/scene231.ts
@@ -0,0 +1,102 @@
+// Scene 231 — StandardMaterial deform features (in-code, no glTF):
+// per-vertex color + skeletal skinning + UV offset on a procedurally built beam,
+// with bones animated programmatically (per-frame updates, no animation system).
+//
+// The beam is a tall box (4 vertically-subdivided side faces + caps) skinned to a
+// 2-bone skeleton: a fixed root bone and an upper bone that swings about the beam
+// origin. The pose is a pure function of the frame index, so freezing at a known
+// frame (via ?seekTime) is deterministic and matches the Babylon.js reference.
+
+import {
+ addToScene,
+ attachControl,
+ createArcRotateCamera,
+ createEngine,
+ createHemisphericLight,
+ createSceneContext,
+ createStandardMaterial,
+ createTexture2DFromPixels,
+ onBeforeRender,
+ registerScene,
+ startEngine,
+} from "babylon-lite";
+import type { ArcRotateCamera } from "babylon-lite";
+import { enableStandardSkeleton, enableStandardUvOffset, enableStandardVertexColor } from "babylon-lite/material/standard/enable-standard-mesh-features.js";
+import { createMeshFromData } from "babylon-lite/mesh/mesh-factories.js";
+import { createSkeleton } from "babylon-lite/skeleton/create-skeleton.js";
+import { boneMatrixData, buildBeamData, buildCheckerPixels, CHECKER_SIZE, SKELETON_BONE_COUNT, UV_OFFSET } from "../shared/scene231-skin.js";
+
+async function main(): Promise {
+ const __initStart = performance.now();
+ const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
+ const engine = await createEngine(canvas);
+ const scene = createSceneContext(engine);
+ scene.clearColor = { r: 0.145, g: 0.165, b: 0.21, a: 1 };
+
+ // Opt in to the Standard deform/vertex features used by this scene. Plain
+ // Standard scenes that never call these stay byte-identical to upstream.
+ enableStandardVertexColor();
+ enableStandardSkeleton();
+ enableStandardUvOffset();
+
+ const camera = createArcRotateCamera(-Math.PI / 2, 1.1, 5.5, { x: 0, y: 0, z: 0 });
+ camera.fov = 0.72;
+ camera.nearPlane = 0.1;
+ camera.farPlane = 100;
+ scene.camera = camera;
+ attachControl(camera as ArcRotateCamera, canvas, scene);
+
+ addToScene(scene, createHemisphericLight([0, 1, 0], 0.9));
+
+ const beam = buildBeamData();
+ const mesh = createMeshFromData(engine, "scene231-beam", beam.positions, beam.normals, beam.indices, beam.uvs, undefined, undefined, beam.colors);
+
+ const material = createStandardMaterial();
+ material.diffuseColor = [1, 1, 1];
+ material.diffuseTexture = createTexture2DFromPixels(engine, buildCheckerPixels(), CHECKER_SIZE, CHECKER_SIZE, { addressModeU: "repeat", addressModeV: "repeat" });
+ material.uvOffset = UV_OFFSET;
+ mesh.material = material;
+
+ const boneData = boneMatrixData(0);
+ mesh.skeleton = createSkeleton(engine, beam.joints, beam.weights, SKELETON_BONE_COUNT, boneData);
+ addToScene(scene, mesh);
+
+ // Per-frame programmatic bone animation: recompute the bone matrices as a pure
+ // function of the frame index and re-upload them to the skeleton's bone texture.
+ const boneTexWidth = SKELETON_BONE_COUNT * 4;
+ const boneTexture = mesh.skeleton.boneTexture;
+ scene.fixedDeltaMs = 16.0;
+
+ const params = new URLSearchParams(window.location.search);
+ const seek = parseFloat(params.get("seekTime") || "");
+ const freezeFrame = isNaN(seek) ? -1 : Math.round(seek * 60);
+ let frame = 0;
+ let frozen = false;
+
+ onBeforeRender(scene, () => {
+ if (frozen) {
+ return;
+ }
+ frame++;
+ boneMatrixData(frame, boneData);
+ engine._device.queue.writeTexture({ texture: boneTexture }, boneData.buffer as ArrayBuffer, { bytesPerRow: boneTexWidth * 16 }, { width: boneTexWidth, height: 1 });
+ if (freezeFrame >= 0 && frame >= freezeFrame) {
+ frozen = true;
+ canvas.dataset.animationFrozen = "true";
+ }
+ });
+
+ await registerScene(scene);
+ await startEngine(engine);
+ canvas.dataset.drawCalls = String(engine.drawCallCount);
+ canvas.dataset.initMs = String(performance.now() - __initStart);
+ canvas.dataset.ready = "true";
+}
+
+main().catch((error: unknown) => {
+ const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement | null;
+ if (canvas) {
+ canvas.dataset.error = error instanceof Error ? error.message : String(error);
+ }
+ console.error(error);
+});
diff --git a/lab/lite/src/shared/scene231-skin.ts b/lab/lite/src/shared/scene231-skin.ts
new file mode 100644
index 0000000000..ec790febf0
--- /dev/null
+++ b/lab/lite/src/shared/scene231-skin.ts
@@ -0,0 +1,236 @@
+// Shared, engine-agnostic data for Scene 231 (StandardMaterial deform features:
+// per-vertex color + skeletal skinning + UV offset, with programmatically
+// animated bones).
+//
+// Both the Lite scene (scene231.ts) and the Babylon.js reference (bjs/scene231.ts)
+// import this module so the geometry, per-vertex colors, skin weights, checker
+// texture, and per-frame bone matrices are byte-for-byte identical. Nothing here
+// depends on either engine — only plain typed arrays — so neither bundle pulls in
+// the other engine, and parity is guaranteed by construction.
+
+/** Procedural beam mesh: 4 vertically-subdivided side faces + 2 end caps. */
+export interface BeamData {
+ positions: Float32Array; // xyz per vertex
+ normals: Float32Array; // xyz per vertex
+ uvs: Float32Array; // uv per vertex
+ indices: Uint32Array;
+ colors: Float32Array; // rgba per vertex
+ joints: Uint16Array; // 4 bone indices per vertex
+ weights: Float32Array; // 4 bone weights per vertex
+ vertexCount: number;
+}
+
+const HALF_W = 0.25; // half width (x)
+const HALF_D = 0.25; // half depth (z)
+const HALF_H = 1.0; // half height (y) -> beam spans y in [-1, 1]
+const SEGMENTS = 16; // height subdivisions per side face (smooth bend)
+const BONE_COUNT = 2;
+
+/** Normalized height 0..1 (bottom..top) from a y in [-HALF_H, HALF_H]. */
+function heightT(y: number): number {
+ return (y + HALF_H) / (2 * HALF_H);
+}
+
+/** Smooth bone-1 (upper) influence as a function of normalized height.
+ * Bottom ~20% rides the root bone; top ~20% rides the upper bone; smooth between. */
+function upperWeight(t: number): number {
+ const k = (t - 0.2) / 0.6;
+ const c = k < 0 ? 0 : k > 1 ? 1 : k;
+ return c * c * (3 - 2 * c); // smoothstep
+}
+
+/** A simple bottom->top rainbow so the bend reads clearly. */
+function heightColor(t: number, out: Float32Array, o: number): void {
+ // HSV-ish sweep red -> green -> blue across the height.
+ const h = t; // 0..1
+ const r = Math.max(0, 1 - Math.abs(h - 0.0) * 2);
+ const g = Math.max(0, 1 - Math.abs(h - 0.5) * 2);
+ const b = Math.max(0, 1 - Math.abs(h - 1.0) * 2);
+ out[o] = 0.25 + 0.75 * r;
+ out[o + 1] = 0.25 + 0.75 * g;
+ out[o + 2] = 0.25 + 0.75 * b;
+ out[o + 3] = 1;
+}
+
+/** Build the beam geometry + per-vertex color + skin weights. */
+export function buildBeamData(): BeamData {
+ const positions: number[] = [];
+ const normals: number[] = [];
+ const uvs: number[] = [];
+ const colors: number[] = [];
+ const joints: number[] = [];
+ const weights: number[] = [];
+ const indices: number[] = [];
+
+ const pushVertex = (x: number, y: number, z: number, nx: number, ny: number, nz: number, u: number, v: number): void => {
+ positions.push(x, y, z);
+ normals.push(nx, ny, nz);
+ uvs.push(u, v);
+ const t = heightT(y);
+ const c = new Float32Array(4);
+ heightColor(t, c, 0);
+ colors.push(c[0]!, c[1]!, c[2]!, c[3]!);
+ const w1 = upperWeight(t);
+ const w0 = 1 - w1;
+ joints.push(0, 1, 0, 0);
+ weights.push(w0, w1, 0, 0);
+ };
+
+ // Four side faces, each a vertical strip subdivided SEGMENTS times.
+ // Each face: outward normal, two columns (the face's two vertical edges).
+ interface Face {
+ // corner a (left edge) and b (right edge) in the XZ plane, plus outward normal
+ ax: number;
+ az: number;
+ bx: number;
+ bz: number;
+ nx: number;
+ nz: number;
+ }
+ const faces: Face[] = [
+ { ax: -HALF_W, az: HALF_D, bx: HALF_W, bz: HALF_D, nx: 0, nz: 1 }, // +Z
+ { ax: HALF_W, az: HALF_D, bx: HALF_W, bz: -HALF_D, nx: 1, nz: 0 }, // +X
+ { ax: HALF_W, az: -HALF_D, bx: -HALF_W, bz: -HALF_D, nx: 0, nz: -1 }, // -Z
+ { ax: -HALF_W, az: -HALF_D, bx: -HALF_W, bz: HALF_D, nx: -1, nz: 0 }, // -X
+ ];
+
+ for (const f of faces) {
+ const base = positions.length / 3;
+ for (let i = 0; i <= SEGMENTS; i++) {
+ const v = i / SEGMENTS;
+ const y = -HALF_H + v * (2 * HALF_H);
+ pushVertex(f.ax, y, f.az, f.nx, 0, f.nz, 0, v);
+ pushVertex(f.bx, y, f.bz, f.nx, 0, f.nz, 1, v);
+ }
+ for (let i = 0; i < SEGMENTS; i++) {
+ const a = base + i * 2;
+ const b = a + 1;
+ const c = a + 2;
+ const d = a + 3;
+ indices.push(a, c, b, b, c, d);
+ }
+ }
+
+ // End caps (single quads). Winding chosen so the outward normal faces ±Y.
+ const addCap = (y: number, ny: number): void => {
+ const base = positions.length / 3;
+ const corners: [number, number][] = [
+ [-HALF_W, -HALF_D],
+ [HALF_W, -HALF_D],
+ [HALF_W, HALF_D],
+ [-HALF_W, HALF_D],
+ ];
+ for (const [x, z] of corners) {
+ pushVertex(x, y, z, 0, ny, 0, x / (2 * HALF_W) + 0.5, z / (2 * HALF_D) + 0.5);
+ }
+ if (ny > 0) {
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
+ } else {
+ indices.push(base, base + 2, base + 1, base, base + 3, base + 2);
+ }
+ };
+ addCap(HALF_H, 1);
+ addCap(-HALF_H, -1);
+
+ return {
+ positions: new Float32Array(positions),
+ normals: new Float32Array(normals),
+ uvs: new Float32Array(uvs),
+ indices: new Uint32Array(indices),
+ colors: new Float32Array(colors),
+ joints: new Uint16Array(joints),
+ weights: new Float32Array(weights),
+ vertexCount: positions.length / 3,
+ };
+}
+
+export const SKELETON_BONE_COUNT = BONE_COUNT;
+
+/** Animation period (frames) and swing amplitude (radians) for the upper bone. */
+const PERIOD_FRAMES = 120;
+const SWING_AMPLITUDE = 0.6;
+
+/** Bend angle (radians) of the upper bone at a given frame — a pure function of
+ * the frame index, so both engines reproduce the identical pose when frozen. */
+export function bendAngle(frame: number): number {
+ return SWING_AMPLITUDE * Math.sin((frame / PERIOD_FRAMES) * Math.PI * 2);
+}
+
+/** Per-frame bone matrices as a packed Float32Array (BONE_COUNT * 16), in
+ * Babylon row-major layout (translation at indices 12,13,14 of each block).
+ * Bone 0 = identity (root). Bone 1 = rotation about Z by bendAngle, pivoting at
+ * the beam origin (y = 0), so the upper part swings side to side. */
+export function boneMatrixData(frame: number, out?: Float32Array): Float32Array {
+ const m = out ?? new Float32Array(BONE_COUNT * 16);
+ // Bone 0: identity
+ m[0] = 1;
+ m[1] = 0;
+ m[2] = 0;
+ m[3] = 0;
+ m[4] = 0;
+ m[5] = 1;
+ m[6] = 0;
+ m[7] = 0;
+ m[8] = 0;
+ m[9] = 0;
+ m[10] = 1;
+ m[11] = 0;
+ m[12] = 0;
+ m[13] = 0;
+ m[14] = 0;
+ m[15] = 1;
+ // Bone 1: rotation about Z (Babylon RotationZ row-major layout)
+ const a = bendAngle(frame);
+ const c = Math.cos(a);
+ const s = Math.sin(a);
+ m[16] = c;
+ m[17] = s;
+ m[18] = 0;
+ m[19] = 0;
+ m[20] = -s;
+ m[21] = c;
+ m[22] = 0;
+ m[23] = 0;
+ m[24] = 0;
+ m[25] = 0;
+ m[26] = 1;
+ m[27] = 0;
+ m[28] = 0;
+ m[29] = 0;
+ m[30] = 0;
+ m[31] = 1;
+ return m;
+}
+
+// ---- Checker texture (for the UV-offset feature) ---------------------------
+
+export const CHECKER_SIZE = 256;
+const CHECKER_CELLS = 8;
+
+/** A grey/orange checker, RGBA8, CHECKER_SIZE square. Identical pixels feed both
+ * engines so the UV-offset shift compares exactly. */
+export function buildCheckerPixels(): Uint8Array {
+ const n = CHECKER_SIZE;
+ const data = new Uint8Array(n * n * 4);
+ const cell = n / CHECKER_CELLS;
+ for (let y = 0; y < n; y++) {
+ for (let x = 0; x < n; x++) {
+ const odd = ((Math.floor(x / cell) + Math.floor(y / cell)) & 1) === 1;
+ const o = (y * n + x) * 4;
+ if (odd) {
+ data[o] = 235;
+ data[o + 1] = 145;
+ data[o + 2] = 40;
+ } else {
+ data[o] = 60;
+ data[o + 1] = 64;
+ data[o + 2] = 74;
+ }
+ data[o + 3] = 255;
+ }
+ }
+ return data;
+}
+
+/** Static UV offset applied to the diffuse texture (demonstrates the feature). */
+export const UV_OFFSET: [number, number] = [0.13, 0.07];
diff --git a/lab/public/bundle/manifest/scene100.json b/lab/public/bundle/manifest/scene100.json
index 6dad0eeaab..5c04cf6066 100644
--- a/lab/public/bundle/manifest/scene100.json
+++ b/lab/public/bundle/manifest/scene100.json
@@ -1,10 +1,9 @@
{
- "rawKB": 50.4,
- "gzipKB": 20.2,
+ "rawKB": 49.3,
+ "gzipKB": 19.8,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene100-standard-renderable-Buv_s4dX.js",
- "scene100-wgsl-helpers-CwHFFXvt.js",
+ "scene100-standard-renderable-BWKAd75D.js",
"scene100.js"
]
}
diff --git a/lab/public/bundle/manifest/scene101.json b/lab/public/bundle/manifest/scene101.json
index 00df96df0f..ae2e877946 100644
--- a/lab/public/bundle/manifest/scene101.json
+++ b/lab/public/bundle/manifest/scene101.json
@@ -1,10 +1,9 @@
{
- "rawKB": 53.8,
- "gzipKB": 21.5,
+ "rawKB": 52.7,
+ "gzipKB": 21.1,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene101-standard-renderable-BAYncetB.js",
- "scene101-wgsl-helpers-CwHFFXvt.js",
+ "scene101-standard-renderable-CVbAyvcw.js",
"scene101.js"
]
}
diff --git a/lab/public/bundle/manifest/scene102.json b/lab/public/bundle/manifest/scene102.json
index 4f62fada30..6c06048543 100644
--- a/lab/public/bundle/manifest/scene102.json
+++ b/lab/public/bundle/manifest/scene102.json
@@ -1,10 +1,9 @@
{
- "rawKB": 59.5,
- "gzipKB": 23.7,
+ "rawKB": 58.4,
+ "gzipKB": 23.3,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene102-standard-renderable-VJFYqVqL.js",
- "scene102-wgsl-helpers-CwHFFXvt.js",
+ "scene102-standard-renderable-CPoVoWmd.js",
"scene102.js"
]
}
diff --git a/lab/public/bundle/manifest/scene103.json b/lab/public/bundle/manifest/scene103.json
index 56e235280d..14a60b3450 100644
--- a/lab/public/bundle/manifest/scene103.json
+++ b/lab/public/bundle/manifest/scene103.json
@@ -1,12 +1,11 @@
{
- "rawKB": 62.1,
- "gzipKB": 24.7,
+ "rawKB": 61,
+ "gzipKB": 24.3,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene103-standard-renderable-DKLp8FJA.js",
+ "scene103-standard-renderable-vLQ1_LqS.js",
"scene103-thin-instance-fragment-CBn2miAC.js",
"scene103-thin-instance-gpu-D2OBjYZH.js",
- "scene103-wgsl-helpers-CwHFFXvt.js",
"scene103.js"
]
}
diff --git a/lab/public/bundle/manifest/scene104.json b/lab/public/bundle/manifest/scene104.json
index dad93f57d3..0c508b7ab7 100644
--- a/lab/public/bundle/manifest/scene104.json
+++ b/lab/public/bundle/manifest/scene104.json
@@ -1,15 +1,14 @@
{
- "rawKB": 101.7,
- "gzipKB": 39.3,
+ "rawKB": 100.6,
+ "gzipKB": 38.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene104-generate-mipmaps-BGv6wRC-.js",
"scene104-gltf-feature-registry-CE6GMq6X.js",
"scene104-gltf-glb-parser-BJfNq64D.js",
"scene104-shader-composer-CgJmQyJj.js",
- "scene104-standard-renderable-BYZDGzK-.js",
+ "scene104-standard-renderable-CZ5EuNPw.js",
"scene104-std-lightmap-fragment-BtZeffyx.js",
- "scene104-wgsl-helpers-CwHFFXvt.js",
"scene104.js"
]
}
diff --git a/lab/public/bundle/manifest/scene105.json b/lab/public/bundle/manifest/scene105.json
index d3f2ebb4aa..c1cf221088 100644
--- a/lab/public/bundle/manifest/scene105.json
+++ b/lab/public/bundle/manifest/scene105.json
@@ -1,15 +1,14 @@
{
- "rawKB": 102.3,
- "gzipKB": 39.6,
+ "rawKB": 101.3,
+ "gzipKB": 39.1,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene105-generate-mipmaps-Bw7xZIhD.js",
"scene105-gltf-feature-registry-DurnsCTb.js",
"scene105-gltf-glb-parser-BsWC1iTB.js",
"scene105-shader-composer-CgJmQyJj.js",
- "scene105-standard-renderable-wWJPvbnz.js",
+ "scene105-standard-renderable-CVA_VO9o.js",
"scene105-std-lightmap-fragment-DJHrjMOe.js",
- "scene105-wgsl-helpers-CwHFFXvt.js",
"scene105.js"
]
}
diff --git a/lab/public/bundle/manifest/scene106.json b/lab/public/bundle/manifest/scene106.json
index 6b1f944f83..9a0859ba47 100644
--- a/lab/public/bundle/manifest/scene106.json
+++ b/lab/public/bundle/manifest/scene106.json
@@ -1,10 +1,9 @@
{
- "rawKB": 51.4,
- "gzipKB": 20.6,
+ "rawKB": 50.4,
+ "gzipKB": 20.1,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene106-standard-renderable-BZNlFJ4y.js",
- "scene106-wgsl-helpers-CwHFFXvt.js",
+ "scene106-standard-renderable-oiUJ9jpI.js",
"scene106.js"
]
}
diff --git a/lab/public/bundle/manifest/scene110.json b/lab/public/bundle/manifest/scene110.json
index f0d9f82749..1ccff775d3 100644
--- a/lab/public/bundle/manifest/scene110.json
+++ b/lab/public/bundle/manifest/scene110.json
@@ -1,10 +1,9 @@
{
- "rawKB": 50.9,
- "gzipKB": 19.9,
+ "rawKB": 49.8,
+ "gzipKB": 19.5,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene110-standard-renderable-D6cO9rrY.js",
- "scene110-wgsl-helpers-CwHFFXvt.js",
+ "scene110-standard-renderable-CZeUI5EB.js",
"scene110.js"
]
}
diff --git a/lab/public/bundle/manifest/scene111.json b/lab/public/bundle/manifest/scene111.json
index 1e51e5a1aa..7e88f54164 100644
--- a/lab/public/bundle/manifest/scene111.json
+++ b/lab/public/bundle/manifest/scene111.json
@@ -1,6 +1,6 @@
{
- "rawKB": 134.6,
- "gzipKB": 54.3,
+ "rawKB": 133.5,
+ "gzipKB": 53.8,
"ignoredRawKB": 6.4,
"runtimeChunks": [
"scene111-_math-factory-DFVyS8gB.js",
@@ -23,11 +23,10 @@
"scene111-shader-composer-CVST0cVc.js",
"scene111-shadow-fragment-core-Dbe1xNkt.js",
"scene111-shadow-task-C9r4Q-jw.js",
- "scene111-standard-renderable-DWURFIF8.js",
+ "scene111-standard-renderable-BGoErCrE.js",
"scene111-std-shadow-fragment-u3x2t2qW.js",
"scene111-transform-block-xSHzJVmq.js",
"scene111-vertex-output-C7G5u6Y0.js",
- "scene111-wgsl-helpers-CwHFFXvt.js",
"scene111.js"
]
}
diff --git a/lab/public/bundle/manifest/scene113.json b/lab/public/bundle/manifest/scene113.json
index 24d62211d9..c63319876b 100644
--- a/lab/public/bundle/manifest/scene113.json
+++ b/lab/public/bundle/manifest/scene113.json
@@ -1,10 +1,9 @@
{
- "rawKB": 63,
- "gzipKB": 24.3,
+ "rawKB": 61.9,
+ "gzipKB": 23.8,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene113-standard-renderable-DHQkU9-B.js",
- "scene113-wgsl-helpers-CwHFFXvt.js",
+ "scene113-standard-renderable-gnIr5e1I.js",
"scene113.js"
]
}
diff --git a/lab/public/bundle/manifest/scene115.json b/lab/public/bundle/manifest/scene115.json
index 562c8e0e90..7c8e12bb10 100644
--- a/lab/public/bundle/manifest/scene115.json
+++ b/lab/public/bundle/manifest/scene115.json
@@ -1,6 +1,6 @@
{
- "rawKB": 126,
- "gzipKB": 51.8,
+ "rawKB": 125,
+ "gzipKB": 51.3,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene115-create-morph-targets-BxcR5t59.js",
@@ -20,8 +20,7 @@
"scene115-shader-composer-CgJmQyJj.js",
"scene115-singlelight-hemispheric-wgsl-D4qRhfo5.js",
"scene115-skeleton-fragment-Wgm6o2IA.js",
- "scene115-standard-renderable-BJyVFLpF.js",
- "scene115-wgsl-helpers-CwHFFXvt.js",
+ "scene115-standard-renderable-CJQ3hAVX.js",
"scene115.js"
]
}
diff --git a/lab/public/bundle/manifest/scene116.json b/lab/public/bundle/manifest/scene116.json
index 68243428f5..15d0fd9c08 100644
--- a/lab/public/bundle/manifest/scene116.json
+++ b/lab/public/bundle/manifest/scene116.json
@@ -1,15 +1,14 @@
{
- "rawKB": 76.5,
- "gzipKB": 31.3,
+ "rawKB": 75.4,
+ "gzipKB": 30.8,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene116-pbr-renderable-RHux3tgC.js",
"scene116-shader-composer-Bga-tI-0.js",
"scene116-singlelight-hemispheric-wgsl-ziPxL_2u.js",
- "scene116-standard-renderable-muHsmIf0.js",
+ "scene116-standard-renderable-By25_6zA.js",
"scene116-std-emissive-fragment-C813VlNk.js",
"scene116-unlit-fragment-Bgox5O_g.js",
- "scene116-wgsl-helpers-CwHFFXvt.js",
"scene116.js"
]
}
diff --git a/lab/public/bundle/manifest/scene129.json b/lab/public/bundle/manifest/scene129.json
index a0958aa133..d0cef60a22 100644
--- a/lab/public/bundle/manifest/scene129.json
+++ b/lab/public/bundle/manifest/scene129.json
@@ -1,11 +1,10 @@
{
- "rawKB": 82.8,
- "gzipKB": 31.5,
+ "rawKB": 81.8,
+ "gzipKB": 31.1,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene129-gs-picking-pipeline-wk9cVMav.js",
- "scene129-standard-renderable-DT6HMQHE.js",
- "scene129-wgsl-helpers-CwHFFXvt.js",
+ "scene129-standard-renderable-C5XyvMdI.js",
"scene129.js"
]
}
diff --git a/lab/public/bundle/manifest/scene141.json b/lab/public/bundle/manifest/scene141.json
index 68c53082c5..eae83679a3 100644
--- a/lab/public/bundle/manifest/scene141.json
+++ b/lab/public/bundle/manifest/scene141.json
@@ -1,6 +1,6 @@
{
- "rawKB": 123.5,
- "gzipKB": 50.9,
+ "rawKB": 122.4,
+ "gzipKB": 50.5,
"ignoredRawKB": 6.4,
"runtimeChunks": [
"scene141-_math-factory-DFVyS8gB.js",
@@ -21,10 +21,9 @@
"scene141-shadow-fragment-core-Dbe1xNkt.js",
"scene141-shadow-task-D7JQvILi.js",
"scene141-singlelight-directional-wgsl-CUGOIxSX.js",
- "scene141-standard-renderable-CgdKXWAd.js",
+ "scene141-standard-renderable-yaaDLyBS.js",
"scene141-transform-block-DWUOQ_Fz.js",
"scene141-vertex-output-C7G5u6Y0.js",
- "scene141-wgsl-helpers-CwHFFXvt.js",
"scene141.js"
]
}
diff --git a/lab/public/bundle/manifest/scene142.json b/lab/public/bundle/manifest/scene142.json
index 0411fb24c4..025485195f 100644
--- a/lab/public/bundle/manifest/scene142.json
+++ b/lab/public/bundle/manifest/scene142.json
@@ -1,10 +1,9 @@
{
- "rawKB": 61.2,
- "gzipKB": 22.9,
+ "rawKB": 60.1,
+ "gzipKB": 22.4,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene142-standard-renderable-COIztMWN.js",
- "scene142-wgsl-helpers-CwHFFXvt.js",
+ "scene142-standard-renderable-DOOqetWl.js",
"scene142.js"
]
}
diff --git a/lab/public/bundle/manifest/scene143.json b/lab/public/bundle/manifest/scene143.json
index 9afe0d63cf..777e485e09 100644
--- a/lab/public/bundle/manifest/scene143.json
+++ b/lab/public/bundle/manifest/scene143.json
@@ -1,12 +1,12 @@
{
"rawKB": 70.3,
- "gzipKB": 27.3,
+ "gzipKB": 27.4,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene143-generate-mipmaps-CiyhUZPA.js",
"scene143-normal-map-fragment-vZfuB--u.js",
"scene143-point-light-CHHO5fr1.js",
- "scene143-standard-renderable-RDRd4x1P.js",
+ "scene143-standard-renderable-uju7lWWB.js",
"scene143-std-ambient-fragment-CU5U7ir2.js",
"scene143-std-opacity-fragment-CbaZz9IY.js",
"scene143-std-reflection-fragment-BMQE51J5.js",
diff --git a/lab/public/bundle/manifest/scene145.json b/lab/public/bundle/manifest/scene145.json
index 9c6fa74db3..a613b4e910 100644
--- a/lab/public/bundle/manifest/scene145.json
+++ b/lab/public/bundle/manifest/scene145.json
@@ -1,20 +1,20 @@
{
- "rawKB": 89,
- "gzipKB": 36.3,
+ "rawKB": 87.8,
+ "gzipKB": 35.8,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene145-bake-local-matrix-67U-IT8C.js",
"scene145-cube-texture-vbJP4eNk.js",
"scene145-generate-mipmaps-Db4M86C8.js",
- "scene145-geometry-view-CN4PelFi.js",
+ "scene145-geometry-view-CKdfj_OV.js",
"scene145-mesh-features-Dnub8q45.js",
"scene145-mip-count-CdXF1U-X.js",
"scene145-parse-camera-BXBDIYdM.js",
"scene145-point-light-DuKWngp0.js",
"scene145-shader-composer-BLINGJZD.js",
- "scene145-standard-material-CVLV5Oh4.js",
- "scene145-standard-pipeline-CRbKzlw9.js",
- "scene145-standard-renderable-CTmuVKCk.js",
+ "scene145-standard-material-Ci3gzKk9.js",
+ "scene145-standard-pipeline-uzw5Rkrm.js",
+ "scene145-standard-renderable-CQegbxGk.js",
"scene145-std-ambient-fragment-Ctnv-tPf.js",
"scene145-std-cube-reflection-fragment-BnXcsg5b.js",
"scene145-std-opacity-fragment-F2uWm_km.js",
@@ -22,7 +22,6 @@
"scene145-thin-instance-fragment-CBn2miAC.js",
"scene145-thin-instance-gpu-Du81IUuw.js",
"scene145-ubo-layout-Cs5YvLYZ.js",
- "scene145-wgsl-helpers-CwHFFXvt.js",
"scene145.js"
]
}
diff --git a/lab/public/bundle/manifest/scene15.json b/lab/public/bundle/manifest/scene15.json
index d9eb54de64..f032142af2 100644
--- a/lab/public/bundle/manifest/scene15.json
+++ b/lab/public/bundle/manifest/scene15.json
@@ -1,10 +1,9 @@
{
- "rawKB": 47.4,
- "gzipKB": 19,
+ "rawKB": 46.3,
+ "gzipKB": 18.5,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene15-standard-renderable-DjpuOYV2.js",
- "scene15-wgsl-helpers-CwHFFXvt.js",
+ "scene15-standard-renderable-CgLabYHh.js",
"scene15.js"
]
}
diff --git a/lab/public/bundle/manifest/scene150.json b/lab/public/bundle/manifest/scene150.json
index f71e57cd42..24d0f42db6 100644
--- a/lab/public/bundle/manifest/scene150.json
+++ b/lab/public/bundle/manifest/scene150.json
@@ -1,10 +1,9 @@
{
- "rawKB": 55.2,
- "gzipKB": 21.6,
+ "rawKB": 54.1,
+ "gzipKB": 21.2,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene150-standard-renderable-Dwlmn5s3.js",
- "scene150-wgsl-helpers-CwHFFXvt.js",
+ "scene150-standard-renderable-BFX72Lxl.js",
"scene150.js"
]
}
diff --git a/lab/public/bundle/manifest/scene151.json b/lab/public/bundle/manifest/scene151.json
index ceef9075cf..ac2c735571 100644
--- a/lab/public/bundle/manifest/scene151.json
+++ b/lab/public/bundle/manifest/scene151.json
@@ -1,10 +1,9 @@
{
- "rawKB": 55.4,
- "gzipKB": 21.7,
+ "rawKB": 54.4,
+ "gzipKB": 21.2,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene151-standard-renderable-C822zhOt.js",
- "scene151-wgsl-helpers-CwHFFXvt.js",
+ "scene151-standard-renderable-BqZ9MeFl.js",
"scene151.js"
]
}
diff --git a/lab/public/bundle/manifest/scene154.json b/lab/public/bundle/manifest/scene154.json
index 4168e12411..e1e3c1bf42 100644
--- a/lab/public/bundle/manifest/scene154.json
+++ b/lab/public/bundle/manifest/scene154.json
@@ -1,10 +1,9 @@
{
- "rawKB": 55.6,
- "gzipKB": 21.7,
+ "rawKB": 54.6,
+ "gzipKB": 21.3,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene154-standard-renderable-B0H1CD6L.js",
- "scene154-wgsl-helpers-CwHFFXvt.js",
+ "scene154-standard-renderable-C4iMVyse.js",
"scene154.js"
]
}
diff --git a/lab/public/bundle/manifest/scene155.json b/lab/public/bundle/manifest/scene155.json
index cfcceca5cd..bb21bb47c9 100644
--- a/lab/public/bundle/manifest/scene155.json
+++ b/lab/public/bundle/manifest/scene155.json
@@ -1,10 +1,9 @@
{
- "rawKB": 58.1,
- "gzipKB": 22.6,
+ "rawKB": 57,
+ "gzipKB": 22.1,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene155-standard-renderable-CDfPDID1.js",
- "scene155-wgsl-helpers-CwHFFXvt.js",
+ "scene155-standard-renderable-DmH58mw3.js",
"scene155.js"
]
}
diff --git a/lab/public/bundle/manifest/scene156.json b/lab/public/bundle/manifest/scene156.json
index 9b393d4929..607b4b03ab 100644
--- a/lab/public/bundle/manifest/scene156.json
+++ b/lab/public/bundle/manifest/scene156.json
@@ -1,10 +1,9 @@
{
- "rawKB": 58.8,
- "gzipKB": 22.8,
+ "rawKB": 57.8,
+ "gzipKB": 22.4,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene156-standard-renderable-DJlsSdaM.js",
- "scene156-wgsl-helpers-CwHFFXvt.js",
+ "scene156-standard-renderable-Bl156GhT.js",
"scene156.js"
]
}
diff --git a/lab/public/bundle/manifest/scene16.json b/lab/public/bundle/manifest/scene16.json
index 5613f8b48b..03d1411d31 100644
--- a/lab/public/bundle/manifest/scene16.json
+++ b/lab/public/bundle/manifest/scene16.json
@@ -1,12 +1,11 @@
{
- "rawKB": 48.9,
- "gzipKB": 19.6,
+ "rawKB": 47.8,
+ "gzipKB": 19.2,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene16-standard-renderable-CXUbe7VR.js",
+ "scene16-standard-renderable-CkJ8UtwR.js",
"scene16-thin-instance-fragment-CBn2miAC.js",
"scene16-thin-instance-gpu-oR8PF1wB.js",
- "scene16-wgsl-helpers-CwHFFXvt.js",
"scene16.js"
]
}
diff --git a/lab/public/bundle/manifest/scene17.json b/lab/public/bundle/manifest/scene17.json
index 31b13dc82c..ae53c077be 100644
--- a/lab/public/bundle/manifest/scene17.json
+++ b/lab/public/bundle/manifest/scene17.json
@@ -1,6 +1,6 @@
{
- "rawKB": 85.9,
- "gzipKB": 35.7,
+ "rawKB": 84.8,
+ "gzipKB": 35.2,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene17-generate-mipmaps-DdI_QVIx.js",
@@ -9,10 +9,9 @@
"scene17-rgbd-decode-aydmE8bD.js",
"scene17-shader-composer-Bga-tI-0.js",
"scene17-singlelight-hemispheric-wgsl-DXh7-K43.js",
- "scene17-standard-renderable-DhjJVqDv.js",
+ "scene17-standard-renderable-ChlC1inf.js",
"scene17-thin-instance-fragment-CBn2miAC.js",
"scene17-thin-instance-gpu-BthweFeQ.js",
- "scene17-wgsl-helpers-CwHFFXvt.js",
"scene17.js"
]
}
diff --git a/lab/public/bundle/manifest/scene170.json b/lab/public/bundle/manifest/scene170.json
index 8da43fbd7e..8008d2e0c9 100644
--- a/lab/public/bundle/manifest/scene170.json
+++ b/lab/public/bundle/manifest/scene170.json
@@ -1,11 +1,10 @@
{
- "rawKB": 51.6,
- "gzipKB": 20.4,
+ "rawKB": 50.5,
+ "gzipKB": 20,
"ignoredRawKB": 1485.2,
"runtimeChunks": [
"scene170-recast-navigation-CYBQI-zY-Dry_z7x_.js",
- "scene170-standard-renderable-awCOjW3n.js",
- "scene170-wgsl-helpers-CwHFFXvt.js",
+ "scene170-standard-renderable-ZooVi18e.js",
"scene170.js"
]
}
diff --git a/lab/public/bundle/manifest/scene171.json b/lab/public/bundle/manifest/scene171.json
index 7f44597238..efb1afc8b8 100644
--- a/lab/public/bundle/manifest/scene171.json
+++ b/lab/public/bundle/manifest/scene171.json
@@ -1,16 +1,15 @@
{
- "rawKB": 96.4,
- "gzipKB": 39.2,
+ "rawKB": 95.3,
+ "gzipKB": 38.8,
"ignoredRawKB": 1485.2,
"runtimeChunks": [
- "scene171-generate-mipmaps-DXPozdXM.js",
+ "scene171-generate-mipmaps-DciVh-Qu.js",
"scene171-gltf-glb-parser-COBAbifQ.js",
- "scene171-pbr-renderable-U8xtN7DF.js",
+ "scene171-pbr-renderable-Q1yQ1-Iu.js",
"scene171-recast-navigation-CYBQI-zY-Dry_z7x_.js",
"scene171-shader-composer-CgJmQyJj.js",
"scene171-singlelight-hemispheric-wgsl-CeuKqi92.js",
- "scene171-standard-renderable-DBKlvAnr.js",
- "scene171-wgsl-helpers-CwHFFXvt.js",
+ "scene171-standard-renderable-C00icSsM.js",
"scene171.js"
]
}
diff --git a/lab/public/bundle/manifest/scene172.json b/lab/public/bundle/manifest/scene172.json
index b545481af6..98c7af850f 100644
--- a/lab/public/bundle/manifest/scene172.json
+++ b/lab/public/bundle/manifest/scene172.json
@@ -1,11 +1,10 @@
{
- "rawKB": 59.9,
- "gzipKB": 23.5,
+ "rawKB": 58.9,
+ "gzipKB": 23,
"ignoredRawKB": 1485.2,
"runtimeChunks": [
"scene172-recast-navigation-CYBQI-zY-Dry_z7x_.js",
- "scene172-standard-renderable-DKUEOmYA.js",
- "scene172-wgsl-helpers-CwHFFXvt.js",
+ "scene172-standard-renderable-DQ8t6v5m.js",
"scene172.js"
]
}
diff --git a/lab/public/bundle/manifest/scene173.json b/lab/public/bundle/manifest/scene173.json
index a9a0d48721..f0c6e4abe0 100644
--- a/lab/public/bundle/manifest/scene173.json
+++ b/lab/public/bundle/manifest/scene173.json
@@ -1,11 +1,10 @@
{
- "rawKB": 62.1,
- "gzipKB": 24.2,
+ "rawKB": 61,
+ "gzipKB": 23.7,
"ignoredRawKB": 1485.2,
"runtimeChunks": [
"scene173-recast-navigation-CYBQI-zY-Dry_z7x_.js",
- "scene173-standard-renderable-jbBENzb2.js",
- "scene173-wgsl-helpers-CwHFFXvt.js",
+ "scene173-standard-renderable-dFvXDtqw.js",
"scene173.js"
]
}
diff --git a/lab/public/bundle/manifest/scene174.json b/lab/public/bundle/manifest/scene174.json
index 1e85a4694c..682cc1b51d 100644
--- a/lab/public/bundle/manifest/scene174.json
+++ b/lab/public/bundle/manifest/scene174.json
@@ -1,16 +1,15 @@
{
- "rawKB": 97,
- "gzipKB": 39.6,
+ "rawKB": 96,
+ "gzipKB": 39.2,
"ignoredRawKB": 1485.2,
"runtimeChunks": [
- "scene174-generate-mipmaps-oyz7Zcb3.js",
+ "scene174-generate-mipmaps-DOk08rXN.js",
"scene174-gltf-glb-parser-V10AO6Mf.js",
- "scene174-pbr-renderable-FyaqLZqu.js",
+ "scene174-pbr-renderable-DWCycXIJ.js",
"scene174-recast-navigation-CYBQI-zY-Dry_z7x_.js",
"scene174-shader-composer-CgJmQyJj.js",
"scene174-singlelight-hemispheric-wgsl-STW_78f5.js",
- "scene174-standard-renderable-CwcJW6qL.js",
- "scene174-wgsl-helpers-CwHFFXvt.js",
+ "scene174-standard-renderable-B7BOkKqy.js",
"scene174.js"
]
}
diff --git a/lab/public/bundle/manifest/scene175.json b/lab/public/bundle/manifest/scene175.json
index d5dec7464a..a3471ce71d 100644
--- a/lab/public/bundle/manifest/scene175.json
+++ b/lab/public/bundle/manifest/scene175.json
@@ -1,16 +1,15 @@
{
- "rawKB": 95.3,
- "gzipKB": 38.9,
+ "rawKB": 94.3,
+ "gzipKB": 38.5,
"ignoredRawKB": 1485.2,
"runtimeChunks": [
- "scene175-generate-mipmaps-B-d91NI_.js",
+ "scene175-generate-mipmaps-C7PmApd7.js",
"scene175-gltf-glb-parser-BmlEZ507.js",
- "scene175-pbr-renderable-DjBc-dib.js",
+ "scene175-pbr-renderable-nE57aWWx.js",
"scene175-recast-navigation-CYBQI-zY-Dry_z7x_.js",
"scene175-shader-composer-CgJmQyJj.js",
"scene175-singlelight-hemispheric-wgsl-D-nS_1oq.js",
- "scene175-standard-renderable-DmsClrbv.js",
- "scene175-wgsl-helpers-CwHFFXvt.js",
+ "scene175-standard-renderable-CYerlzcF.js",
"scene175.js"
]
}
diff --git a/lab/public/bundle/manifest/scene18.json b/lab/public/bundle/manifest/scene18.json
index c688573be6..4ca4cf2b92 100644
--- a/lab/public/bundle/manifest/scene18.json
+++ b/lab/public/bundle/manifest/scene18.json
@@ -1,15 +1,14 @@
{
- "rawKB": 60.7,
- "gzipKB": 24.4,
+ "rawKB": 59.7,
+ "gzipKB": 23.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene18-generate-mipmaps-CORvzSVx.js",
"scene18-material-view-Dua1bl7W.js",
"scene18-no-color-view-DPM4VP7h.js",
"scene18-shadow-task-t_t6oLxe.js",
- "scene18-standard-renderable-M2ivaWTL.js",
+ "scene18-standard-renderable-HehChBxy.js",
"scene18-std-shadow-fragment-pwyXRo4i.js",
- "scene18-wgsl-helpers-CwHFFXvt.js",
"scene18.js"
]
}
diff --git a/lab/public/bundle/manifest/scene2.json b/lab/public/bundle/manifest/scene2.json
index 9f40d2a040..0f663314b5 100644
--- a/lab/public/bundle/manifest/scene2.json
+++ b/lab/public/bundle/manifest/scene2.json
@@ -1,10 +1,9 @@
{
- "rawKB": 47,
- "gzipKB": 18.8,
+ "rawKB": 46,
+ "gzipKB": 18.4,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene2-standard-renderable-BiFo_CfT.js",
- "scene2-wgsl-helpers-CwHFFXvt.js",
+ "scene2-standard-renderable-CLfaRvJ6.js",
"scene2.js"
]
}
diff --git a/lab/public/bundle/manifest/scene200.json b/lab/public/bundle/manifest/scene200.json
index 3f20c4ee26..de8d783acb 100644
--- a/lab/public/bundle/manifest/scene200.json
+++ b/lab/public/bundle/manifest/scene200.json
@@ -1,10 +1,9 @@
{
- "rawKB": 46.8,
- "gzipKB": 18.7,
+ "rawKB": 45.8,
+ "gzipKB": 18.2,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene200-standard-renderable-CmdaDWAN.js",
- "scene200-wgsl-helpers-CwHFFXvt.js",
+ "scene200-standard-renderable-CDg0mS2W.js",
"scene200.js"
]
}
diff --git a/lab/public/bundle/manifest/scene201.json b/lab/public/bundle/manifest/scene201.json
index 98c6f467ac..7d45ce52de 100644
--- a/lab/public/bundle/manifest/scene201.json
+++ b/lab/public/bundle/manifest/scene201.json
@@ -1,13 +1,12 @@
{
- "rawKB": 48.6,
- "gzipKB": 19.7,
+ "rawKB": 47.6,
+ "gzipKB": 19.3,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene201-_mat4-storage-f64-BubF55_X.js",
"scene201-floating-origin-18rYq_DG.js",
"scene201-pack-mat4-with-offset-BS-Lz8k7.js",
- "scene201-standard-renderable-BT6iqp8p.js",
- "scene201-wgsl-helpers-CwHFFXvt.js",
+ "scene201-standard-renderable-C4YKR7PM.js",
"scene201.js"
]
}
diff --git a/lab/public/bundle/manifest/scene202.json b/lab/public/bundle/manifest/scene202.json
index 35695f64a4..e7d7cda21e 100644
--- a/lab/public/bundle/manifest/scene202.json
+++ b/lab/public/bundle/manifest/scene202.json
@@ -1,13 +1,12 @@
{
- "rawKB": 49.4,
- "gzipKB": 20,
+ "rawKB": 48.3,
+ "gzipKB": 19.6,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene202-_mat4-storage-f64-Cxw5KwUZ.js",
"scene202-floating-origin-BG1gyRgj.js",
"scene202-pack-mat4-with-offset-BS-Lz8k7.js",
- "scene202-standard-renderable-C6pqEWwQ.js",
- "scene202-wgsl-helpers-CwHFFXvt.js",
+ "scene202-standard-renderable-C0ZdJKYn.js",
"scene202.js"
]
}
diff --git a/lab/public/bundle/manifest/scene203.json b/lab/public/bundle/manifest/scene203.json
index b396da646c..cda57e9916 100644
--- a/lab/public/bundle/manifest/scene203.json
+++ b/lab/public/bundle/manifest/scene203.json
@@ -1,13 +1,12 @@
{
- "rawKB": 49.7,
- "gzipKB": 20.2,
+ "rawKB": 48.6,
+ "gzipKB": 19.7,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene203-_mat4-storage-f64-C0lTlSaC.js",
"scene203-floating-origin-CBr6x8Ml.js",
"scene203-pack-mat4-with-offset-BS-Lz8k7.js",
- "scene203-standard-renderable-DiimDZpT.js",
- "scene203-wgsl-helpers-CwHFFXvt.js",
+ "scene203-standard-renderable-4vsd-D0F.js",
"scene203.js"
]
}
diff --git a/lab/public/bundle/manifest/scene204.json b/lab/public/bundle/manifest/scene204.json
index dbb5ec71cb..076125847b 100644
--- a/lab/public/bundle/manifest/scene204.json
+++ b/lab/public/bundle/manifest/scene204.json
@@ -1,15 +1,14 @@
{
- "rawKB": 51.1,
- "gzipKB": 20.9,
+ "rawKB": 50,
+ "gzipKB": 20.4,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene204-_mat4-storage-f64-7Hs0bt2y.js",
"scene204-floating-origin-GYPpgH2l.js",
"scene204-pack-mat4-with-offset-BS-Lz8k7.js",
- "scene204-standard-renderable-CEqz6rMQ.js",
+ "scene204-standard-renderable-Cfe2uFMQ.js",
"scene204-thin-instance-fragment-CBn2miAC.js",
"scene204-thin-instance-gpu-BPglyyv5.js",
- "scene204-wgsl-helpers-CwHFFXvt.js",
"scene204.js"
]
}
diff --git a/lab/public/bundle/manifest/scene205.json b/lab/public/bundle/manifest/scene205.json
index 8507031d65..8834f3e88a 100644
--- a/lab/public/bundle/manifest/scene205.json
+++ b/lab/public/bundle/manifest/scene205.json
@@ -1,6 +1,6 @@
{
- "rawKB": 62,
- "gzipKB": 25.5,
+ "rawKB": 60.9,
+ "gzipKB": 25,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene205-_mat4-storage-f64-BubVEbOW.js",
@@ -8,8 +8,7 @@
"scene205-floating-origin-BpHUAtCM.js",
"scene205-pack-mat4-with-offset-BS-Lz8k7.js",
"scene205-scene-uniforms-FTYH6Ct0.js",
- "scene205-standard-renderable-BN27SEi1.js",
- "scene205-wgsl-helpers-CwHFFXvt.js",
+ "scene205-standard-renderable-DkLqwmbO.js",
"scene205.js"
]
}
diff --git a/lab/public/bundle/manifest/scene206.json b/lab/public/bundle/manifest/scene206.json
index 4a10a76d05..caa750b402 100644
--- a/lab/public/bundle/manifest/scene206.json
+++ b/lab/public/bundle/manifest/scene206.json
@@ -1,6 +1,6 @@
{
- "rawKB": 62.3,
- "gzipKB": 25.5,
+ "rawKB": 61.3,
+ "gzipKB": 25.1,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene206-_mat4-storage-f64-FI8ZUjRd.js",
@@ -8,8 +8,7 @@
"scene206-floating-origin-B3Cmn-Fn.js",
"scene206-pack-mat4-with-offset-BS-Lz8k7.js",
"scene206-scene-uniforms-FTYH6Ct0.js",
- "scene206-standard-renderable-DLDxk5f_.js",
- "scene206-wgsl-helpers-CwHFFXvt.js",
+ "scene206-standard-renderable-CvjHnnBy.js",
"scene206.js"
]
}
diff --git a/lab/public/bundle/manifest/scene207.json b/lab/public/bundle/manifest/scene207.json
index e10639a1bd..53bf0eec62 100644
--- a/lab/public/bundle/manifest/scene207.json
+++ b/lab/public/bundle/manifest/scene207.json
@@ -1,6 +1,6 @@
{
- "rawKB": 59.7,
- "gzipKB": 24,
+ "rawKB": 58.6,
+ "gzipKB": 23.6,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene207-_mat4-storage-f64-B_0HYOAx.js",
@@ -9,9 +9,8 @@
"scene207-no-color-view-BJnjfxE3.js",
"scene207-pack-mat4-with-offset-BS-Lz8k7.js",
"scene207-shadow-task-SxovdLRJ.js",
- "scene207-standard-renderable-Q6qXJ1fg.js",
+ "scene207-standard-renderable-DEhH2f6U.js",
"scene207-std-shadow-fragment-pwyXRo4i.js",
- "scene207-wgsl-helpers-CwHFFXvt.js",
"scene207.js"
]
}
diff --git a/lab/public/bundle/manifest/scene209.json b/lab/public/bundle/manifest/scene209.json
index 60d08013c4..baaf8aadf3 100644
--- a/lab/public/bundle/manifest/scene209.json
+++ b/lab/public/bundle/manifest/scene209.json
@@ -1,14 +1,13 @@
{
- "rawKB": 54.4,
- "gzipKB": 22.2,
+ "rawKB": 53.3,
+ "gzipKB": 21.8,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene209-_mat4-storage-f64-3NavC9Rv.js",
"scene209-floating-origin-DMyB87f5.js",
"scene209-havok-floating-origin-CkalwAjY.js",
"scene209-pack-mat4-with-offset-BS-Lz8k7.js",
- "scene209-standard-renderable-BhttRL8q.js",
- "scene209-wgsl-helpers-CwHFFXvt.js",
+ "scene209-standard-renderable-CB-AoszP.js",
"scene209.js"
]
}
diff --git a/lab/public/bundle/manifest/scene214.json b/lab/public/bundle/manifest/scene214.json
index 655be7ab70..1af8f03b5e 100644
--- a/lab/public/bundle/manifest/scene214.json
+++ b/lab/public/bundle/manifest/scene214.json
@@ -1,14 +1,13 @@
{
- "rawKB": 68.3,
- "gzipKB": 27.1,
+ "rawKB": 67.2,
+ "gzipKB": 26.7,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene214-material-view-Dua1bl7W.js",
"scene214-no-color-view-5_yMJTcH.js",
"scene214-shadow-task-kZBQCrfU.js",
- "scene214-standard-renderable-2VrikE7W.js",
+ "scene214-standard-renderable-CpRmBMBT.js",
"scene214-std-shadow-fragment-i_uxbIpF.js",
- "scene214-wgsl-helpers-CwHFFXvt.js",
"scene214.js"
]
}
diff --git a/lab/public/bundle/manifest/scene217.json b/lab/public/bundle/manifest/scene217.json
index eaec1c3a71..8c326de9f6 100644
--- a/lab/public/bundle/manifest/scene217.json
+++ b/lab/public/bundle/manifest/scene217.json
@@ -1,13 +1,12 @@
{
- "rawKB": 78.2,
- "gzipKB": 31.5,
+ "rawKB": 77.1,
+ "gzipKB": 31.1,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene217-multilight-wgsl-D_ZZ-Nk7.js",
"scene217-pbr-renderable-CxhvLVWg.js",
"scene217-shader-composer-BFQxgkqp.js",
- "scene217-standard-renderable-DGQZW6Se.js",
- "scene217-wgsl-helpers-CwHFFXvt.js",
+ "scene217-standard-renderable-CtbT01a9.js",
"scene217.js"
]
}
diff --git a/lab/public/bundle/manifest/scene22.json b/lab/public/bundle/manifest/scene22.json
index a8a95f5f54..064a772a0f 100644
--- a/lab/public/bundle/manifest/scene22.json
+++ b/lab/public/bundle/manifest/scene22.json
@@ -1,6 +1,6 @@
{
- "rawKB": 97.7,
- "gzipKB": 39.4,
+ "rawKB": 96.6,
+ "gzipKB": 38.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene22-esm-shadow-view-BpdnPOo5.js",
@@ -13,8 +13,7 @@
"scene22-shader-composer-Bga-tI-0.js",
"scene22-shadow-fragment-core-Dbe1xNkt.js",
"scene22-shadow-task-Yvrdp5XB.js",
- "scene22-standard-renderable-DFYGplzb.js",
- "scene22-wgsl-helpers-CwHFFXvt.js",
+ "scene22-standard-renderable-CjgqKCKX.js",
"scene22.js"
]
}
diff --git a/lab/public/bundle/manifest/scene221.json b/lab/public/bundle/manifest/scene221.json
index 2acb63b9d4..94b1e754b0 100644
--- a/lab/public/bundle/manifest/scene221.json
+++ b/lab/public/bundle/manifest/scene221.json
@@ -1,13 +1,12 @@
{
- "rawKB": 100.4,
- "gzipKB": 37.9,
+ "rawKB": 99.3,
+ "gzipKB": 37.5,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene221-shader-renderable-tHF1mFau.js",
- "scene221-standard-renderable-qDSfNdr4.js",
+ "scene221-standard-renderable-DiV6aifY.js",
"scene221-swapchain-overlay-ilhN4El_.js",
"scene221-ubo-layout-CnrP4RZD.js",
- "scene221-wgsl-helpers-CwHFFXvt.js",
"scene221.js"
]
}
diff --git a/lab/public/bundle/manifest/scene222.json b/lab/public/bundle/manifest/scene222.json
index 1e2079b23b..dd82726e10 100644
--- a/lab/public/bundle/manifest/scene222.json
+++ b/lab/public/bundle/manifest/scene222.json
@@ -1,14 +1,13 @@
{
- "rawKB": 111.5,
- "gzipKB": 41,
+ "rawKB": 110.4,
+ "gzipKB": 40.6,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene222-gpu-pool-CHlSfwGp.js",
"scene222-shader-renderable-D9pd8HjW.js",
- "scene222-standard-renderable-BdLy8HER.js",
+ "scene222-standard-renderable-BGCfFabL.js",
"scene222-swapchain-overlay-ilhN4El_.js",
"scene222-ubo-layout-Cs5YvLYZ.js",
- "scene222-wgsl-helpers-CwHFFXvt.js",
"scene222.js"
]
}
diff --git a/lab/public/bundle/manifest/scene223.json b/lab/public/bundle/manifest/scene223.json
index e25ad0fbc8..595950e82f 100644
--- a/lab/public/bundle/manifest/scene223.json
+++ b/lab/public/bundle/manifest/scene223.json
@@ -1,11 +1,10 @@
{
- "rawKB": 63.9,
- "gzipKB": 24.3,
+ "rawKB": 62.9,
+ "gzipKB": 23.9,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene223-standard-renderable-DnE_HU3_.js",
+ "scene223-standard-renderable-Ckz_QFap.js",
"scene223-swapchain-overlay-ilhN4El_.js",
- "scene223-wgsl-helpers-CwHFFXvt.js",
"scene223.js"
]
}
diff --git a/lab/public/bundle/manifest/scene224.json b/lab/public/bundle/manifest/scene224.json
index d8f9cd07f2..05fbdef09b 100644
--- a/lab/public/bundle/manifest/scene224.json
+++ b/lab/public/bundle/manifest/scene224.json
@@ -1,11 +1,10 @@
{
- "rawKB": 81.8,
- "gzipKB": 30.4,
+ "rawKB": 80.7,
+ "gzipKB": 30,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene224-standard-renderable-DWNKdo2N.js",
+ "scene224-standard-renderable-C_bZOxUq.js",
"scene224-swapchain-overlay-ilhN4El_.js",
- "scene224-wgsl-helpers-CwHFFXvt.js",
"scene224.js"
]
}
diff --git a/lab/public/bundle/manifest/scene225.json b/lab/public/bundle/manifest/scene225.json
index 11153c4a14..7171310e2d 100644
--- a/lab/public/bundle/manifest/scene225.json
+++ b/lab/public/bundle/manifest/scene225.json
@@ -1,10 +1,9 @@
{
- "rawKB": 58.2,
- "gzipKB": 23.4,
+ "rawKB": 57.2,
+ "gzipKB": 23,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene225-standard-renderable-DKiRZ8hQ.js",
- "scene225-wgsl-helpers-CwHFFXvt.js",
+ "scene225-standard-renderable-BDW6UtVR.js",
"scene225.js"
]
}
diff --git a/lab/public/bundle/manifest/scene227.json b/lab/public/bundle/manifest/scene227.json
index 907677dad6..5b94296228 100644
--- a/lab/public/bundle/manifest/scene227.json
+++ b/lab/public/bundle/manifest/scene227.json
@@ -1,10 +1,9 @@
{
- "rawKB": 48.4,
- "gzipKB": 19.2,
+ "rawKB": 47.4,
+ "gzipKB": 18.7,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene227-standard-renderable-DSZGd6oH.js",
- "scene227-wgsl-helpers-CwHFFXvt.js",
+ "scene227-standard-renderable-CI24_mt_.js",
"scene227.js"
]
}
diff --git a/lab/public/bundle/manifest/scene228.json b/lab/public/bundle/manifest/scene228.json
index 7e9978a74e..2271150464 100644
--- a/lab/public/bundle/manifest/scene228.json
+++ b/lab/public/bundle/manifest/scene228.json
@@ -1,10 +1,9 @@
{
- "rawKB": 50.2,
- "gzipKB": 20,
+ "rawKB": 49.2,
+ "gzipKB": 19.5,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene228-standard-renderable-DlLkRdiv.js",
- "scene228-wgsl-helpers-CwHFFXvt.js",
+ "scene228-standard-renderable-BL6_DDU8.js",
"scene228.js"
]
}
diff --git a/lab/public/bundle/manifest/scene231.json b/lab/public/bundle/manifest/scene231.json
new file mode 100644
index 0000000000..71e4f02c6e
--- /dev/null
+++ b/lab/public/bundle/manifest/scene231.json
@@ -0,0 +1,12 @@
+{
+ "rawKB": 52.9,
+ "gzipKB": 21,
+ "ignoredRawKB": 0,
+ "runtimeChunks": [
+ "scene231-standard-renderable-D0Q4Homm.js",
+ "scene231-std-feature-hooks-C2W70yZH.js",
+ "scene231-std-skeleton-fragment-XbOa0aFW.js",
+ "scene231-std-vertex-color-fragment-BesIHRMf.js",
+ "scene231.js"
+ ]
+}
diff --git a/lab/public/bundle/manifest/scene24.json b/lab/public/bundle/manifest/scene24.json
index b8b6d93f74..4344ce94b4 100644
--- a/lab/public/bundle/manifest/scene24.json
+++ b/lab/public/bundle/manifest/scene24.json
@@ -1,6 +1,6 @@
{
- "rawKB": 59,
- "gzipKB": 24.6,
+ "rawKB": 58,
+ "gzipKB": 24.1,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene24-bake-local-matrix-67U-IT8C.js",
@@ -8,12 +8,11 @@
"scene24-generate-mipmaps-BaRC2la_.js",
"scene24-parse-camera-D4iQaH48.js",
"scene24-point-light-Dzvg6X6L.js",
- "scene24-standard-renderable-a3hpbuT2.js",
+ "scene24-standard-renderable-CqEDqzcG.js",
"scene24-std-ambient-fragment-qLXGuA_5.js",
"scene24-std-cube-reflection-fragment-B-EokvPj.js",
"scene24-std-opacity-fragment-BMTURaOK.js",
"scene24-std-specular-fragment-DhCYMq1P.js",
- "scene24-wgsl-helpers-CwHFFXvt.js",
"scene24.js"
]
}
diff --git a/lab/public/bundle/manifest/scene25.json b/lab/public/bundle/manifest/scene25.json
index 3e027f7193..181cb6aa88 100644
--- a/lab/public/bundle/manifest/scene25.json
+++ b/lab/public/bundle/manifest/scene25.json
@@ -1,10 +1,9 @@
{
- "rawKB": 52.1,
- "gzipKB": 20.7,
+ "rawKB": 51,
+ "gzipKB": 20.2,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene25-standard-renderable-0EV0Ofua.js",
- "scene25-wgsl-helpers-CwHFFXvt.js",
+ "scene25-standard-renderable-D-7KZT2R.js",
"scene25.js"
]
}
diff --git a/lab/public/bundle/manifest/scene252.json b/lab/public/bundle/manifest/scene252.json
index 445b05d8d9..aa6bdcff57 100644
--- a/lab/public/bundle/manifest/scene252.json
+++ b/lab/public/bundle/manifest/scene252.json
@@ -1,11 +1,10 @@
{
- "rawKB": 49,
- "gzipKB": 19.8,
+ "rawKB": 48,
+ "gzipKB": 19.4,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene252-morph-fragment-core-BW8kE7zi.js",
- "scene252-standard-renderable-BrPvNfxw.js",
- "scene252-wgsl-helpers-CwHFFXvt.js",
+ "scene252-standard-renderable-Dhm3MYGk.js",
"scene252.js"
]
}
diff --git a/lab/public/bundle/manifest/scene261.json b/lab/public/bundle/manifest/scene261.json
index cc20e5b688..9d2666e6a7 100644
--- a/lab/public/bundle/manifest/scene261.json
+++ b/lab/public/bundle/manifest/scene261.json
@@ -1,10 +1,9 @@
{
- "rawKB": 54.5,
- "gzipKB": 21.3,
+ "rawKB": 53.4,
+ "gzipKB": 20.8,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene261-standard-renderable-BVWOm7Y2.js",
- "scene261-wgsl-helpers-CwHFFXvt.js",
+ "scene261-standard-renderable-COzhP9_C.js",
"scene261.js"
]
}
diff --git a/lab/public/bundle/manifest/scene3.json b/lab/public/bundle/manifest/scene3.json
index 6b528a415d..a1f2632be8 100644
--- a/lab/public/bundle/manifest/scene3.json
+++ b/lab/public/bundle/manifest/scene3.json
@@ -1,11 +1,12 @@
{
- "rawKB": 54,
- "gzipKB": 21.6,
+ "rawKB": 54.3,
+ "gzipKB": 21.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene3-scene-uniforms-FTYH6Ct0.js",
"scene3-skybox-renderable-B-1mAKv9.js",
- "scene3-standard-renderable-7jpxQTFQ.js",
+ "scene3-standard-renderable-VUEouOUq.js",
+ "scene3-std-fog-wgsl-D0HYiOcd.js",
"scene3-wgsl-helpers-DkIRJkUm.js",
"scene3.js"
]
diff --git a/lab/public/bundle/manifest/scene36.json b/lab/public/bundle/manifest/scene36.json
index 3df6a3db6c..ebc600ffb4 100644
--- a/lab/public/bundle/manifest/scene36.json
+++ b/lab/public/bundle/manifest/scene36.json
@@ -1,11 +1,10 @@
{
- "rawKB": 51.7,
- "gzipKB": 20.4,
+ "rawKB": 50.6,
+ "gzipKB": 19.9,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene36-standard-renderable-DZtkeKp5.js",
+ "scene36-standard-renderable-BcvUBtCB.js",
"scene36-std-emissive-fragment-DthzsVUc.js",
- "scene36-wgsl-helpers-CwHFFXvt.js",
"scene36.js"
]
}
diff --git a/lab/public/bundle/manifest/scene38.json b/lab/public/bundle/manifest/scene38.json
index ee1e308513..fb3fcef046 100644
--- a/lab/public/bundle/manifest/scene38.json
+++ b/lab/public/bundle/manifest/scene38.json
@@ -1,10 +1,9 @@
{
- "rawKB": 61.3,
- "gzipKB": 24.2,
+ "rawKB": 60.2,
+ "gzipKB": 23.8,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene38-standard-renderable-DkWPKKU6.js",
- "scene38-wgsl-helpers-CwHFFXvt.js",
+ "scene38-standard-renderable-BqnUkSQa.js",
"scene38.js"
]
}
diff --git a/lab/public/bundle/manifest/scene4.json b/lab/public/bundle/manifest/scene4.json
index 7cf31215f7..1b19fc9170 100644
--- a/lab/public/bundle/manifest/scene4.json
+++ b/lab/public/bundle/manifest/scene4.json
@@ -1,6 +1,6 @@
{
- "rawKB": 72.6,
- "gzipKB": 28.6,
+ "rawKB": 71.6,
+ "gzipKB": 28.2,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene4-esm-shadow-view-7yqUFA1x.js",
@@ -8,9 +8,8 @@
"scene4-material-view-Dua1bl7W.js",
"scene4-no-color-view-jut7SxNQ.js",
"scene4-shadow-task-DKE6tIG9.js",
- "scene4-standard-renderable-C-a_tPgC.js",
+ "scene4-standard-renderable-BJByRoqq.js",
"scene4-std-shadow-fragment-pwyXRo4i.js",
- "scene4-wgsl-helpers-CwHFFXvt.js",
"scene4.js"
]
}
diff --git a/lab/public/bundle/manifest/scene40.json b/lab/public/bundle/manifest/scene40.json
index fcaaf3da39..b449e27acd 100644
--- a/lab/public/bundle/manifest/scene40.json
+++ b/lab/public/bundle/manifest/scene40.json
@@ -1,10 +1,9 @@
{
- "rawKB": 49.7,
- "gzipKB": 20,
+ "rawKB": 48.6,
+ "gzipKB": 19.5,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene40-standard-renderable-CYGiOwyw.js",
- "scene40-wgsl-helpers-CwHFFXvt.js",
+ "scene40-standard-renderable-BSbBBde4.js",
"scene40.js"
]
}
diff --git a/lab/public/bundle/manifest/scene41.json b/lab/public/bundle/manifest/scene41.json
index acacb9746e..119ce50134 100644
--- a/lab/public/bundle/manifest/scene41.json
+++ b/lab/public/bundle/manifest/scene41.json
@@ -1,6 +1,6 @@
{
- "rawKB": 92.9,
- "gzipKB": 36.9,
+ "rawKB": 91.8,
+ "gzipKB": 36.5,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene41-generate-mipmaps-BWxJCIcc.js",
@@ -10,8 +10,7 @@
"scene41-gltf-sampler-desc-E_7RaQlV.js",
"scene41-point-light-CnkkP9oB.js",
"scene41-shader-composer-CgJmQyJj.js",
- "scene41-standard-renderable-CRuhIfFk.js",
- "scene41-wgsl-helpers-CwHFFXvt.js",
+ "scene41-standard-renderable-14ulrC65.js",
"scene41.js"
]
}
diff --git a/lab/public/bundle/manifest/scene42.json b/lab/public/bundle/manifest/scene42.json
index 457b675044..338294eb49 100644
--- a/lab/public/bundle/manifest/scene42.json
+++ b/lab/public/bundle/manifest/scene42.json
@@ -1,10 +1,9 @@
{
- "rawKB": 51.3,
- "gzipKB": 20.3,
+ "rawKB": 50.2,
+ "gzipKB": 19.9,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene42-standard-renderable-B-mqcMeS.js",
- "scene42-wgsl-helpers-CwHFFXvt.js",
+ "scene42-standard-renderable-DO0JZNHm.js",
"scene42.js"
]
}
diff --git a/lab/public/bundle/manifest/scene43.json b/lab/public/bundle/manifest/scene43.json
index 8941365d23..93ea0ec283 100644
--- a/lab/public/bundle/manifest/scene43.json
+++ b/lab/public/bundle/manifest/scene43.json
@@ -1,12 +1,11 @@
{
- "rawKB": 58.1,
- "gzipKB": 23.2,
+ "rawKB": 57.1,
+ "gzipKB": 22.8,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene43-standard-renderable-BeC4p3CE.js",
+ "scene43-standard-renderable-D5wwk8xt.js",
"scene43-thin-instance-fragment-CBn2miAC.js",
"scene43-thin-instance-gpu-UxO3XtwE.js",
- "scene43-wgsl-helpers-CwHFFXvt.js",
"scene43.js"
]
}
diff --git a/lab/public/bundle/manifest/scene44.json b/lab/public/bundle/manifest/scene44.json
index e65f5586a9..d90734a1d9 100644
--- a/lab/public/bundle/manifest/scene44.json
+++ b/lab/public/bundle/manifest/scene44.json
@@ -1,10 +1,9 @@
{
- "rawKB": 50.2,
- "gzipKB": 20.1,
+ "rawKB": 49.1,
+ "gzipKB": 19.6,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene44-standard-renderable-Bu9LYSr6.js",
- "scene44-wgsl-helpers-CwHFFXvt.js",
+ "scene44-standard-renderable-D7grmEBD.js",
"scene44.js"
]
}
diff --git a/lab/public/bundle/manifest/scene45.json b/lab/public/bundle/manifest/scene45.json
index d45e53cbd3..afa0f0e24b 100644
--- a/lab/public/bundle/manifest/scene45.json
+++ b/lab/public/bundle/manifest/scene45.json
@@ -1,10 +1,9 @@
{
- "rawKB": 51.3,
- "gzipKB": 20.4,
+ "rawKB": 50.3,
+ "gzipKB": 20,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene45-standard-renderable-BieL1U6W.js",
- "scene45-wgsl-helpers-CwHFFXvt.js",
+ "scene45-standard-renderable-DTOeh1Uk.js",
"scene45.js"
]
}
diff --git a/lab/public/bundle/manifest/scene46.json b/lab/public/bundle/manifest/scene46.json
index a202d34e56..d2aa068a66 100644
--- a/lab/public/bundle/manifest/scene46.json
+++ b/lab/public/bundle/manifest/scene46.json
@@ -1,10 +1,9 @@
{
- "rawKB": 54.4,
- "gzipKB": 21.4,
+ "rawKB": 53.4,
+ "gzipKB": 20.9,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene46-standard-renderable-hx5y8uJN.js",
- "scene46-wgsl-helpers-CwHFFXvt.js",
+ "scene46-standard-renderable-t06DqCCs.js",
"scene46.js"
]
}
diff --git a/lab/public/bundle/manifest/scene47.json b/lab/public/bundle/manifest/scene47.json
index 0138a7dd1c..4bab5ddcff 100644
--- a/lab/public/bundle/manifest/scene47.json
+++ b/lab/public/bundle/manifest/scene47.json
@@ -1,6 +1,6 @@
{
- "rawKB": 92.2,
- "gzipKB": 36.8,
+ "rawKB": 91.1,
+ "gzipKB": 36.3,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene47-generate-mipmaps-CmIGpFd5.js",
@@ -9,8 +9,7 @@
"scene47-gltf-glb-parser-6-tsF2C2.js",
"scene47-gltf-sampler-desc-BJMmnJDF.js",
"scene47-shader-composer-CgJmQyJj.js",
- "scene47-standard-renderable-CUonYaUG.js",
- "scene47-wgsl-helpers-CwHFFXvt.js",
+ "scene47-standard-renderable-C8-hTfN6.js",
"scene47.js"
]
}
diff --git a/lab/public/bundle/manifest/scene48.json b/lab/public/bundle/manifest/scene48.json
index 02832779bb..bdd92db00d 100644
--- a/lab/public/bundle/manifest/scene48.json
+++ b/lab/public/bundle/manifest/scene48.json
@@ -1,10 +1,9 @@
{
- "rawKB": 54.3,
- "gzipKB": 21.6,
+ "rawKB": 53.3,
+ "gzipKB": 21.1,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene48-standard-renderable-C3pxu2Wg.js",
- "scene48-wgsl-helpers-CwHFFXvt.js",
+ "scene48-standard-renderable-BLdyT1WV.js",
"scene48.js"
]
}
diff --git a/lab/public/bundle/manifest/scene49.json b/lab/public/bundle/manifest/scene49.json
index b6ae5b3185..42793cc073 100644
--- a/lab/public/bundle/manifest/scene49.json
+++ b/lab/public/bundle/manifest/scene49.json
@@ -1,12 +1,11 @@
{
- "rawKB": 92.8,
- "gzipKB": 35,
+ "rawKB": 91.7,
+ "gzipKB": 34.5,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene49-standard-renderable-ytL-ZaT7.js",
+ "scene49-standard-renderable-DypapRX-.js",
"scene49-swapchain-overlay-ilhN4El_.js",
"scene49-ubo-layout-CnrP4RZD.js",
- "scene49-wgsl-helpers-CwHFFXvt.js",
"scene49.js"
]
}
diff --git a/lab/public/bundle/manifest/scene52.json b/lab/public/bundle/manifest/scene52.json
index c3cfaadd2f..c49737435b 100644
--- a/lab/public/bundle/manifest/scene52.json
+++ b/lab/public/bundle/manifest/scene52.json
@@ -1,10 +1,9 @@
{
- "rawKB": 57.4,
- "gzipKB": 23.1,
+ "rawKB": 56.4,
+ "gzipKB": 22.6,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene52-standard-renderable-DXnTjsje.js",
- "scene52-wgsl-helpers-CwHFFXvt.js",
+ "scene52-standard-renderable-Dnrt9hqQ.js",
"scene52.js"
]
}
diff --git a/lab/public/bundle/manifest/scene53.json b/lab/public/bundle/manifest/scene53.json
index 2362aab3ae..fa538634d6 100644
--- a/lab/public/bundle/manifest/scene53.json
+++ b/lab/public/bundle/manifest/scene53.json
@@ -1,10 +1,9 @@
{
- "rawKB": 57.6,
- "gzipKB": 23,
+ "rawKB": 56.5,
+ "gzipKB": 22.5,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene53-standard-renderable-BwQeEm6J.js",
- "scene53-wgsl-helpers-CwHFFXvt.js",
+ "scene53-standard-renderable-BHOghp1k.js",
"scene53.js"
]
}
diff --git a/lab/public/bundle/manifest/scene54.json b/lab/public/bundle/manifest/scene54.json
index 9c6755c2eb..5c964221dc 100644
--- a/lab/public/bundle/manifest/scene54.json
+++ b/lab/public/bundle/manifest/scene54.json
@@ -1,12 +1,11 @@
{
- "rawKB": 60,
- "gzipKB": 24.3,
+ "rawKB": 58.9,
+ "gzipKB": 23.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene54-billboard-renderable-BeTxDz7N.js",
"scene54-scene-uniforms-FTYH6Ct0.js",
- "scene54-standard-renderable-gymiA78h.js",
- "scene54-wgsl-helpers-CwHFFXvt.js",
+ "scene54-standard-renderable-DVTnTwex.js",
"scene54.js"
]
}
diff --git a/lab/public/bundle/manifest/scene56.json b/lab/public/bundle/manifest/scene56.json
index a8833b727c..f3dd8605ac 100644
--- a/lab/public/bundle/manifest/scene56.json
+++ b/lab/public/bundle/manifest/scene56.json
@@ -1,12 +1,11 @@
{
- "rawKB": 60.3,
- "gzipKB": 24.4,
+ "rawKB": 59.2,
+ "gzipKB": 24,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene56-billboard-renderable-rwL30tEO.js",
"scene56-scene-uniforms-FTYH6Ct0.js",
- "scene56-standard-renderable-DLV9jNG_.js",
- "scene56-wgsl-helpers-CwHFFXvt.js",
+ "scene56-standard-renderable-C919me1H.js",
"scene56.js"
]
}
diff --git a/lab/public/bundle/manifest/scene57.json b/lab/public/bundle/manifest/scene57.json
index aad25185e7..0b2c8060f3 100644
--- a/lab/public/bundle/manifest/scene57.json
+++ b/lab/public/bundle/manifest/scene57.json
@@ -1,12 +1,11 @@
{
- "rawKB": 60.1,
- "gzipKB": 24.3,
+ "rawKB": 59,
+ "gzipKB": 23.8,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene57-billboard-renderable-CUrr_OIJ.js",
"scene57-scene-uniforms-FTYH6Ct0.js",
- "scene57-standard-renderable-Cv1M5x8s.js",
- "scene57-wgsl-helpers-CwHFFXvt.js",
+ "scene57-standard-renderable-DGgYnSaX.js",
"scene57.js"
]
}
diff --git a/lab/public/bundle/manifest/scene59.json b/lab/public/bundle/manifest/scene59.json
index 87f49781c6..90eea99aff 100644
--- a/lab/public/bundle/manifest/scene59.json
+++ b/lab/public/bundle/manifest/scene59.json
@@ -1,12 +1,11 @@
{
- "rawKB": 63.1,
- "gzipKB": 25.4,
+ "rawKB": 62.1,
+ "gzipKB": 24.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene59-billboard-renderable-KN9gLXsu.js",
"scene59-scene-uniforms-FTYH6Ct0.js",
- "scene59-standard-renderable-DnLmB7RO.js",
- "scene59-wgsl-helpers-CwHFFXvt.js",
+ "scene59-standard-renderable-DSbJcNia.js",
"scene59.js"
]
}
diff --git a/lab/public/bundle/manifest/scene75.json b/lab/public/bundle/manifest/scene75.json
index 5552b1faeb..09d3fb2fe3 100644
--- a/lab/public/bundle/manifest/scene75.json
+++ b/lab/public/bundle/manifest/scene75.json
@@ -1,10 +1,9 @@
{
- "rawKB": 50.4,
- "gzipKB": 20.1,
+ "rawKB": 49.3,
+ "gzipKB": 19.7,
"ignoredRawKB": 0,
"runtimeChunks": [
- "scene75-standard-renderable-CYo4Y48I.js",
- "scene75-wgsl-helpers-CwHFFXvt.js",
+ "scene75-standard-renderable-CzXYDff7.js",
"scene75.js"
]
}
diff --git a/lab/public/bundle/manifest/scene84.json b/lab/public/bundle/manifest/scene84.json
index b084224767..f820d15867 100644
--- a/lab/public/bundle/manifest/scene84.json
+++ b/lab/public/bundle/manifest/scene84.json
@@ -1,6 +1,6 @@
{
- "rawKB": 84.6,
- "gzipKB": 36.2,
+ "rawKB": 83.5,
+ "gzipKB": 35.7,
"ignoredRawKB": 5.5,
"runtimeChunks": [
"scene84-_math-factory-DFVyS8gB.js",
@@ -17,12 +17,11 @@
"scene84-node-renderable-UtCGlhZJ.js",
"scene84-screen-size-block-yVY2pHhh.js",
"scene84-screen-space-block-Cfi3u-na.js",
- "scene84-standard-renderable-B56QP5k3.js",
+ "scene84-standard-renderable-BAlJYZmI.js",
"scene84-transform-block-PEh7VaCZ.js",
"scene84-twirl-block-tQLhuAc6.js",
"scene84-vector-splitter-BMf4DQi8.js",
"scene84-vertex-output-C7G5u6Y0.js",
- "scene84-wgsl-helpers-CwHFFXvt.js",
"scene84.js"
]
}
diff --git a/lab/public/bundle/manifest/scene9.json b/lab/public/bundle/manifest/scene9.json
index 8a66579e2e..17b4e5a13e 100644
--- a/lab/public/bundle/manifest/scene9.json
+++ b/lab/public/bundle/manifest/scene9.json
@@ -1,12 +1,12 @@
{
"rawKB": 59.6,
- "gzipKB": 24.1,
+ "gzipKB": 24.2,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene9-generate-mipmaps-xw5BdfZd.js",
"scene9-normal-map-fragment-BjHVJ1qk.js",
"scene9-point-light-ESYN5_k0.js",
- "scene9-standard-renderable-DIEblUNe.js",
+ "scene9-standard-renderable-B7mFN3Tk.js",
"scene9-std-ambient-fragment-BT8qGSob.js",
"scene9-std-opacity-fragment-Dwz7NrjB.js",
"scene9-std-reflection-fragment-Cb2Nq9Do.js",
diff --git a/lab/public/bundle/manifest/scene90.json b/lab/public/bundle/manifest/scene90.json
index 35ea4b15a3..0355c59fdf 100644
--- a/lab/public/bundle/manifest/scene90.json
+++ b/lab/public/bundle/manifest/scene90.json
@@ -1,11 +1,10 @@
{
- "rawKB": 59.3,
- "gzipKB": 23.5,
+ "rawKB": 58.2,
+ "gzipKB": 23,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene90-generate-mipmaps-SRXClq-S.js",
- "scene90-standard-renderable-CTe6AqwL.js",
- "scene90-wgsl-helpers-CwHFFXvt.js",
+ "scene90-standard-renderable-D9oDnwRJ.js",
"scene90.js"
]
}
diff --git a/lab/public/bundle/manifest/scene91.json b/lab/public/bundle/manifest/scene91.json
index 364bd4c468..3bf62f5d8e 100644
--- a/lab/public/bundle/manifest/scene91.json
+++ b/lab/public/bundle/manifest/scene91.json
@@ -1,12 +1,11 @@
{
- "rawKB": 58.6,
- "gzipKB": 23.4,
+ "rawKB": 57.5,
+ "gzipKB": 23,
"ignoredRawKB": 1227.9,
"runtimeChunks": [
"scene91-generate-mipmaps-CUt8_7AA.js",
"scene91-manifold-Sh8xE2da-C8q62PkN.js",
- "scene91-standard-renderable-x17W8JUu.js",
- "scene91-wgsl-helpers-CwHFFXvt.js",
+ "scene91-standard-renderable-CYw-ygSr.js",
"scene91.js"
]
}
diff --git a/lab/public/bundle/manifest/scene94.json b/lab/public/bundle/manifest/scene94.json
index 9f236fb1d5..f4fed1dbb0 100644
--- a/lab/public/bundle/manifest/scene94.json
+++ b/lab/public/bundle/manifest/scene94.json
@@ -1,11 +1,10 @@
{
- "rawKB": 63.9,
- "gzipKB": 25.2,
+ "rawKB": 62.8,
+ "gzipKB": 24.8,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene94-billboard-renderable-BgBdc7uu.js",
- "scene94-standard-renderable-C7WeVEUG.js",
- "scene94-wgsl-helpers-CwHFFXvt.js",
+ "scene94-standard-renderable-DbK2IvV9.js",
"scene94.js"
]
}
diff --git a/lab/public/bundle/manifest/scene95.json b/lab/public/bundle/manifest/scene95.json
index 73867ec8b4..0637d8d95f 100644
--- a/lab/public/bundle/manifest/scene95.json
+++ b/lab/public/bundle/manifest/scene95.json
@@ -1,11 +1,10 @@
{
- "rawKB": 65,
- "gzipKB": 25.6,
+ "rawKB": 63.9,
+ "gzipKB": 25.1,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene95-billboard-renderable-D1Ojksz4.js",
- "scene95-standard-renderable-Dlc9juiN.js",
- "scene95-wgsl-helpers-CwHFFXvt.js",
+ "scene95-standard-renderable-BDf4WrSx.js",
"scene95.js"
]
}
diff --git a/lab/public/bundle/manifest/scene98.json b/lab/public/bundle/manifest/scene98.json
index cfc55ac6ff..8507a069db 100644
--- a/lab/public/bundle/manifest/scene98.json
+++ b/lab/public/bundle/manifest/scene98.json
@@ -1,12 +1,11 @@
{
- "rawKB": 60.2,
- "gzipKB": 24.4,
+ "rawKB": 59.2,
+ "gzipKB": 23.9,
"ignoredRawKB": 0,
"runtimeChunks": [
"scene98-billboard-renderable-jlPUXki_.js",
"scene98-scene-uniforms-FTYH6Ct0.js",
- "scene98-standard-renderable-uFFg5Orm.js",
- "scene98-wgsl-helpers-CwHFFXvt.js",
+ "scene98-standard-renderable-BA0XJKwc.js",
"scene98.js"
]
}
diff --git a/packages/babylon-lite/src/material/pbr/fragments/skeleton-fragment.ts b/packages/babylon-lite/src/material/pbr/fragments/skeleton-fragment.ts
index 8715105cc6..2e29c27bc7 100644
--- a/packages/babylon-lite/src/material/pbr/fragments/skeleton-fragment.ts
+++ b/packages/babylon-lite/src/material/pbr/fragments/skeleton-fragment.ts
@@ -1,73 +1,17 @@
/**
- * Skeleton Fragment
+ * Skeleton Fragment (PBR ext wrapper)
*
- * Vertex-stage skeletal animation: bone texture sampling + skinning matrix.
- * Only bundled when a scene has skeletal animation.
- * Supports 4-bone and 8-bone skinning.
+ * Re-exports the shared, material-agnostic skeleton fragment and wraps it in a
+ * PBR extension (`PbrExt`) so the PBR composer can detect/bind skinning.
+ * The shared `createSkeletonFragment` lives in `shader/fragments/skeleton-fragment.ts`
+ * and is reused by the Standard material side without pulling in this wrapper.
+ * PBR keeps the default `"vertex"` binding style (bone texture placed before base
+ * bindings) — behaviour identical to before the move.
*/
-import type { ShaderFragment, VertexAttribute } from "../../../shader/fragment-types.js";
+import { createSkeletonFragment } from "../../../shader/fragments/skeleton-fragment.js";
-// WebGPU shader stage constants
-const STAGE_VERTEX = 0x1;
-
-const SKELETON_HELPERS = `
-fn readMatrixFromRawSampler(smp: texture_2d, index: f32) -> mat4x4 {
-let offset = i32(index) * 4;
-let m0 = textureLoad(smp, vec2(offset + 0, 0), 0);
-let m1 = textureLoad(smp, vec2(offset + 1, 0), 0);
-let m2 = textureLoad(smp, vec2(offset + 2, 0), 0);
-let m3 = textureLoad(smp, vec2(offset + 3, 0), 0);
-return mat4x4f(m0, m1, m2, m3);
-}
-`;
-
-function makeSkinningCode(has8Bones: boolean): string {
- let code = `var influence: mat4x4 = readMatrixFromRawSampler(boneSampler, f32(joints[0])) * weights[0];
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints[1])) * weights[1];
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints[2])) * weights[2];
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints[3])) * weights[3];`;
- if (has8Bones) {
- code += `
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[0])) * weights1[0];
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[1])) * weights1[1];
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[2])) * weights1[2];
-influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[3])) * weights1[3];`;
- }
- code += `\nfinalWorld = mesh.world * influence;`;
- return code;
-}
-
-/**
- * Create a skeleton fragment.
- * @param has8Bones - Whether to use 8-bone skinning (joints1/weights1).
- */
-export function createSkeletonFragment(has8Bones: boolean): ShaderFragment {
- return {
- _id: "skeleton",
-
- _vertexAttributes: [
- { _name: "joints", _type: "vec4", _gpuFormat: "uint32x4" as GPUVertexFormat, _arrayStride: 16 },
- { _name: "weights", _type: "vec4", _gpuFormat: "float32x4" as GPUVertexFormat, _arrayStride: 16 },
- ...(has8Bones
- ? [
- { _name: "joints1", _type: "vec4", _gpuFormat: "uint32x4" as GPUVertexFormat, _arrayStride: 16 },
- { _name: "weights1", _type: "vec4", _gpuFormat: "float32x4" as GPUVertexFormat, _arrayStride: 16 },
- ]
- : []),
- ] as VertexAttribute[],
-
- _vertexBindings: [
- { _name: "boneSampler", _type: { _kind: "texture", _textureType: "texture_2d" as const, _sampleType: "unfilterable-float" as const }, _visibility: STAGE_VERTEX },
- ],
-
- _vertexHelperFunctions: SKELETON_HELPERS,
-
- _vertexSlots: {
- VW: makeSkinningCode(has8Bones),
- },
- };
-}
+export { createSkeletonFragment };
import type { PbrExt } from "../pbr-flags.js";
import { MSH_HAS_SKELETON, MSH_HAS_SKELETON_8 } from "../../mesh-features.js";
diff --git a/packages/babylon-lite/src/material/standard/enable-standard-mesh-features.ts b/packages/babylon-lite/src/material/standard/enable-standard-mesh-features.ts
new file mode 100644
index 0000000000..f5258d06af
--- /dev/null
+++ b/packages/babylon-lite/src/material/standard/enable-standard-mesh-features.ts
@@ -0,0 +1,53 @@
+/**
+ * Opt-in enablers for the Standard material's deform/vertex mesh features (vertex color, skeleton)
+ * and UV offset. Each registers a dispatcher into the Standard group-builder's module-local
+ * `_stdMeshExtDispatch` registry so the group-builder dynamically loads the matching fragment chunk
+ * when a mesh in the group has the feature (UV offset toggles a pipeline fold flag instead).
+ *
+ * This is the **net-neutral fold seam** (same pattern as `enableMaterialStencil`): a scene that
+ * never calls one of these never imports this module, so the group-builder's dispatch registry and
+ * the per-feature shader chunks fold completely out of the bundle. Loaders (the FBX loader) and
+ * in-code scenes that build such meshes call the matching `enableStandard*()`, so only those scenes
+ * pay for them; plain Standard scenes stay byte-identical to upstream.
+ *
+ * Each enabler is idempotent (registers its dispatcher at most once per session).
+ *
+ * NOTE: morph targets are integrated automatically by master's `_computeMeshFeatures` path (no
+ * opt-in needed), and explicit-tangent normal mapping is not yet ported — so no enabler for those.
+ */
+
+import type { Mesh } from "../../mesh/mesh.js";
+import { _registerStdMeshExtDispatch } from "./standard-group-builder.js";
+import { _installStandardUvOffset } from "./standard-pipeline.js";
+
+let _vColorEnabled = false;
+/** Enable Standard per-vertex color (RGB). Called by loaders/scenes that build vertex-colored meshes. */
+export function enableStandardVertexColor(): void {
+ if (_vColorEnabled) {
+ return;
+ }
+ _vColorEnabled = true;
+ _registerStdMeshExtDispatch([(m: Mesh) => !!m._gpu?.colorBuffer, () => import("./fragments/std-vertex-color-fragment.js"), "stdVertexColorExt"]);
+}
+
+let _skeletonEnabled = false;
+/** Enable Standard skeletal skinning. Called by loaders/scenes that build skinned meshes. */
+export function enableStandardSkeleton(): void {
+ if (_skeletonEnabled) {
+ return;
+ }
+ _skeletonEnabled = true;
+ _registerStdMeshExtDispatch([(m: Mesh) => !!m.skeleton, () => import("./fragments/std-skeleton-fragment.js"), "stdSkeletonExt"]);
+}
+
+let _uvOffsetEnabled = false;
+/** Enable Standard UV offset (`material.uvOffset`). Called by loaders/scenes that set a non-zero UV
+ * translation (the FBX loader, from `uvTranslation`). Without this, the pipeline's UV-offset reads
+ * fold to a constant 0 so non-offset scenes stay byte-identical. */
+export function enableStandardUvOffset(): void {
+ if (_uvOffsetEnabled) {
+ return;
+ }
+ _uvOffsetEnabled = true;
+ _installStandardUvOffset();
+}
diff --git a/packages/babylon-lite/src/material/standard/fragments/std-skeleton-fragment.ts b/packages/babylon-lite/src/material/standard/fragments/std-skeleton-fragment.ts
new file mode 100644
index 0000000000..ebd2eaf221
--- /dev/null
+++ b/packages/babylon-lite/src/material/standard/fragments/std-skeleton-fragment.ts
@@ -0,0 +1,56 @@
+/** Standard Skeleton Extension — vertex-stage skeletal (skinning) deformation wired as a
+ * plain StdExt. Reuses the shared, material-agnostic skeleton fragment (the same
+ * WGSL/attributes/helper/VW used by PBR) and relocates its single bone-texture binding
+ * from `_vertexBindings` to `_bindings` (afterBase) so the bone texture lands AFTER the
+ * base bindings, where the shared StdExt bind loop already runs. Doing the relocation here
+ * (rather than parameterizing the shared factory) keeps the PBR skeleton chunk byte-identical
+ * to before the fragment was shared. Gated on the mesh-driven HAS_SKELETON feature bit
+ * (8-bone via HAS_SKELETON_8). The joints/weights vertex buffers are bound generically
+ * through `_bindVertexBuffers`, mirroring the PBR draw's skinning vertex-buffer order. */
+
+import type { ShaderFragment } from "../../../shader/fragment-types.js";
+import { createSkeletonFragment } from "../../../shader/fragments/skeleton-fragment.js";
+import type { Mesh } from "../../../mesh/mesh.js";
+import type { StandardMaterialProps } from "../standard-material.js";
+import type { StdExt } from "../standard-flags.js";
+import { HAS_SKELETON, HAS_SKELETON_8 } from "../standard-flags.js";
+import { _installStdExtFeature } from "../std-feature-hooks.js";
+
+/** Registry extension gated on `HAS_SKELETON`. The bone texture is a vertex-only mesh-driven
+ * resource, so `_bind` pulls it off the mesh (not the material) and pushes it in the same
+ * order the composer declares it (afterBase). `_bindVertexBuffers` binds the joints/weights
+ * (+joints1/weights1 for 8-bone) draw-time vertex buffers in the composer's attribute order.
+ * No `_textures` — the bone texture is owned by the mesh's skeleton system, not the GPU pool. */
+export const stdSkeletonExt: StdExt = {
+ _id: "skeleton",
+ _phase: "mesh",
+ _feature: HAS_SKELETON,
+ _frag: (features: number): ShaderFragment => {
+ // Reuse the shared fragment (PBR's "vertex" placement) and relocate the single bone
+ // binding to `_bindings` so the Standard composer's trailing ext-bind loop binds it
+ // after the base bindings — matching `_bind` below.
+ const frag = createSkeletonFragment((features & HAS_SKELETON_8) !== 0);
+ return { ...frag, _bindings: frag._vertexBindings, _vertexBindings: undefined };
+ },
+ _bind(_mat: StandardMaterialProps, entries: GPUBindGroupEntry[], b: number, mesh?: Mesh): number {
+ entries.push({ binding: b++, resource: mesh!.skeleton!.boneTexture.createView() });
+ return b;
+ },
+ _bindVertexBuffers(mesh: Mesh, pass: GPURenderPassEncoder | GPURenderBundleEncoder, slot: number): number {
+ const s = mesh.skeleton;
+ if (!s) {
+ return slot;
+ }
+ pass.setVertexBuffer(slot++, s.jointsBuffer);
+ pass.setVertexBuffer(slot++, s.weightsBuffer);
+ if (s.joints1Buffer && s.weights1Buffer) {
+ pass.setVertexBuffer(slot++, s.joints1Buffer);
+ pass.setVertexBuffer(slot++, s.weights1Buffer);
+ }
+ return slot;
+ },
+};
+
+// Loading this chunk wires the skeleton feature bit into the bundle (folds the renderable's
+// feature-OR branch in scenes that never enable skinning). See standard-renderable._stdExtBits.
+_installStdExtFeature(HAS_SKELETON);
diff --git a/packages/babylon-lite/src/material/standard/fragments/std-vertex-color-fragment.ts b/packages/babylon-lite/src/material/standard/fragments/std-vertex-color-fragment.ts
new file mode 100644
index 0000000000..d8e2cd265f
--- /dev/null
+++ b/packages/babylon-lite/src/material/standard/fragments/std-vertex-color-fragment.ts
@@ -0,0 +1,44 @@
+/** Standard Vertex-Color Fragment — multiplies baseColor by the per-vertex color before lighting,
+ * matching BJS StandardMaterial. The `color` attribute is float32x4 RGBA (stride 16) — the
+ * engine-wide vertex-color layout (glTF/PBR, procedural meshes, node materials, and the FBX loader
+ * all emit RGBA) — and the rgb is used to modulate base color (vertex alpha is forced to 1.0). */
+
+import type { ShaderFragment } from "../../../shader/fragment-types.js";
+import type { StdExt } from "../standard-flags.js";
+import { HAS_VERTEX_COLOR } from "../standard-flags.js";
+import { _installStdExtFeature } from "../std-feature-hooks.js";
+
+export function createStdVertexColorFragment(): ShaderFragment {
+ return {
+ _id: "std-vcolor",
+ _vertexAttributes: [{ _name: "color", _type: "vec4", _gpuFormat: "float32x4", _arrayStride: 16 }],
+ _varyings: [{ _name: "vColor", _type: "vec3" }],
+ _vertexSlots: { VB: `out.vColor = color.rgb;` },
+ // Backtick (not double-quote) so the bundle's WGSL identifier mangler rewrites
+ // `baseColor` here to match the mangled declaration in the standard template.
+ _fragmentSlots: { AT: `\nbaseColor = baseColor * input.vColor;` },
+ };
+}
+
+/** Registry extension gated on `HAS_VERTEX_COLOR`. Vertex color is a vertex attribute (not a
+ * UBO/texture), so there is no group-1 binding — the `_bind`/`_textures` hooks are omitted and
+ * the shared StdExt bind/texture loops skip it. It DOES contribute a draw-time vertex buffer,
+ * bound generically via `_bindVertexBuffers` (mirrors the layout the composer emits for the
+ * `color` attribute). */
+export const stdVertexColorExt: StdExt = {
+ _id: "std-vcolor",
+ _phase: "mesh",
+ _feature: HAS_VERTEX_COLOR,
+ _frag: () => createStdVertexColorFragment(),
+ _bindVertexBuffers(mesh, pass, slot) {
+ const g = mesh._gpu;
+ if (g.colorBuffer) {
+ pass.setVertexBuffer(slot++, g.colorBuffer, g._vbLayout?._c?._offset);
+ }
+ return slot;
+ },
+};
+
+// Loading this chunk wires the vertex-color feature bit into the bundle (folds the renderable's
+// feature-OR branch in scenes that never enable vertex color). See standard-renderable._stdExtBits.
+_installStdExtFeature(HAS_VERTEX_COLOR);
diff --git a/packages/babylon-lite/src/material/standard/standard-flags.ts b/packages/babylon-lite/src/material/standard/standard-flags.ts
index 4e6793b084..92231dc5f8 100644
--- a/packages/babylon-lite/src/material/standard/standard-flags.ts
+++ b/packages/babylon-lite/src/material/standard/standard-flags.ts
@@ -1,5 +1,6 @@
import type { ShaderFragment } from "../../shader/fragment-types.js";
import type { Texture2D } from "../../texture/texture-2d.js";
+import type { Mesh } from "../../mesh/mesh.js";
import type { StandardMaterialProps } from "./standard-material.js";
// ─── Feature Flags ──────────────────────────────────────────────────
@@ -29,6 +30,12 @@ export const GEOMETRY_OUTPUT = 1 << 21;
export const LIGHTMAP_SHADOWMAP = 1 << 15;
/** Lightmap UVs are V-flipped (BJS Texture.uAng === π → uv'=(u, 1-v)). */
export const LIGHTMAP_FLIP_V = 1 << 22;
+/** Mesh carries per-vertex color (RGB modulates baseColor). Opt-in via `enableStandardVertexColor`. */
+export const HAS_VERTEX_COLOR = 1 << 23;
+/** Mesh is skinned (bone-texture + joints/weights). Opt-in via `enableStandardSkeleton`. */
+export const HAS_SKELETON = 1 << 26;
+/** Skinned mesh uses 8 bones/vertex (joints1/weights1). */
+export const HAS_SKELETON_8 = 1 << 27;
// ─── Standard Material Extension Registry ───────────────────────────
@@ -46,8 +53,13 @@ export interface StdExt {
readonly _feature: number;
/** @internal */
_frag(features: number, shadowLights?: ShadowLightSlotLite[]): ShaderFragment;
- /** @internal Push group-1 bind entries starting at binding `b`; return new b. */
- _bind?(mat: StandardMaterialProps, entries: GPUBindGroupEntry[], b: number): number;
+ /** @internal Push group-1 bind entries starting at binding `b`; return new b. `mesh` is
+ * forwarded for mesh-driven resources (e.g. the skeleton bone texture); texture exts ignore it. */
+ _bind?(mat: StandardMaterialProps, entries: GPUBindGroupEntry[], b: number, mesh?: Mesh): number;
+ /** @internal Bind draw-time vertex buffers for this feature (e.g. joints/weights, vertex color)
+ * starting at vertex-buffer `slot`; return the next slot. Called only for mesh-phase deform/vertex
+ * exts whose feature bit is active. */
+ _bindVertexBuffers?(mesh: Mesh, pass: GPURenderPassEncoder | GPURenderBundleEncoder, slot: number): number;
/** @internal Enumerate textures for acquire/release. */
_textures?(mat: StandardMaterialProps, out: Texture2D[]): void;
}
diff --git a/packages/babylon-lite/src/material/standard/standard-geometry-renderable.ts b/packages/babylon-lite/src/material/standard/standard-geometry-renderable.ts
index 29bdc47f50..29d1c252ec 100644
--- a/packages/babylon-lite/src/material/standard/standard-geometry-renderable.ts
+++ b/packages/babylon-lite/src/material/standard/standard-geometry-renderable.ts
@@ -42,7 +42,7 @@ import { syncThinInstanceBuffers } from "../../mesh/thin-instance-gpu.js";
import type { Material } from "../material.js";
import type { StandardMaterialProps } from "./standard-material.js";
import { _getStdExtsSorted, DOUBLE_SIDED, HAS_DIFFUSE_TEXTURE, HAS_OPACITY_TEXTURE, NEEDS_UV, NEEDS_UV2 } from "./standard-flags.js";
-import { writeStdMaterialData } from "./standard-pipeline.js";
+import { writeStdMaterialData, _isStandardUvOffsetEnabled } from "./standard-pipeline.js";
import { composeStandardGeometryShader } from "./standard-geometry-output-shader.js";
import { getSceneBindGroupLayout } from "../../render/scene-helpers.js";
import { collectStdBoundTextures } from "./collect-std-bound-textures.js";
@@ -298,12 +298,18 @@ function _ensureViewResources(view: StandardGeometryMaterialView, engine: Engine
const uvData = new F32(4);
let scaleX = 1;
let scaleY = 1;
+ let offsetX = 0;
let offsetY = 0;
if ((features & HAS_DIFFUSE_TEXTURE) !== 0 && source.diffuseTexture) {
scaleX = source.uvScale[0];
scaleY = source.uvScale[1];
+ // UV offset folds to 0 unless a loader/scene opted in via enableStandardUvOffset.
+ if (_isStandardUvOffsetEnabled()) {
+ offsetX = source.uvOffset![0];
+ offsetY = source.uvOffset![1];
+ }
if (source.diffuseTexture.invertY) {
- offsetY = scaleY;
+ offsetY += scaleY;
scaleY = -scaleY;
}
} else if ((features & HAS_OPACITY_TEXTURE) !== 0 && source.opacityTexture?.invertY) {
@@ -315,7 +321,7 @@ function _ensureViewResources(view: StandardGeometryMaterialView, engine: Engine
}
uvData[0] = scaleX;
uvData[1] = scaleY;
- uvData[2] = 0;
+ uvData[2] = offsetX;
uvData[3] = offsetY;
upUBO = createUniformBuffer(engine, uvData);
}
@@ -339,13 +345,7 @@ function _ensureViewResources(view: StandardGeometryMaterialView, engine: Engine
return res;
}
-function _createGeometryMeshBindGroup(
- engine: EngineContext,
- view: StandardGeometryMaterialView,
- res: StandardGeometryViewResources,
- _mesh: Mesh,
- meshUBO: GPUBuffer
-): GPUBindGroup {
+function _createGeometryMeshBindGroup(engine: EngineContext, view: StandardGeometryMaterialView, res: StandardGeometryViewResources, mesh: Mesh, meshUBO: GPUBuffer): GPUBindGroup {
const source = view.source as StandardMaterialProps;
const features = res._features;
let nextBinding = 0;
@@ -362,7 +362,9 @@ function _createGeometryMeshBindGroup(
}
for (const used of res._extFragments) {
if (used._ext._bind) {
- nextBinding = used._ext._bind(source, entries, nextBinding);
+ // Geometry-output features never OR in the skeleton/vcolor exts (those are color-pass
+ // only), so `mesh` is forwarded only for signature parity; texture exts ignore it.
+ nextBinding = used._ext._bind(source, entries, nextBinding, mesh);
}
}
// Geometry-params `gp` UBO is contributed by the geometry composer as the
diff --git a/packages/babylon-lite/src/material/standard/standard-group-builder.ts b/packages/babylon-lite/src/material/standard/standard-group-builder.ts
index 7ee5fcc0e2..f0d261d173 100644
--- a/packages/babylon-lite/src/material/standard/standard-group-builder.ts
+++ b/packages/babylon-lite/src/material/standard/standard-group-builder.ts
@@ -1,7 +1,23 @@
import type { EngineContext } from "../../engine/engine.js";
import type { MeshGroupBuilder } from "../../render/renderable.js";
import { _registerStdExt } from "./standard-flags.js";
+import type { StdExt } from "./standard-flags.js";
import type { StandardMaterialProps } from "./standard-material.js";
+import type { Mesh } from "../../mesh/mesh.js";
+
+// Mesh-feature → StdExt dispatch registry (resolver-hook fold). Each deform/vertex feature
+// (skeleton, vertex color) is installed here ONLY by its `enableStandard*()` opt-in
+// (enable-standard-mesh-features.ts), which loaders/scenes call when they create such a mesh. A
+// scene that never enables one keeps this `null`, so the dispatch loop below folds entirely out of
+// the bundle — the same module-local-proven-null fold the stencil path uses — and non-deform
+// Standard scenes (fog/skybox, Sponza, …) stay byte-identical to upstream.
+export type StdMeshExtDispatch = readonly [(m: Mesh) => boolean, () => Promise, string | null];
+let _stdMeshExtDispatch: StdMeshExtDispatch[] | null = null;
+/** @internal Install a mesh-feature StdExt dispatcher (called only by `enableStandard*` opt-ins).
+ * `key` names the StdExt export to register, or is `null` for chunks that self-install. */
+export function _registerStdMeshExtDispatch(d: StdMeshExtDispatch): void {
+ (_stdMeshExtDispatch ??= []).push(d);
+}
/** Lazy-imports the standard renderable builder and builds the pipeline. */
// Material-property → fragment-module dispatch table. Each entry is a plain
@@ -39,6 +55,10 @@ export function getStandardGroupBuilder(): MeshGroupBuilder {
let shadowFragment: any;
let morphFragment: any;
let cull: typeof import("../../mesh/thin-instance-cull-binding.js") | undefined;
+ // Fog WGSL is dynamic-imported only when the scene has fog, so non-fog Standard scenes
+ // bundle zero fog bytes (a static import would defeat tree-shaking — see std-fog-wgsl.ts).
+ let fogHelper = "";
+ let fogBlock = "";
const imports: Promise[] = [];
if (hasTI) {
@@ -74,6 +94,31 @@ export function getStandardGroupBuilder(): MeshGroupBuilder {
})
);
}
+ if (scene.fog) {
+ imports.push(
+ import("./std-fog-wgsl.js").then((m) => {
+ fogHelper = m.STD_FOG_HELPER;
+ fogBlock = m.STD_FOG_BLOCK;
+ })
+ );
+ }
+ // Deform/vertex mesh-feature StdExts (skeleton, vertex color) are dispatched from the
+ // module-local `_stdMeshExtDispatch` registry, populated only by the `enableStandard*()`
+ // opt-ins loaders/scenes call. When no feature was enabled the registry is `null` and this
+ // whole block folds away, so non-deform Standard scenes stay byte-identical.
+ if (_stdMeshExtDispatch) {
+ for (const [pred, load, key] of _stdMeshExtDispatch) {
+ if (meshes.some(pred)) {
+ imports.push(
+ load().then((mod) => {
+ if (key) {
+ _registerStdExt((mod as Record)[key]!);
+ }
+ })
+ );
+ }
+ }
+ }
for (const [prop, load, key] of _STD_MAT_EXTS) {
if (meshes.some((m) => !!(m.material as any)[prop])) {
imports.push(load().then((mod) => _registerStdExt(mod[key])));
@@ -84,7 +129,7 @@ export function getStandardGroupBuilder(): MeshGroupBuilder {
}
const renderableMod = await import("./standard-renderable.js");
- const result = renderableMod.buildStandardMeshRenderables(scene, meshes, { tiSync, tiFragment, shadowFragment, morphFragment, cull });
+ const result = renderableMod.buildStandardMeshRenderables(scene, meshes, { tiSync, tiFragment, shadowFragment, morphFragment, cull, fogHelper, fogBlock });
// Wire the per-mesh rebuild closure used by material swap + per-pass override.
builder._rebuildSingle = result.rebuildSingle;
return result;
diff --git a/packages/babylon-lite/src/material/standard/standard-material.ts b/packages/babylon-lite/src/material/standard/standard-material.ts
index 00a84f0ff0..284fb56a10 100644
--- a/packages/babylon-lite/src/material/standard/standard-material.ts
+++ b/packages/babylon-lite/src/material/standard/standard-material.ts
@@ -97,6 +97,11 @@ export interface StandardMaterialProps extends Material {
reflectionCoordMode: 1 | 2;
/** UV tiling scale. Default [1, 1]. */
uvScale: [number, number];
+ /** UV offset/translation applied after scale (`uv*scale + offset`). Optional; absent (the
+ * default) behaves as [0, 0] and folds the offset reads out of the bundle. Set only by loaders
+ * or scenes that need it (the FBX loader, from `uvTranslation`, which also calls
+ * `enableStandardUvOffset`). Matches BJS `Texture.uOffset`/`vOffset`. */
+ uvOffset?: [number, number];
/** Back-face culling. Default true (BJS convention). False = double-sided. */
backFaceCulling: boolean;
/** When true, skip all lighting and output emissive * diffuse * baseColor. Default false. */
diff --git a/packages/babylon-lite/src/material/standard/standard-pipeline.ts b/packages/babylon-lite/src/material/standard/standard-pipeline.ts
index 5c76d2fe86..35d55ac5b6 100644
--- a/packages/babylon-lite/src/material/standard/standard-pipeline.ts
+++ b/packages/babylon-lite/src/material/standard/standard-pipeline.ts
@@ -14,6 +14,7 @@ import { F32 } from "../../engine/typed-arrays.js";
import type { EngineContext } from "../../engine/engine.js";
import type { RenderTargetSignature } from "../../engine/render-target.js";
import type { StandardMaterialProps } from "./standard-material.js";
+import type { Mesh } from "../../mesh/mesh.js";
import type { ResolvedStencil } from "../stencil-state.js";
import type { StencilState } from "../material.js";
import { _standardFeatureKey } from "./standard-material.js";
@@ -47,6 +48,19 @@ export function _installStandardStencilResolver(resolve: (stencil: StencilState)
_stencilResolver = resolve;
}
+/** UV-offset fold flag, set only by `enableStandardUvOffset`. Module-local with a single exported
+ * setter: when no scene opts in the setter tree-shakes, the bundler proves this is always `false`,
+ * and the `material.uvOffset` reads below fold to a constant 0 — non-offset scenes stay byte-identical. */
+let _uvOffsetEnabled = false;
+/** @internal Enable Standard UV offset reads (called by `enableStandardUvOffset`). */
+export function _installStandardUvOffset(): void {
+ _uvOffsetEnabled = true;
+}
+/** @internal Whether `material.uvOffset` is honored (read by the geometry-output UV path). */
+export function _isStandardUvOffsetEnabled(): boolean {
+ return _uvOffsetEnabled;
+}
+
// ─── Composer Path (Phase 1) ────────────────────────────────────────
// Converts feature bitmask → StandardTemplateConfig → ComposedShader.
// This produces identical WGSL to the old string-builder path but via
@@ -54,7 +68,14 @@ export function _installStandardStencilResolver(resolve: (stencil: StencilState)
/** Compose Standard shader via the generic ShaderComposer.
* @param fragments - Optional extra fragments (e.g. thin-instance). */
-export function composeStandardShader(features: number, _meshFeatures = 0, fragments: ShaderFragment[] = [], esmShadowDepthCode = ""): ComposedShader {
+export function composeStandardShader(
+ features: number,
+ _meshFeatures = 0,
+ fragments: ShaderFragment[] = [],
+ esmShadowDepthCode = "",
+ fogHelper = "",
+ fogBlock = ""
+): ComposedShader {
const has = (bit: number) => (features & bit) !== 0;
const pc = fragments[0]?._pc;
const template = createStandardTemplate(
@@ -67,6 +88,8 @@ export function composeStandardShader(features: number, _meshFeatures = 0, fragm
_noColorOutput: has(NO_COLOR_OUTPUT),
_esmShadowOutput: has(ESM_SHADOW_OUTPUT),
_hasMorph: !!pc,
+ _fogHelper: fogHelper,
+ _fogBlock: fogBlock,
},
esmShadowDepthCode
);
@@ -140,6 +163,8 @@ export function getOrCreateStandardBindings(
fragments: ShaderFragment[] = [],
shaderKey = "",
esmShadowDepthCode = "",
+ fogHelper = "",
+ fogBlock = "",
stencil: StencilState | null = null
): StandardShaderBindings {
ensureDevice(engine);
@@ -156,7 +181,7 @@ export function getOrCreateStandardBindings(
const cc = getComposedCache();
let composed = cc.get(key);
if (!composed) {
- composed = composeStandardShader(features, meshFeatures, fragments, esmShadowDepthCode);
+ composed = composeStandardShader(features, meshFeatures, fragments, esmShadowDepthCode, fogHelper, fogBlock);
cc.set(key, composed);
}
@@ -257,7 +282,8 @@ export function createStandardMeshBindGroup(
meshUBO: GPUBuffer,
materialUBO: GPUBuffer,
material: StandardMaterialProps,
- morphTargets: { deltasBuffer: GPUBuffer; weightsBuffer: GPUBuffer } | null = null
+ morphTargets: { deltasBuffer: GPUBuffer; weightsBuffer: GPUBuffer } | null = null,
+ mesh?: Mesh
): GPUBindGroup {
const device = engine._device;
const features = bindings._features;
@@ -288,16 +314,17 @@ export function createStandardMeshBindGroup(
const uvData = new F32(4);
const scaleX = material.uvScale[0];
let scaleY = material.uvScale[1];
- let offsetY = 0;
+ // UV offset folds to a constant 0 unless a loader/scene opted in via enableStandardUvOffset.
+ let offsetY = _uvOffsetEnabled ? material.uvOffset![1] : 0;
// Flip V for y-down source data (e.g. basis/compressed textures).
// uv * (sx, sy) + (ox, oy) with vFlip becomes uv.xy * (sx, -sy) + (ox, sy+oy).
if (material.diffuseTexture?.invertY) {
- offsetY = scaleY;
+ offsetY += scaleY;
scaleY = -scaleY;
}
uvData[0] = scaleX;
uvData[1] = scaleY;
- uvData[2] = 0;
+ uvData[2] = _uvOffsetEnabled ? material.uvOffset![0] : 0;
uvData[3] = offsetY;
entries.push({ binding: nextBinding++, resource: { buffer: createUniformBuffer(engine, uvData) } });
}
@@ -314,7 +341,7 @@ export function createStandardMeshBindGroup(
const sortedExts = _getStdExtsSorted();
for (const ext of sortedExts) {
if (features & ext._feature && ext._bind) {
- nextBinding = ext._bind(material, entries, nextBinding);
+ nextBinding = ext._bind(material, entries, nextBinding, mesh);
}
}
diff --git a/packages/babylon-lite/src/material/standard/standard-renderable.ts b/packages/babylon-lite/src/material/standard/standard-renderable.ts
index 8888f84a2f..4117b6b4f2 100644
--- a/packages/babylon-lite/src/material/standard/standard-renderable.ts
+++ b/packages/babylon-lite/src/material/standard/standard-renderable.ts
@@ -15,12 +15,33 @@ import { _computeStandardMaterialFeatures, _standardShaderVariantKey } from "./s
import { acquireTexture, releaseTexture, clearSamplerCache } from "../../resource/gpu-pool.js";
import { createUniformBuffer } from "../../resource/gpu-buffers.js";
import { getOrCreateStandardBindings, getOrCreateStandardPipeline, createStandardMeshBindGroup, clearStandardPipelineCache, writeStdMaterialData } from "./standard-pipeline.js";
-import { ESM_SHADOW_OUTPUT, NO_COLOR_OUTPUT, NEEDS_UV, NEEDS_UV2, HAS_OPACITY_TEXTURE, _getStdExts } from "./standard-flags.js";
+import {
+ ESM_SHADOW_OUTPUT,
+ NO_COLOR_OUTPUT,
+ NEEDS_UV,
+ NEEDS_UV2,
+ HAS_OPACITY_TEXTURE,
+ HAS_VERTEX_COLOR,
+ HAS_SKELETON,
+ HAS_SKELETON_8,
+ _getStdExtsSorted,
+} from "./standard-flags.js";
+import type { StdExt } from "./standard-flags.js";
+import { _stdExtBits } from "./std-feature-hooks.js";
import type { ShaderFragment } from "../../shader/fragment-types.js";
import type { ShadowGenerator } from "../../shadow/shadow-generator.js";
import { writeMeshLightSelection } from "../../render/lights-ubo.js";
import type { Material, MaterialRenderFeatures } from "../material.js";
-import { _computeMeshFeatures, MSH_HAS_INSTANCE_COLOR, MSH_HAS_MORPH_TARGETS, MSH_HAS_THIN_INSTANCES, MSH_RECEIVE_SHADOWS } from "../mesh-features.js";
+import {
+ _computeMeshFeatures,
+ MSH_HAS_INSTANCE_COLOR,
+ MSH_HAS_MORPH_TARGETS,
+ MSH_HAS_THIN_INSTANCES,
+ MSH_HAS_SKELETON,
+ MSH_HAS_SKELETON_8,
+ MSH_HAS_VERTEX_COLOR,
+ MSH_RECEIVE_SHADOWS,
+} from "../mesh-features.js";
import { packMat4IntoF32 } from "../../math/pack-mat4-into-f32.js";
/** Scratch buffer for material UBO writes (24 floats = 96 bytes). Reused across
@@ -46,6 +67,11 @@ export interface StdFragmentFactories {
morphFragment?: () => ShaderFragment;
/** Present only when the scene has at least one culling-enabled thin-instance mesh. */
cull?: typeof import("../../mesh/thin-instance-cull-binding.js");
+ /** `calcFogFactor` helper WGSL — present (non-empty) only when the scene has fog; threaded
+ * from the dynamic `std-fog-wgsl` import so non-fog scenes bundle zero fog bytes. */
+ fogHelper?: string;
+ /** Fog blend block WGSL — present alongside `fogHelper`. */
+ fogBlock?: string;
}
/** Build Renderable(s) + a SceneUniformUpdater for a set of standard meshes.
@@ -54,7 +80,7 @@ export interface StdFragmentFactories {
export function buildStandardMeshRenderables(scene: SceneContext, meshes: Mesh[], factories: StdFragmentFactories): MeshGroupBuildResult {
const engine = scene.surface.engine;
const device = engine._device;
- const { tiSync, tiFragment, shadowFragment, cull, morphFragment } = factories;
+ const { tiSync, tiFragment, shadowFragment, cull, morphFragment, fogHelper = "", fogBlock = "" } = factories;
// Collect per-light shadow info.
const shadowLights: { lightIndex: number; shadowType: "esm" | "pcf" | "csm"; gen: ShadowGenerator }[] = [];
@@ -75,10 +101,24 @@ export function buildStandardMeshRenderables(scene: SceneContext, meshes: Mesh[]
const mat = (materialOverride ?? mesh.material) as StandardMaterialProps;
const renderFeatures = (mat._renderFeatures ??= { features: _computeStandardMaterialFeatures(mat) }) as MaterialRenderFeatures;
const isOverride = materialOverride != null;
- const features = renderFeatures.features;
+ let features = renderFeatures.features;
const shadowOutput = (features & (NO_COLOR_OUTPUT | ESM_SHADOW_OUTPUT)) !== 0;
const receiveShadows = !shadowOutput && mesh.receiveShadows && hasSomeShadows;
const meshFeatures = _computeMeshFeatures(mesh, receiveShadows);
+ // OR in the opt-in deform/vertex feature bits driven by mesh attributes. Gated on
+ // `_stdExtBits` so a scene that enabled no Standard deform/vertex feature folds this whole
+ // block (and the downstream vertex-buffer binders) out of the bundle — byte-neutral.
+ if (_stdExtBits) {
+ if (_stdExtBits & HAS_VERTEX_COLOR && meshFeatures & MSH_HAS_VERTEX_COLOR) {
+ features |= HAS_VERTEX_COLOR;
+ }
+ if (_stdExtBits & HAS_SKELETON && meshFeatures & MSH_HAS_SKELETON && !(meshFeatures & MSH_HAS_THIN_INSTANCES)) {
+ features |= HAS_SKELETON;
+ if (meshFeatures & MSH_HAS_SKELETON_8) {
+ features |= HAS_SKELETON_8;
+ }
+ }
+ }
// Build per-feature fragment list (deduped via pipeline cache).
const frags: ShaderFragment[] = [];
// Keep morph first: composeStandardShader uses the first fragment's patch
@@ -86,12 +126,18 @@ export function buildStandardMeshRenderables(scene: SceneContext, meshes: Mesh[]
if (meshFeatures & MSH_HAS_MORPH_TARGETS && morphFragment) {
frags.push(morphFragment());
}
- for (const ext of _getStdExts().values()) {
+ // Vertex-buffer binders contributed by active deform/vertex exts (skeleton joints/weights,
+ // vertex color). Collected here, replayed in the draw closure. Folds with `_stdExtBits`.
+ const vbBinders: NonNullable[] = [];
+ for (const ext of _getStdExtsSorted()) {
if (features & ext._feature) {
const f = ext._frag(features);
if (f) {
frags.push(f);
}
+ if (_stdExtBits && ext._bindVertexBuffers) {
+ vbBinders.push(ext._bindVertexBuffers);
+ }
}
}
let shaderKey = "";
@@ -117,7 +163,17 @@ export function buildStandardMeshRenderables(scene: SceneContext, meshes: Mesh[]
}
}
const esmShadowDepthCode = (features & ESM_SHADOW_OUTPUT) !== 0 ? (mat as StandardMaterialProps & { readonly _esmShadowDepthCode: string })._esmShadowDepthCode : "";
- const bindings = getOrCreateStandardBindings(engine, features, meshFeatures, frags, shaderKey, esmShadowDepthCode, (mat as StandardMaterialProps).stencil ?? null);
+ const bindings = getOrCreateStandardBindings(
+ engine,
+ features,
+ meshFeatures,
+ frags,
+ shaderKey,
+ esmShadowDepthCode,
+ fogHelper,
+ fogBlock,
+ (mat as StandardMaterialProps).stencil ?? null
+ );
const meshShadowGens = receiveShadows ? shadowLights.map((sl) => sl.gen) : [];
@@ -130,7 +186,7 @@ export function buildStandardMeshRenderables(scene: SceneContext, meshes: Mesh[]
const matData = new F32(24);
writeStdMaterialData(matData, mat, textureLevel);
const materialUBO = createUniformBuffer(engine, matData);
- const meshBindGroup = createStandardMeshBindGroup(engine, bindings, meshUBO, materialUBO, mat, mesh.morphTargets ?? null);
+ const meshBindGroup = createStandardMeshBindGroup(engine, bindings, meshUBO, materialUBO, mat, mesh.morphTargets ?? null, mesh);
// Shadow bind group (group 2) — shared across receiving meshes via shadowBGCache.
let shadowBindGroup: GPUBindGroup | null = null;
@@ -219,6 +275,15 @@ export function buildStandardMeshRenderables(scene: SceneContext, meshes: Mesh[]
pass.setVertexBuffer(slot++, g.uv2Buffer, vb?._u2?._offset);
}
+ // Deform/vertex feature vertex buffers (skeleton joints/weights, vertex color), in the
+ // composer's attribute order (after base attrs, before thin-instance). Folds out when no
+ // Standard deform/vertex feature was enabled.
+ if (_stdExtBits) {
+ for (const bind of vbBinders) {
+ slot = bind(mesh, pass, slot);
+ }
+ }
+
const ti = hasThinInstances ? mesh.thinInstances : null;
if (ti && tiSync) {
slot = tiSync(engine, ti, pass, slot, hasInstanceColor, cullBinding?.cullDrawBufs);
diff --git a/packages/babylon-lite/src/material/standard/standard-template.ts b/packages/babylon-lite/src/material/standard/standard-template.ts
index c4082360ae..bd197f980c 100644
--- a/packages/babylon-lite/src/material/standard/standard-template.ts
+++ b/packages/babylon-lite/src/material/standard/standard-template.ts
@@ -11,7 +11,6 @@
*/
import type { ShaderTemplate, UboField, VertexAttribute, Varying, BindingDecl } from "../../shader/fragment-types.js";
-import { WGSL_FOG } from "../../shader/wgsl-helpers.js";
import { MAX_LIGHTS } from "../../light/types.js";
import { appendMeshLightUboFields, meshLightIndexWGSL } from "../../render/lights-ubo.js";
@@ -69,6 +68,11 @@ export interface StandardTemplateConfig {
readonly _esmShadowOutput?: boolean;
/** @internal Has morph targets — switches the vertex shader to morphedPos/morphedNorm (defined by the morph fragment's VR slot). */
readonly _hasMorph?: boolean;
+ /** @internal `calcFogFactor` helper WGSL, supplied (non-empty) only when the scene has fog;
+ * threaded in from `std-fog-wgsl` so non-fog scenes bundle zero fog bytes. "" otherwise. */
+ readonly _fogHelper?: string;
+ /** @internal Fog blend block WGSL, supplied alongside `_fogHelper`. "" for non-fog scenes. */
+ readonly _fogBlock?: string;
}
/**
@@ -76,7 +80,7 @@ export interface StandardTemplateConfig {
* The template contains slot markers that the composer fills.
*/
export function createStandardTemplate(config: StandardTemplateConfig, esmShadowDepthCode = ""): ShaderTemplate {
- const { _diffuse, _needsUV, _needsUV2, _diffuseUsesUV2, _disableLighting, _noColorOutput, _esmShadowOutput, _hasMorph } = config;
+ const { _diffuse, _needsUV, _needsUV2, _diffuseUsesUV2, _disableLighting, _noColorOutput, _esmShadowOutput, _hasMorph, _fogHelper = "", _fogBlock = "" } = config;
// ── Base vertex attributes ──────────────────────────────────
const _baseVertexAttributes: VertexAttribute[] = [
@@ -201,7 +205,7 @@ _1: f32,
};
`;
- const helpers = _disableLighting ? WGSL_FOG : WGSL_FOG + LIGHTING_FN;
+ const helpers = _disableLighting ? _fogHelper : _fogHelper + LIGHTING_FN;
// reflection, shadow, bump helpers are provided by their respective fragments
// Main fragment body — mirrors old composeFragmentShader exactly
@@ -281,10 +285,7 @@ ${_noColorOutput ? "return;" : _esmShadowOutput ? esmShadowDepthCode : ""}
${lightingBlock}
/*BC*/
color = vec4(max(color.rgb, vec3(0.0)), color.a);
-if (scene.vFogInfos.x > 0.0) {
-let fog = calcFogFactor(input.vf);
-color = vec4(mix(scene.vFogColor.rgb, color.rgb, fog), color.a);
-}
+${_fogBlock}
/*BA*/
${_noColorOutput ? "" : "return color;"}
}`;
diff --git a/packages/babylon-lite/src/material/standard/std-feature-hooks.ts b/packages/babylon-lite/src/material/standard/std-feature-hooks.ts
new file mode 100644
index 0000000000..d27f55005d
--- /dev/null
+++ b/packages/babylon-lite/src/material/standard/std-feature-hooks.ts
@@ -0,0 +1,34 @@
+/**
+ * Internal fold-state for the Standard material's deform/vertex + explicit-tangent features.
+ *
+ * This module holds the module-local state and its setters and is **only ever named-imported**
+ * (never namespace-imported / re-exported through a dynamically `await import()`-ed chunk). That is
+ * the whole point: `standard-renderable.ts` and `normal-map-fragment.ts` are loaded via dynamic
+ * namespace imports (`await import("./standard-renderable.js")`, `mod.bumpStdExt`), so Rollup must
+ * retain ALL of their exports. If the setters below lived there, terser could never prove the state
+ * stays at its initializer, and every gated branch would ship into scenes that don't use the feature.
+ *
+ * By keeping the setters here — imported by name into `standard-renderable`/`normal-map-fragment`
+ * for READING the state, and imported by name into the dynamic feature chunks for WRITING it — a
+ * scene that loads no feature chunk has no reachable caller of a setter. Terser then proves the
+ * state constant and folds the feature-OR, the vertex-buffer binder loop, and the tangent path out
+ * entirely (true byte-neutrality), exactly like the stencil resolver hook.
+ */
+
+import type { ShaderFragment } from "../../shader/fragment-types.js";
+
+/** Active deform/vertex StdExt feature bits. Each dynamic feature chunk (std-vertex-color / morph /
+ * skeleton / normal-tangent fragment) ORs its `HAS_*` bit in via `_installStdExtFeature` on import. */
+export let _stdExtBits = 0;
+/** @internal OR a deform/vertex feature bit into the active set (called by feature chunks on load). */
+export function _installStdExtFeature(bit: number): void {
+ _stdExtBits |= bit;
+}
+
+/** Explicit-tangent normal-map fragment factory, injected by `std-normal-tangent-fragment.ts` on
+ * import. Stays `null` (and every tangent branch folds) until that chunk loads. */
+export let _tangentFrag: ((features: number) => ShaderFragment) | null = null;
+/** @internal Install the explicit-tangent normal-map fragment factory (called by the tangent chunk). */
+export function _installNormalTangentFrag(frag: (features: number) => ShaderFragment): void {
+ _tangentFrag = frag;
+}
diff --git a/packages/babylon-lite/src/material/standard/std-fog-wgsl.ts b/packages/babylon-lite/src/material/standard/std-fog-wgsl.ts
new file mode 100644
index 0000000000..1f258cc0ec
--- /dev/null
+++ b/packages/babylon-lite/src/material/standard/std-fog-wgsl.ts
@@ -0,0 +1,23 @@
+/**
+ * Standard fog receiver WGSL — the `calcFogFactor` helper plus the fog blend block.
+ *
+ * Dynamically imported by `standard-group-builder` ONLY when `scene.fog` is set, then threaded into
+ * the Standard template as plain strings (mirrors the PBR fog path in `pbr-fog-wgsl.ts`). This keeps
+ * every byte of fog WGSL out of the bundles of Standard scenes that don't use fog — a static `import`
+ * of the helper into `standard-template` would defeat tree-shaking and inflate every Standard scene
+ * (see GUIDANCE §4c′). This is the ONLY module that statically holds the fog WGSL for Standard.
+ *
+ * Parity notes (matches Babylon.js `default.fragment` exactly):
+ * - Standard mixes fog into the final LDR colour at the end of the fragment (after the lit colour is
+ * clamped to non-negative), with the raw (non-linearised) `vFogColor` — no gamma round-trip, unlike PBR.
+ * - The fog FACTOR is the linear/exp/exp2 distance falloff from `calcFogFactor` (no `pow(.., 2.2)`).
+ * - The runtime `vFogInfos.x > 0.0` guard lets `fogMode` toggle none/linear/exp/exp2 at runtime.
+ */
+
+import { WGSL_FOG } from "../../shader/wgsl-helpers.js";
+
+/** `calcFogFactor` + `E_FOG` helper WGSL (reads `scene.vFogInfos`). */
+export const STD_FOG_HELPER = WGSL_FOG;
+
+/** Fog blend block, emitted at the end of the Standard fragment (operates on the final LDR `color`). */
+export const STD_FOG_BLOCK = "if(scene.vFogInfos.x>0.0){let fog=calcFogFactor(input.vf);color=vec4(mix(scene.vFogColor.rgb,color.rgb,fog),color.a);}";
diff --git a/packages/babylon-lite/src/shader/fragments/skeleton-fragment.ts b/packages/babylon-lite/src/shader/fragments/skeleton-fragment.ts
new file mode 100644
index 0000000000..7cc7f02d60
--- /dev/null
+++ b/packages/babylon-lite/src/shader/fragments/skeleton-fragment.ts
@@ -0,0 +1,78 @@
+/**
+ * Skeleton Fragment (shared)
+ *
+ * Vertex-stage skeletal animation: bone texture sampling + skinning matrix.
+ * Material-agnostic — used by both the PBR and Standard material composers.
+ * Only bundled when a scene has skeletal animation. Supports 4-bone and 8-bone
+ * skinning.
+ *
+ * The bone texture is declared as a `_vertexBindings` entry (placed by the
+ * composer immediately after the mesh UBO, before base bindings) — exactly as
+ * the PBR path has always done. The Standard side reuses this same fragment and
+ * relocates the single bone binding to `_bindings` (afterBase) in its own
+ * wrapper, so no per-call branch lives in this shared module (keeps the PBR
+ * skeleton chunk byte-identical to before the move).
+ */
+
+import type { ShaderFragment, VertexAttribute } from "../fragment-types.js";
+
+// WebGPU shader stage constants
+const STAGE_VERTEX = 0x1;
+
+const SKELETON_HELPERS = `
+fn readMatrixFromRawSampler(smp: texture_2d, index: f32) -> mat4x4 {
+let offset = i32(index) * 4;
+let m0 = textureLoad(smp, vec2(offset + 0, 0), 0);
+let m1 = textureLoad(smp, vec2(offset + 1, 0), 0);
+let m2 = textureLoad(smp, vec2(offset + 2, 0), 0);
+let m3 = textureLoad(smp, vec2(offset + 3, 0), 0);
+return mat4x4f(m0, m1, m2, m3);
+}
+`;
+
+function makeSkinningCode(has8Bones: boolean): string {
+ let code = `var influence: mat4x4 = readMatrixFromRawSampler(boneSampler, f32(joints[0])) * weights[0];
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints[1])) * weights[1];
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints[2])) * weights[2];
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints[3])) * weights[3];`;
+ if (has8Bones) {
+ code += `
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[0])) * weights1[0];
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[1])) * weights1[1];
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[2])) * weights1[2];
+influence = influence + readMatrixFromRawSampler(boneSampler, f32(joints1[3])) * weights1[3];`;
+ }
+ code += `\nfinalWorld = mesh.world * influence;`;
+ return code;
+}
+
+/**
+ * Create a skeleton fragment.
+ * @param has8Bones - Whether to use 8-bone skinning (joints1/weights1).
+ */
+export function createSkeletonFragment(has8Bones: boolean): ShaderFragment {
+ return {
+ _id: "skeleton",
+
+ _vertexAttributes: [
+ { _name: "joints", _type: "vec4", _gpuFormat: "uint32x4" as GPUVertexFormat, _arrayStride: 16 },
+ { _name: "weights", _type: "vec4", _gpuFormat: "float32x4" as GPUVertexFormat, _arrayStride: 16 },
+ ...(has8Bones
+ ? [
+ { _name: "joints1", _type: "vec4", _gpuFormat: "uint32x4" as GPUVertexFormat, _arrayStride: 16 },
+ { _name: "weights1", _type: "vec4", _gpuFormat: "float32x4" as GPUVertexFormat, _arrayStride: 16 },
+ ]
+ : []),
+ ] as VertexAttribute[],
+
+ _vertexBindings: [
+ { _name: "boneSampler", _type: { _kind: "texture", _textureType: "texture_2d" as const, _sampleType: "unfilterable-float" as const }, _visibility: STAGE_VERTEX },
+ ],
+
+ _vertexHelperFunctions: SKELETON_HELPERS,
+
+ _vertexSlots: {
+ VW: makeSkinningCode(has8Bones),
+ },
+ };
+}
diff --git a/scene-config.json b/scene-config.json
index 0c403fc22c..88be8b622f 100644
--- a/scene-config.json
+++ b/scene-config.json
@@ -27,7 +27,7 @@
"slug": "scene3-fog",
"name": "Scene 3 — Fog + Skybox",
"maxMad": 0.01,
- "maxRawKB": 54,
+ "maxRawKB": 54.5,
"description": "Exponential² fog, multiple colored boxes, skybox, ground plane.",
"tags": ["fog", "procedural", "std"],
"skipPerf": true
@@ -2007,6 +2007,15 @@
"tags": ["pbr", "gltf", "non-indexed"],
"skipPerf": true
},
+ {
+ "id": 231,
+ "slug": "scene231-standard-deform-features",
+ "name": "Scene 231 — StandardMaterial Deform Features",
+ "maxMad": 0.1,
+ "description": "In-code (no glTF) beam exercising StandardMaterial per-vertex color + skeletal skinning + UV offset, with programmatically animated bones frozen at a deterministic frame. Validates the opt-in Standard deform features against Babylon.js.",
+ "tags": ["std", "procedural", "skeleton", "vertex-color", "uv-offset", "animated"],
+ "skipPerf": true
+ },
{
"id": 240,
"slug": "scene240-animated-triangle",
diff --git a/tests/lite/parity/scenes/scene231-standard-deform-features.spec.ts b/tests/lite/parity/scenes/scene231-standard-deform-features.spec.ts
new file mode 100644
index 0000000000..1668587966
--- /dev/null
+++ b/tests/lite/parity/scenes/scene231-standard-deform-features.spec.ts
@@ -0,0 +1,31 @@
+/**
+ * Scene 231 — StandardMaterial deform features (per-vertex color + skeletal
+ * skinning + UV offset) on an in-code beam with programmatically animated bones.
+ */
+import { test, expect } from "@playwright/test";
+import * as path from "path";
+import { attachCompareArtifacts, captureGolden, compareImages, getSceneConfig } from "../compare-utils";
+
+const sceneConfig = getSceneConfig(231);
+const REFERENCE_DIR = path.resolve(__dirname, "../../../../reference/lite/scene231-standard-deform-features");
+const GOLDEN_REF = path.join(REFERENCE_DIR, "babylon-ref-golden.png");
+
+test.skip(!!sceneConfig.skipParity, "Scene 231 skipped via skipParity in scene-config.json");
+
+test("Scene 231 — StandardMaterial deform features match Babylon.js reference", async ({ page }, testInfo) => {
+ const browser = page.context().browser()!;
+ await captureGolden(browser, { sceneId: 231, seekTime: 0.5, timeout: 90_000 });
+
+ await page.goto("/scene231.html?seekTime=0.5");
+ await page.waitForFunction(() => document.querySelector("canvas")?.dataset.ready === "true", { timeout: 60_000 });
+ await page.waitForFunction(() => document.querySelector("canvas")?.dataset.animationFrozen === "true", { timeout: 60_000 });
+ await page.waitForTimeout(500);
+
+ const screenshotPath = path.join(REFERENCE_DIR, "test-actual.png");
+ await page.locator("canvas").screenshot({ path: screenshotPath });
+
+ const full = compareImages(screenshotPath, GOLDEN_REF);
+ await attachCompareArtifacts(testInfo, screenshotPath, GOLDEN_REF, REFERENCE_DIR);
+ console.log(`Full image MAD: ${full.mad.toFixed(3)} (limit ${sceneConfig.maxMad})`);
+ expect(full.mad, `Full image MAD should be <= ${sceneConfig.maxMad}`).toBeLessThanOrEqual(sceneConfig.maxMad);
+});