feat(web): finish RE 5000PBR lesson video and fix non-ASCII GLB import
- Add 11:07 Chinese-narrated lesson video, VTT, poster and 13 chapter screenshots. - Regenerate 20-render WebGL2 matrix and 15 operation captures on RTX 4060. - Fix WASM staging for non-ASCII import filenames (猴头.glb); rebuild web_engine. - Harden lesson video recording pipeline; fix stale lesson-link E2E selector. - Add RE PBR derived libraries, lesson page, evidence and status report. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -268,3 +268,68 @@ test("M10-07 keeps texture-fed Mix blocked with a visible issue", () => {
|
||||
assert.equal(report.status, "BLOCKED");
|
||||
assert.ok(report.issues.some((entry) => entry.message.includes("Mix color inputs")));
|
||||
});
|
||||
|
||||
test("RE 5000PBR graph collapses REROUTE, ignores FRAME, keeps DISPLACEMENT/HEIGHT and applies Value-driven Mapping", () => {
|
||||
const report = compiler.compileMaterialGraph(material({
|
||||
nodes: [
|
||||
{ id: "uv", type: "TEX_COORD", name: "UV" },
|
||||
{ id: "mapping", type: "MAPPING", name: "Mapping" },
|
||||
{ id: "reroute", type: "REROUTE", name: "Reroute" },
|
||||
{ id: "frame", type: "FRAME", name: "Frame" },
|
||||
{ id: "value", type: "VALUE", name: "Value", defaultValue: [2] },
|
||||
{ id: "base", type: "IMAGE_TEXTURE", name: "Base", imageId: "image:Base" },
|
||||
{ id: "rough", type: "IMAGE_TEXTURE", name: "Rough", imageId: "image:Rough" },
|
||||
{ id: "height", type: "IMAGE_TEXTURE", name: "Height", imageId: "image:Height" },
|
||||
{ id: "displacement", type: "DISPLACEMENT", name: "Displacement" },
|
||||
{ id: "principled", type: "PRINCIPLED", name: "Principled" },
|
||||
{ id: "output", type: "OUTPUT", name: "Output" },
|
||||
],
|
||||
links: [
|
||||
{ fromNodeId: "uv", fromSocket: "UV", toNodeId: "mapping", toSocket: "Vector" },
|
||||
{ fromNodeId: "value", fromSocket: "Value", toNodeId: "mapping", toSocket: "Scale" },
|
||||
{ fromNodeId: "mapping", fromSocket: "Vector", toNodeId: "reroute", toSocket: "Input" },
|
||||
{ fromNodeId: "reroute", fromSocket: "Output", toNodeId: "base", toSocket: "Vector" },
|
||||
{ fromNodeId: "reroute", fromSocket: "Output", toNodeId: "rough", toSocket: "Vector" },
|
||||
{ fromNodeId: "reroute", fromSocket: "Output", toNodeId: "height", toSocket: "Vector" },
|
||||
{ fromNodeId: "base", fromSocket: "Color", toNodeId: "principled", toSocket: "Base Color" },
|
||||
{ fromNodeId: "rough", fromSocket: "Color", toNodeId: "principled", toSocket: "Roughness" },
|
||||
{ fromNodeId: "height", fromSocket: "Color", toNodeId: "displacement", toSocket: "Height" },
|
||||
{ fromNodeId: "displacement", fromSocket: "Displacement", toNodeId: "output", toSocket: "Displacement" },
|
||||
{ fromNodeId: "principled", fromSocket: "BSDF", toNodeId: "output", toSocket: "Surface" },
|
||||
],
|
||||
}), { imageIds: new Set(["image:Base", "image:Rough", "image:Height"]) });
|
||||
assert.equal(report.status, "COMPILED");
|
||||
assert.ok(report.compiledNodeTypes.every((type) => !["REROUTE", "FRAME"].includes(type)));
|
||||
assert.ok(report.compiledNodeTypes.includes("DISPLACEMENT"));
|
||||
const base = report.textureBindings.find((binding) => binding.usage === "BASE_COLOR");
|
||||
assert.deepEqual(base.uvTransform?.scale, [2, 2]);
|
||||
assert.ok(report.textureBindings.some((binding) => binding.usage === "ROUGHNESS"));
|
||||
const height = report.textureBindings.find((binding) => binding.usage === "HEIGHT");
|
||||
assert.equal(height.imageId, "image:Height");
|
||||
assert.equal(height.scale, 0.01);
|
||||
assert.equal(height.midlevel, 0.5);
|
||||
assert.deepEqual(height.uvTransform?.scale, [2, 2]);
|
||||
assert.equal(report.material.displacementImageId, "image:Height");
|
||||
assert.equal(report.material.displacementScale, 0.01);
|
||||
assert.equal(report.material.displacementMidlevel, 0.5);
|
||||
assert.equal(report.material.roughness, 1);
|
||||
});
|
||||
|
||||
test("RE 5000PBR roughness/metallic can be read through Separate Color", () => {
|
||||
const report = compiler.compileMaterialGraph(material({
|
||||
nodes: [
|
||||
{ id: "image", type: "IMAGE_TEXTURE", name: "Mask", imageId: "image:Mask" },
|
||||
{ id: "separate", type: "SEPARATE_COLOR", name: "Separate" },
|
||||
{ id: "principled", type: "PRINCIPLED", name: "Principled" },
|
||||
{ id: "output", type: "OUTPUT", name: "Output" },
|
||||
],
|
||||
links: [
|
||||
{ fromNodeId: "image", fromSocket: "Color", toNodeId: "separate", toSocket: "Color" },
|
||||
{ fromNodeId: "separate", fromSocket: "Blue", toNodeId: "principled", toSocket: "Metallic" },
|
||||
{ fromNodeId: "principled", fromSocket: "BSDF", toNodeId: "output", toSocket: "Surface" },
|
||||
],
|
||||
}), { imageIds: new Set(["image:Mask"]) });
|
||||
assert.equal(report.status, "COMPILED");
|
||||
assert.ok(report.textureBindings.some((binding) => binding.usage === "METALLIC" && binding.imageId === "image:Mask"));
|
||||
assert.equal(report.material.metallic, 1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user