Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
22 changes: 22 additions & 0 deletions test/unit/webgl/p5.Shader.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,28 @@ suite('p5.Shader', function() {
}).not.toThrowError();
});

test('returns numbers for builtin globals outside hooks and a strandNode when called inside hooks', () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this test enough? do you suggest adding unit tests as well? @davepagurek

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could add a visual test that makes use of e.g. width and height?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could add a visual test that makes use of e.g. width and height?

Aah yes, I mean visual tests.

myp5.createCanvas(5, 5, myp5.WEBGL);
let mxInHook;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last thing for this before merging, these variables don't get assigned to -- the callback in modify has no access to these due to our transpilation process. Currently it'd effectively be assigning to a global variable. We should probably remove these lets and then declare them locally in the shader to avoid confusion.

let wInHook;
myp5.baseMaterialShader().modify(() => {
myp5.getPixelInputs(inputs => {
mxInHook = window.mouseX;
wInHook = window.width;
inputs.color = [1, 0, 0, 1];
assert.isTrue(mxInHook.isStrandsNode);
assert.isTrue(wInHook.isStrandsNode);
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