Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions src/strands/strands_api.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,86 @@ import * as FES from './strands_FES'
import { getNodeDataFromID } from './ir_dag'
import { StrandsNode, createStrandsNode } from './strands_node'

const BUILTIN_GLOBAL_SPECS = {
width: { typeInfo: DataType.float1, get: (p) => p.width },
height: { typeInfo: DataType.float1, get: (p) => p.height },
mouseX: { typeInfo: DataType.float1, get: (p) => p.mouseX },
mouseY: { typeInfo: DataType.float1, get: (p) => p.mouseY },
pmouseX: { typeInfo: DataType.float1, get: (p) => p.pmouseX },
pmouseY: { typeInfo: DataType.float1, get: (p) => p.pmouseY },
winMouseX: { typeInfo: DataType.float1, get: (p) => p.winMouseX },
winMouseY: { typeInfo: DataType.float1, get: (p) => p.winMouseY },
pwinMouseX: { typeInfo: DataType.float1, get: (p) => p.pwinMouseX },
pwinMouseY: { typeInfo: DataType.float1, get: (p) => p.pwinMouseY },
frameCount: { typeInfo: DataType.float1, get: (p) => p.frameCount },
deltaTime: { typeInfo: DataType.float1, get: (p) => p.deltaTime },
displayWidth: { typeInfo: DataType.float1, get: (p) => p.displayWidth },
displayHeight: { typeInfo: DataType.float1, get: (p) => p.displayHeight },
windowWidth: { typeInfo: DataType.float1, get: (p) => p.windowWidth },
windowHeight: { typeInfo: DataType.float1, get: (p) => p.windowHeight },
mouseIsPressed: { typeInfo: DataType.bool1, get: (p) => p.mouseIsPressed },
}

function _getBuiltinGlobalsCache(strandsContext) {
if (!strandsContext._builtinGlobals || strandsContext._builtinGlobals.dag !== strandsContext.dag) {
strandsContext._builtinGlobals = {
dag: strandsContext.dag,
nodes: new Map(),
uniformsAdded: new Set(),
}
}
// return the cache
return strandsContext._builtinGlobals
}

function getBuiltinGlobalNode(strandsContext, name) {
const spec = BUILTIN_GLOBAL_SPECS[name]
if (!spec) return null

const cache = _getBuiltinGlobalsCache(strandsContext)
const uniformName = `_p5_global_${name}`
const cached = cache.nodes.get(uniformName)
if (cached) return cached

if (!cache.uniformsAdded.has(uniformName)) {
cache.uniformsAdded.add(uniformName)
strandsContext.uniforms.push({
name: uniformName,
typeInfo: spec.typeInfo,
defaultValue: () => {
const p5Instance = strandsContext.renderer?._pInst || strandsContext.p5?.instance
return p5Instance ? spec.get(p5Instance) : undefined
},
})
}

const { id, dimension } = build.variableNode(strandsContext, spec.typeInfo, uniformName)
const node = createStrandsNode(id, dimension, strandsContext)
node._originalBuiltinName = name
cache.nodes.set(uniformName, node)
return node
}

function installBuiltinGlobalAccessors(strandsContext) {
if (strandsContext._builtinGlobalsAccessorsInstalled) return

const getRuntimeP5Instance = () => strandsContext.renderer?._pInst || strandsContext.p5?.instance

for (const name of Object.keys(BUILTIN_GLOBAL_SPECS)) {
const spec = BUILTIN_GLOBAL_SPECS[name]
Object.defineProperty(window, name, {
get: () => {
if (strandsContext.active) {
return getBuiltinGlobalNode(strandsContext, name);
}
const inst = getRuntimeP5Instance()
return spec.get(inst);
},
})
}
strandsContext._builtinGlobalsAccessorsInstalled = true
}

//////////////////////////////////////////////
// User nodes
//////////////////////////////////////////////
Expand Down Expand Up @@ -419,6 +499,8 @@ function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName
return returnedNodeID;
}
export function createShaderHooksFunctions(strandsContext, fn, shader) {
installBuiltinGlobalAccessors(strandsContext)

// Add shader context to hooks before spreading
const vertexHooksWithContext = Object.fromEntries(
Object.entries(shader.hooks.vertex).map(([name, hook]) => [name, { ...hook, shaderContext: 'vertex' }])
Expand Down
18 changes: 18 additions & 0 deletions test/unit/visual/cases/webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,24 @@ visualSuite('WebGL', function() {
screenshot();
});

visualTest('uses width/height in getFinalColor', (p5, screenshot) => {
let firstShader;
function firstShaderCallback() {
getFinalColor((color) => {
color = [width / 60, height / 60, 0, 1];
return color;
});
}
p5.createCanvas(60, 60, p5.WEBGL);
p5.pixelDensity(1);
firstShader = p5.baseColorShader().modify(firstShaderCallback);
p5.background(0);
p5.shader(firstShader);
p5.noStroke();
p5.plane(20, 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);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
21 changes: 21 additions & 0 deletions test/unit/webgl/p5.Shader.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,27 @@ suite('p5.Shader', function() {
}).not.toThrowError();
});

test('returns numbers for builtin globals outside hooks and a strandNode when called inside hooks', () => {
myp5.createCanvas(5, 5, myp5.WEBGL);
myp5.baseMaterialShader().modify(() => {
myp5.getPixelInputs(inputs => {
const mxInHook = window.mouseX;
const wInHook = window.width;
assert.isTrue(mxInHook.isStrandsNode);
assert.isTrue(wInHook.isStrandsNode);
inputs.color = [1, 0, 0, 1];
return inputs;
});
}, { myp5 });

const mx = window.mouseX;
const w = window.width;
assert.isNumber(mx);
assert.isNumber(w);
assert.strictEqual(w, myp5.width);
});


test('handle custom uniform names with automatic values', () => {
myp5.createCanvas(50, 50, myp5.WEBGL);
const testShader = myp5.baseMaterialShader().modify(() => {
Expand Down
Loading