Skip to content

Commit f85dbf4

Browse files
authored
Merge branch 'dev-2.0' into nb-gitignore-local-code-workspace
2 parents bc98a1c + 95a5df2 commit f85dbf4

12 files changed

Lines changed: 651 additions & 118 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"test/**/*.js": "eslint",
2020
"utils/**/*.{js,mjs}": "eslint"
2121
},
22-
"version": "2.2.0",
22+
"version": "2.2.1-rc.0",
2323
"dependencies": {
2424
"@davepagurek/bezier-path": "^0.0.2",
2525
"@japont/unicode-range": "^1.0.0",

src/strands/p5.strands.js

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ function strands(p5, fn) {
3030
//////////////////////////////////////////////
3131
// Global Runtime
3232
//////////////////////////////////////////////
33-
function initStrandsContext(ctx, backend, { active = false, renderer = null, baseShader = null } = {}) {
33+
function initStrandsContext(
34+
ctx,
35+
backend,
36+
{ active = false, renderer = null, baseShader = null } = {},
37+
) {
3438
ctx.dag = createDirectedAcyclicGraph();
3539
ctx.cfg = createControlFlowGraph();
3640
ctx.uniforms = [];
@@ -78,11 +82,8 @@ function strands(p5, fn) {
7882

7983
const prev = {};
8084
for (const key of Object.getOwnPropertyNames(fn)) {
81-
const descriptor = Object.getOwnPropertyDescriptor(
82-
fn,
83-
key
84-
);
85-
if (descriptor && !descriptor.get && typeof fn[key] === 'function') {
85+
const descriptor = Object.getOwnPropertyDescriptor(fn, key);
86+
if (descriptor && !descriptor.get && typeof fn[key] === "function") {
8687
prev[key] = window[key];
8788
window[key] = fn[key].bind(pInst);
8889
}
@@ -104,7 +105,10 @@ function strands(p5, fn) {
104105

105106
p5.Shader.prototype.modify = function (shaderModifier, scope = {}) {
106107
try {
107-
if (shaderModifier instanceof Function || typeof shaderModifier === 'string') {
108+
if (
109+
shaderModifier instanceof Function ||
110+
typeof shaderModifier === "string"
111+
) {
108112
// Reset the context object every time modify is called;
109113
// const backend = glslBackend;
110114
initStrandsContext(strandsContext, this._renderer.strandsBackend, {
@@ -121,9 +125,10 @@ function strands(p5, fn) {
121125
if (options.parser) {
122126
// #7955 Wrap function declaration code in brackets so anonymous functions are not top level statements, which causes an error in acorn when parsing
123127
// https://github.com/acornjs/acorn/issues/1385
124-
const sourceString = typeof shaderModifier === 'string'
125-
? `(${shaderModifier})`
126-
: `(${shaderModifier.toString()})`;
128+
const sourceString =
129+
typeof shaderModifier === "string"
130+
? `(${shaderModifier})`
131+
: `(${shaderModifier.toString()})`;
127132
strandsCallback = transpileStrandsToJS(
128133
p5,
129134
sourceString,
@@ -275,6 +280,100 @@ if (typeof p5 !== "undefined") {
275280
* </div>
276281
*/
277282

283+
/**
284+
* @method smoothstep
285+
* @description
286+
* A shader function that performs smooth Hermite interpolation between `0.0`
287+
* and `1.0`.
288+
*
289+
* This function is equivalent to the GLSL built-in
290+
* `smoothstep(edge0, edge1, x)` and is available inside p5.strands shader
291+
* callbacks. It is commonly used to create soft transitions, smooth edges,
292+
* fades, and anti-aliased effects.
293+
*
294+
* Smoothstep is useful when a threshold or cutoff is needed, but with a
295+
* gradual transition instead of a hard edge.
296+
*
297+
* - Returns `0.0` when `x` is less than or equal to `edge0`
298+
* - Returns `1.0` when `x` is greater than or equal to `edge1`
299+
* - Smoothly interpolates between `0.0` and `1.0` when `x` is between them
300+
*
301+
* @param {Number} edge0
302+
* Lower edge of the transition
303+
* @param {Number} edge1
304+
* Upper edge of the transition
305+
* @param {Number} x
306+
* Input value to interpolate
307+
*
308+
* @returns {Number}
309+
* A value between `0.0` and `1.0`
310+
*
311+
* @example
312+
* <div modernizr="webgl">
313+
* <code>
314+
* // Example 1: A soft vertical fade using smoothstep (no uniforms)
315+
*
316+
* let fadeShader;
317+
*
318+
* function fadeCallback() {
319+
* getColor((inputs) => {
320+
* // x goes from 0 → 1 across the canvas
321+
* let x = inputs.texCoord.x;
322+
*
323+
* // smoothstep creates a soft transition instead of a hard edge
324+
* let t = smoothstep(0.25, 0.35, x);
325+
*
326+
* // Use t directly as brightness
327+
* return [t, t, t, 1];
328+
* });
329+
* }
330+
*
331+
* function setup() {
332+
* createCanvas(300, 200, WEBGL);
333+
* fadeShader = baseFilterShader().modify(fadeCallback);
334+
* }
335+
*
336+
* function draw() {
337+
* background(0);
338+
* filter(fadeShader);
339+
* }
340+
* </code>
341+
* </div>
342+
*
343+
* @example
344+
* <div modernizr="webgl">
345+
* <code>
346+
* // Example 2: Animate the smooth transition using a uniform
347+
*
348+
* let animatedShader;
349+
*
350+
* function animatedFadeCallback() {
351+
* const time = uniformFloat(() => millis() * 0.001);
352+
*
353+
* getColor((inputs) => {
354+
* let x = inputs.texCoord.x;
355+
*
356+
* // Move the smoothstep band back and forth over time
357+
* let center = 0.5 + 0.25 * sin(time);
358+
* let t = smoothstep(center - 0.05, center + 0.05, x);
359+
*
360+
* return [t, t, t, 1];
361+
* });
362+
* }
363+
*
364+
* function setup() {
365+
* createCanvas(300, 200, WEBGL);
366+
* animatedShader = baseFilterShader().modify(animatedFadeCallback);
367+
* }
368+
*
369+
* function draw() {
370+
* background(0);
371+
* filter(animatedShader);
372+
* }
373+
* </code>
374+
* </div>
375+
*/
376+
278377
/**
279378
* @method beforeVertex
280379
* @private

src/strands/strands_api.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,86 @@ import * as FES from './strands_FES'
2121
import { getNodeDataFromID } from './ir_dag'
2222
import { StrandsNode, createStrandsNode } from './strands_node'
2323

24+
const BUILTIN_GLOBAL_SPECS = {
25+
width: { typeInfo: DataType.float1, get: (p) => p.width },
26+
height: { typeInfo: DataType.float1, get: (p) => p.height },
27+
mouseX: { typeInfo: DataType.float1, get: (p) => p.mouseX },
28+
mouseY: { typeInfo: DataType.float1, get: (p) => p.mouseY },
29+
pmouseX: { typeInfo: DataType.float1, get: (p) => p.pmouseX },
30+
pmouseY: { typeInfo: DataType.float1, get: (p) => p.pmouseY },
31+
winMouseX: { typeInfo: DataType.float1, get: (p) => p.winMouseX },
32+
winMouseY: { typeInfo: DataType.float1, get: (p) => p.winMouseY },
33+
pwinMouseX: { typeInfo: DataType.float1, get: (p) => p.pwinMouseX },
34+
pwinMouseY: { typeInfo: DataType.float1, get: (p) => p.pwinMouseY },
35+
frameCount: { typeInfo: DataType.float1, get: (p) => p.frameCount },
36+
deltaTime: { typeInfo: DataType.float1, get: (p) => p.deltaTime },
37+
displayWidth: { typeInfo: DataType.float1, get: (p) => p.displayWidth },
38+
displayHeight: { typeInfo: DataType.float1, get: (p) => p.displayHeight },
39+
windowWidth: { typeInfo: DataType.float1, get: (p) => p.windowWidth },
40+
windowHeight: { typeInfo: DataType.float1, get: (p) => p.windowHeight },
41+
mouseIsPressed: { typeInfo: DataType.bool1, get: (p) => p.mouseIsPressed },
42+
}
43+
44+
function _getBuiltinGlobalsCache(strandsContext) {
45+
if (!strandsContext._builtinGlobals || strandsContext._builtinGlobals.dag !== strandsContext.dag) {
46+
strandsContext._builtinGlobals = {
47+
dag: strandsContext.dag,
48+
nodes: new Map(),
49+
uniformsAdded: new Set(),
50+
}
51+
}
52+
// return the cache
53+
return strandsContext._builtinGlobals
54+
}
55+
56+
function getBuiltinGlobalNode(strandsContext, name) {
57+
const spec = BUILTIN_GLOBAL_SPECS[name]
58+
if (!spec) return null
59+
60+
const cache = _getBuiltinGlobalsCache(strandsContext)
61+
const uniformName = `_p5_global_${name}`
62+
const cached = cache.nodes.get(uniformName)
63+
if (cached) return cached
64+
65+
if (!cache.uniformsAdded.has(uniformName)) {
66+
cache.uniformsAdded.add(uniformName)
67+
strandsContext.uniforms.push({
68+
name: uniformName,
69+
typeInfo: spec.typeInfo,
70+
defaultValue: () => {
71+
const p5Instance = strandsContext.renderer?._pInst || strandsContext.p5?.instance
72+
return p5Instance ? spec.get(p5Instance) : undefined
73+
},
74+
})
75+
}
76+
77+
const { id, dimension } = build.variableNode(strandsContext, spec.typeInfo, uniformName)
78+
const node = createStrandsNode(id, dimension, strandsContext)
79+
node._originalBuiltinName = name
80+
cache.nodes.set(uniformName, node)
81+
return node
82+
}
83+
84+
function installBuiltinGlobalAccessors(strandsContext) {
85+
if (strandsContext._builtinGlobalsAccessorsInstalled) return
86+
87+
const getRuntimeP5Instance = () => strandsContext.renderer?._pInst || strandsContext.p5?.instance
88+
89+
for (const name of Object.keys(BUILTIN_GLOBAL_SPECS)) {
90+
const spec = BUILTIN_GLOBAL_SPECS[name]
91+
Object.defineProperty(window, name, {
92+
get: () => {
93+
if (strandsContext.active) {
94+
return getBuiltinGlobalNode(strandsContext, name);
95+
}
96+
const inst = getRuntimeP5Instance()
97+
return spec.get(inst);
98+
},
99+
})
100+
}
101+
strandsContext._builtinGlobalsAccessorsInstalled = true
102+
}
103+
24104
//////////////////////////////////////////////
25105
// User nodes
26106
//////////////////////////////////////////////
@@ -419,6 +499,8 @@ function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName
419499
return returnedNodeID;
420500
}
421501
export function createShaderHooksFunctions(strandsContext, fn, shader) {
502+
installBuiltinGlobalAccessors(strandsContext)
503+
422504
// Add shader context to hooks before spreading
423505
const vertexHooksWithContext = Object.fromEntries(
424506
Object.entries(shader.hooks.vertex).map(([name, hook]) => [name, { ...hook, shaderContext: 'vertex' }])

src/webgl/p5.Framebuffer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class Framebuffer {
6262
this.renderer.framebuffers.add(this);
6363

6464
this._isClipApplied = false;
65+
this._useCanvasFormat = settings._useCanvasFormat || false;
6566

6667
this.dirty = { colorTexture: false, depthTexture: false };
6768

0 commit comments

Comments
 (0)