From 7fd2eb23892acb266881f3a658c60686dc5af124 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Thu, 5 Dec 2024 18:10:32 -0500 Subject: [PATCH] Support WebGL custom vertex attributes --- preview/global/sketch.js | 57 +++++++++++++++++++++++++++++++----- src/shape/custom_shapes.js | 17 +++++++++++ src/shape/vertex.js | 2 +- src/webgl/ShapeBuilder.js | 60 +++++++++++++++++++++++--------------- src/webgl/material.js | 5 ++-- src/webgl/p5.Geometry.js | 3 ++ src/webgl/p5.RendererGL.js | 2 +- 7 files changed, 110 insertions(+), 36 deletions(-) diff --git a/preview/global/sketch.js b/preview/global/sketch.js index 00719764a7..4789f83f36 100644 --- a/preview/global/sketch.js +++ b/preview/global/sketch.js @@ -1,14 +1,55 @@ -console.log(p5); +const vertSrc = `#version 300 es + precision mediump float; + uniform mat4 uModelViewMatrix; + uniform mat4 uProjectionMatrix; + in vec3 aPosition; + in vec2 aOffset; + + void main(){ + vec4 positionVec4 = vec4(aPosition.xyz, 1.0); + positionVec4.xy += aOffset; + gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4; + } +`; + +const fragSrc = `#version 300 es + precision mediump float; + out vec4 outColor; + void main(){ + outColor = vec4(0.0, 1.0, 1.0, 1.0); + } +`; + +let myShader; function setup(){ - createCanvas(200, 200); + createCanvas(100, 100, WEBGL); + + // Create and use the custom shader. + myShader = createShader(vertSrc, fragSrc); + + describe('A wobbly, cyan circle on a gray background.'); } -async function draw(){ - background(0, 50, 50); - circle(100, 100, 50); +function draw(){ + // Set the styles + background(125); + noStroke(); + shader(myShader); + + // Draw the circle. + beginShape(); + for (let i = 0; i < 30; i++){ + const x = 40 * cos(i/30 * TWO_PI); + const y = 40 * sin(i/30 * TWO_PI); + + // Apply some noise to the coordinates. + const xOff = 10 * noise(x + millis()/1000) - 5; + const yOff = 10 * noise(y + millis()/1000) - 5; - fill('white'); - textSize(30); - text('hello', 10, 30); + // Apply these noise values to the following vertex. + vertexProperty('aOffset', [xOff, yOff]); + vertex(x, y); + } + endShape(CLOSE); } diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index c2e924a87b..b13cd885a2 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -571,6 +571,7 @@ class Shape { kind = null; contours = []; _splineEnds = constants.SHOW; + userVertexProperties = null; constructor( vertexProperties, @@ -626,6 +627,22 @@ class Shape { this.#vertexProperties = { ...this.#initialVertexProperties }; this.kind = null; this.contours = []; + this.userVertexProperties = null; + } + + vertexProperty(name, data) { + this.userVertexProperties = this.userVertexProperties || {}; + const key = this.vertexPropertyKey(name); + if (!this.userVertexProperties[key]) { + this.userVertexProperties[key] = data.length ? data.length : 1; + } + this.#vertexProperties[key] = data; + } + vertexPropertyName(key) { + return key.replace(/Src$/, ''); + } + vertexPropertyKey(name) { + return name + 'Src'; } /* diff --git a/src/shape/vertex.js b/src/shape/vertex.js index accb641166..eece9ad0c5 100644 --- a/src/shape/vertex.js +++ b/src/shape/vertex.js @@ -2398,7 +2398,7 @@ function vertex(p5, fn){ * fill(j/rows*255, j/cols*255, 255); * * // Calculate the distance from the corner of each cell to the mouse. - * let distance = dist(x1,y1, mouseX, mouseY); + * let distance = dist(x, y, mouseX, mouseY); * * // Send the distance to the shader. * vertexProperty('aDistance', min(distance, 100)); diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js index c113f3de83..301dd19672 100644 --- a/src/webgl/ShapeBuilder.js +++ b/src/webgl/ShapeBuilder.js @@ -50,7 +50,7 @@ export class ShapeBuilder { } constructFromContours(shape, contours) { - if (this._useUserVertexProperties === true){ + if (this._useUserVertexProperties){ this._resetUserVertexProperties(); } this.geometry.reset(); @@ -58,6 +58,23 @@ export class ShapeBuilder { this.shapeMode = constants.TESS; const shouldProcessEdges = !!this.renderer.states.strokeColor; + const userVertexPropertyHelpers = {}; + if (shape.userVertexProperties) { + this._useUserVertexProperties = true; + for (const key in shape.userVertexProperties) { + const name = shape.vertexPropertyName(key); + const prop = this.geometry._userVertexPropertyHelper(name, [], shape.userVertexProperties[key]); + userVertexPropertyHelpers[key] = prop; + this.tessyVertexSize += prop.getDataSize(); + this.bufferStrides[prop.getSrcName()] = prop.getDataSize(); + this.renderer.buffers.user.push( + new RenderBuffer(prop.getDataSize(), prop.getSrcName(), prop.getDstName(), name, this.renderer) + ); + } + } else { + this._useUserVertexProperties = false; + } + let idx = -1; for (const contour of contours) { this.contourIndices.push(this.geometry.vertices.length); @@ -65,10 +82,25 @@ export class ShapeBuilder { for (const vertex of contour) { idx++ this.geometry.vertices.push(vertex.position); - this.geometry.vertexNormals.push(vertex.normal); + this.geometry.vertexNormals.push(vertex.normal || new Vector(0, 0, 0)); this.geometry.uvs.push(vertex.textureCoordinates.x, vertex.textureCoordinates.y); - this.geometry.vertexColors.push(...vertex.fill.array()); - this.geometry.vertexStrokeColors.push(...vertex.stroke.array()); + if (this.renderer.states.fillColor) { + this.geometry.vertexColors.push(...vertex.fill.array()); + } else { + this.geometry.vertexColors.push(0, 0, 0, 0); + } + if (this.renderer.states.strokeColor) { + this.geometry.vertexStrokeColors.push(...vertex.stroke.array()); + } else { + this.geometry.vertexColors.push(0, 0, 0, 0); + } + for (const key in userVertexPropertyHelpers) { + const prop = userVertexPropertyHelpers[key]; + if (key in vertex) { + prop.setCurrentData(vertex[key]); + } + prop.pushCurrentData(); + } if (shouldProcessEdges && prevIdx >= 0) { // TODO: handle other shape modes this.geometry.edges.push([prevIdx, idx]); @@ -259,26 +291,6 @@ export class ShapeBuilder { return this; } - vertexProperty(propertyName, data) { - if (!this._useUserVertexProperties) { - this._useUserVertexProperties = true; - this.geometry.userVertexProperties = {}; - } - const propertyExists = this.geometry.userVertexProperties[propertyName]; - let prop; - if (propertyExists){ - prop = this.geometry.userVertexProperties[propertyName]; - } else { - prop = this.geometry._userVertexPropertyHelper(propertyName, data); - this.tessyVertexSize += prop.getDataSize(); - this.bufferStrides[prop.getSrcName()] = prop.getDataSize(); - this.renderer.buffers.user.push( - new RenderBuffer(prop.getDataSize(), prop.getSrcName(), prop.getDstName(), propertyName, this.renderer) - ); - } - prop.setCurrentData(data); - } - _resetUserVertexProperties() { const properties = this.geometry.userVertexProperties; for (const propName in properties){ diff --git a/src/webgl/material.js b/src/webgl/material.js index fa70667de6..4a42d117fe 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -9,6 +9,7 @@ import * as constants from '../core/constants'; import { RendererGL } from './p5.RendererGL'; import { Shader } from './p5.Shader'; import { request } from '../io/files'; +import { Color } from '../color/p5.Color'; function material(p5, fn){ /** @@ -3625,7 +3626,7 @@ function material(p5, fn){ this.states.drawMode = constants.TEXTURE; this.states._useNormalMaterial = false; this.states._tex = tex; - this.states.fillColor = true; + this.states.fillColor = new Color(255); }; RendererGL.prototype.normalMaterial = function(...args) { @@ -3634,7 +3635,7 @@ function material(p5, fn){ this.states._useEmissiveMaterial = false; this.states._useNormalMaterial = true; this.states.curFillColor = [1, 1, 1, 1]; - this.states.fillColor = true; + this.states.fillColor = new Color(255); this.states.strokeColor = null; } diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index afaf4c3b7c..89701ced9c 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1818,6 +1818,9 @@ class Geometry { return this.name; }, getCurrentData(){ + if (this.currentData === undefined) { + this.currentData = new Array(this.getDataSize()).fill(0); + } return this.currentData; }, getDataSize() { diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 8ecf6bf87f..a804242c72 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -518,7 +518,7 @@ class RendererGL extends Renderer { } vertexProperty(...args) { - this.shapeBuilder.vertexProperty(...args); + this.currentShape.vertexProperty(...args); } normal(xorv, y, z) {