diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index 05ae0ec5ea..97607316fa 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -404,23 +404,38 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) { CFG.pushBlock(cfg, entryBlockID); const args = createHookArguments(strandsContext, hookType.parameters); const userReturned = hookUserCallback(...args); + + // Auto-return input if types match and user didn't return anything + let effectiveReturn = userReturned; + if (userReturned === undefined) { + const expected = hookType.returnType; + if (isStructType(expected.typeName)) { + if (hookType.parameters.length === 1) { + const paramType = hookType.parameters[0].type.typeName; + if (paramType === expected.typeName) { + effectiveReturn = args[0]; + } + } + } + } + const expectedReturnType = hookType.returnType; let rootNodeID = null; if(isStructType(expectedReturnType)) { const expectedStructType = structType(expectedReturnType); - if (userReturned instanceof StrandsNode) { - const returnedNode = getNodeDataFromID(strandsContext.dag, userReturned.id); + if (effectiveReturn instanceof StrandsNode) { + const returnedNode = getNodeDataFromID(strandsContext.dag, effectiveReturn.id); if (returnedNode.baseType !== expectedStructType.typeName) { - FES.userError("type error", `You have returned a ${userReturned.baseType} from ${hookType.name} when a ${expectedStructType.typeName} was expected.`); + FES.userError("type error", `You have returned a ${effectiveReturn.baseType} from ${hookType.name} when a ${expectedStructType.typeName} was expected.`); } const newDeps = returnedNode.dependsOn.slice(); for (let i = 0; i < expectedStructType.properties.length; i++) { const expectedType = expectedStructType.properties[i].dataType; - const receivedNode = createStrandsNode(returnedNode.dependsOn[i], dag.dependsOn[userReturned.id], strandsContext); + const receivedNode = createStrandsNode(returnedNode.dependsOn[i], dag.dependsOn[effectiveReturn.id], strandsContext); newDeps[i] = enforceReturnTypeMatch(strandsContext, expectedType, receivedNode, hookType.name); } - dag.dependsOn[userReturned.id] = newDeps; - rootNodeID = userReturned.id; + dag.dependsOn[effectiveReturn.id] = newDeps; + rootNodeID = effectiveReturn.id; } else { const expectedProperties = expectedStructType.properties; @@ -428,11 +443,11 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) { for (let i = 0; i < expectedProperties.length; i++) { const expectedProp = expectedProperties[i]; const propName = expectedProp.name; - const receivedValue = userReturned[propName]; + const receivedValue = effectiveReturn[propName]; if (receivedValue === undefined) { FES.userError('type error', `You've returned an incomplete struct from ${hookType.name}.\n` + `Expected: { ${expectedReturnType.properties.map(p => p.name).join(', ')} }\n` + - `Received: { ${Object.keys(userReturned).join(', ')} }\n` + + `Received: { ${Object.keys(effectiveReturn).join(', ')} }\n` + `All of the properties are required!`); } const expectedTypeInfo = expectedProp.dataType; @@ -448,7 +463,7 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) { throw new Error(`Missing dataType for return type ${expectedReturnType.typeName}`); } const expectedTypeInfo = expectedReturnType.dataType; - rootNodeID = enforceReturnTypeMatch(strandsContext, expectedTypeInfo, userReturned, hookType.name); + rootNodeID = enforceReturnTypeMatch(strandsContext, expectedTypeInfo, effectiveReturn, hookType.name); } const fullHookName = `${hookType.returnType.typeName} ${hookType.name}`; const hookInfo = availableHooks[fullHookName]; diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index c2157b3d18..f8a0c8b4eb 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -928,6 +928,111 @@ visualSuite('WebGL', function() { p5.circle(p5.noise(0), p5.noise(0), 20); screenshot(); }); + + visualSuite('auto-return for shader hooks', () => { + visualTest('auto-returns input struct when return is omitted', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const shader = p5.baseMaterialShader().modify(() => { + p5.getWorldInputs((inputs) => { + inputs.position.x += 10; + // No explicit return - should auto-return inputs + }); + }, { p5 }); + p5.background(255); + p5.noStroke(); + p5.shader(shader); + p5.sphere(20); + screenshot(); + }); + + visualTest('explicit return still works', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const shader = p5.baseMaterialShader().modify(() => { + p5.getWorldInputs((inputs) => { + inputs.position.x += 10; + return inputs; // Explicit return should still work + }); + }, { p5 }); + p5.background(255); + p5.noStroke(); + p5.shader(shader); + p5.sphere(20); + screenshot(); + }); + + visualTest('auto-return works with getObjectInputs', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const shader = p5.baseMaterialShader().modify(() => { + p5.getObjectInputs((inputs) => { + inputs.position.x += 0.25; + // No explicit return + }); + }, { p5 }); + p5.background(255); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.rotateY(p5.PI / 2); + p5.camera(-800, 0, 0, 0, 0, 0); + p5.shader(shader); + p5.sphere(20); + screenshot(); + }); + + visualTest('auto-return works with getCameraInputs', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const shader = p5.baseMaterialShader().modify(() => { + p5.getCameraInputs((inputs) => { + inputs.position.x += 10; + // No explicit return + }); + }, { p5 }); + p5.background(255); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.rotateY(p5.PI / 2); + p5.camera(-800, 0, 0, 0, 0, 0); + p5.shader(shader); + p5.sphere(20); + screenshot(); + }); + + visualTest('auto-return preserves multiple property modifications', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const shader = p5.baseMaterialShader().modify(() => { + p5.getWorldInputs((inputs) => { + inputs.position.x += 5; + inputs.position.y += 5; + inputs.normal.x += 0.5; + inputs.normal = p5.normalize(inputs.normal); + // No explicit return - all modifications should be preserved + }); + }, { p5 }); + p5.background(255); + p5.lights(); + p5.fill('red'); + p5.noStroke(); + p5.shader(shader); + p5.sphere(20); + screenshot(); + }); + + visualTest('auto-return works with getPixelInputs', (p5, screenshot) => { + p5.createCanvas(50, 50, p5.WEBGL); + const shader = p5.baseMaterialShader().modify(() => { + p5.getPixelInputs((inputs) => { + inputs.color = p5.vec4(1.0, 0.0, 0.0, 1.0); // Red + // No explicit return + }); + }, { p5 }); + p5.background(255); + p5.noStroke(); + p5.shader(shader); + p5.circle(0, 0, 40); + screenshot(); + }); + }); }); visualSuite('background()', function () { diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return preserves multiple property modifications/000.png b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return preserves multiple property modifications/000.png new file mode 100644 index 0000000000..ef6d92a6bd Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return preserves multiple property modifications/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return preserves multiple property modifications/metadata.json b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return preserves multiple property modifications/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return preserves multiple property modifications/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getCameraInputs/000.png b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getCameraInputs/000.png new file mode 100644 index 0000000000..3e7bdddc8d Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getCameraInputs/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getCameraInputs/metadata.json b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getCameraInputs/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getCameraInputs/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getObjectInputs/000.png b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getObjectInputs/000.png new file mode 100644 index 0000000000..f210659935 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getObjectInputs/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getObjectInputs/metadata.json b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getObjectInputs/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getObjectInputs/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getPixelInputs/000.png b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getPixelInputs/000.png new file mode 100644 index 0000000000..e1346b5741 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getPixelInputs/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getPixelInputs/metadata.json b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getPixelInputs/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-return works with getPixelInputs/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-returns input struct when return is omitted/000.png b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-returns input struct when return is omitted/000.png new file mode 100644 index 0000000000..0eb873e934 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-returns input struct when return is omitted/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-returns input struct when return is omitted/metadata.json b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-returns input struct when return is omitted/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/auto-returns input struct when return is omitted/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/explicit return still works/000.png b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/explicit return still works/000.png new file mode 100644 index 0000000000..0eb873e934 Binary files /dev/null and b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/explicit return still works/000.png differ diff --git a/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/explicit return still works/metadata.json b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/explicit return still works/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/p5.strands/auto-return for shader hooks/explicit return still works/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file