From b305b1b0032631b6a7d56a929925b511ff51e96e Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 14 Sep 2024 01:15:34 +0100 Subject: [PATCH 01/94] immediate mode geometry properties are fully deleted --- src/webgl/p5.Geometry.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 8ee028ea67..31bd08d4cf 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -450,6 +450,11 @@ p5.Geometry = class Geometry { this.vertexNormals.length = 0; this.uvs.length = 0; + for (const attr of this.userAttributes){ + delete this[attr.name]; + } + this.userAttributes.length = 0; + this.dirtyFlags = {}; } From 4dcf0d9c5e5e9d796db14f2f09a34766722b9470 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:35:46 +0100 Subject: [PATCH 02/94] updated renderer.setAttribute() call to reflect new method name --- src/core/shape/vertex.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 60c2cf1489..a2e02a976b 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2253,4 +2253,10 @@ p5.prototype.normal = function(x, y, z) { return this; }; +p5.prototype.setAttribute = function(attributeName, data){ + // this._assert3d('setAttribute'); + // p5._validateParameters('setAttribute', arguments); + this._renderer.setAttribute(attributeName, data); +}; + export default p5; From 9d7ab3743fe1c152c086d18314c0fe9ef1497d6f Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:36:18 +0100 Subject: [PATCH 03/94] removed debugging log --- src/webgl/GeometryBuilder.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index ac78ec7e94..4f9a0d4e8d 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -59,6 +59,17 @@ class GeometryBuilder { ...this.transformNormals(input.vertexNormals) ); this.geometry.uvs.push(...input.uvs); + const userAttributes = input.userAttributes; + if (userAttributes.length > 0){ + for (const attr of userAttributes){ + const name = attr.name; + const size = attr.size; + const data = input[name]; + for (let i = 0; i < data.length; i+=size){ + this.geometry.setAttribute(name, data.slice(i, i + size)); + } + } + } if (this.renderer._doFill) { this.geometry.faces.push( From 9b3909a6b8e81a615625420874c3f9d8ad24f5c8 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:37:02 +0100 Subject: [PATCH 04/94] updated to take array or single value --- src/webgl/p5.Geometry.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 31bd08d4cf..9aae274b0d 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1915,6 +1915,22 @@ p5.Geometry = class Geometry { } return this; } + + setAttribute(attributeName, data){ + const size = data.length ? data.length : 1; + if (!this.hasOwnProperty(attributeName)){ + this[attributeName] = []; + this.userAttributes.push({ + name: attributeName, + size: size + }); + } + if (size > 1){ + this[attributeName].push(...data); + } else{ + this[attributeName].push(data); + } + } }; /** From 913131eb35dd4788dc2ec6c34d8bf2b9e5cb91ba Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:37:44 +0100 Subject: [PATCH 05/94] removed empty lines --- src/webgl/p5.RenderBuffer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/webgl/p5.RenderBuffer.js b/src/webgl/p5.RenderBuffer.js index 79851e485f..146d63e039 100644 --- a/src/webgl/p5.RenderBuffer.js +++ b/src/webgl/p5.RenderBuffer.js @@ -32,7 +32,6 @@ p5.RenderBuffer = class { if (!attr) { return; } - // check if the model has the appropriate source array let buffer = geometry[this.dst]; const src = model[this.src]; @@ -53,7 +52,6 @@ p5.RenderBuffer = class { const values = map ? map(src) : src; // fill the buffer with the values this._renderer._bindBuffer(buffer, gl.ARRAY_BUFFER, values); - // mark the model's source array as clean model.dirtyFlags[this.src] = false; } From 88c7fbcace6e8522f35329a1a4f721b30be6f758 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:38:30 +0100 Subject: [PATCH 06/94] beginShape updated for tesselated shapes and to reset new attributes object --- src/webgl/p5.RendererGL.Immediate.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 47a656fa83..eb74f00898 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -33,6 +33,14 @@ import './p5.RenderBuffer'; p5.RendererGL.prototype.beginShape = function(mode) { this.immediateMode.shapeMode = mode !== undefined ? mode : constants.TESS; + if (this._useUserAttributes === true){ + for (const name of Object.keys(this.userAttributes)){ + delete this.immediateMode.geometry[name]; + } + delete this.userAttributes; + this._useUserAttributes = false; + } + this.tessyVertexSize = 12; this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; return this; From 19a3d70558ca7b762bb087cb75e36d734eca7f47 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:40:47 +0100 Subject: [PATCH 07/94] set more than one custom attribute per shape/geometry. back fill with 0's in case not enough attributes set --- src/webgl/p5.RendererGL.Immediate.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index eb74f00898..16bb97d4bf 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -121,6 +121,19 @@ p5.RendererGL.prototype.vertex = function(x, y) { const vert = new p5.Vector(x, y, z); this.immediateMode.geometry.vertices.push(vert); this.immediateMode.geometry.vertexNormals.push(this._currentNormal); + if (this._useUserAttributes){ + const geom = this.immediateMode.geometry; + const verts = geom.vertices; + Object.entries(this.userAttributes).forEach(([name, data]) => { + const size = data.length ? data.length : 1; + if (verts.length > 0 && !geom.hasOwnProperty(name)) { + for (let i = 0; i < verts.length - 1; i++) { + this.immediateMode.geometry.setAttribute(name, Array(size).fill(0)); + } + } + this.immediateMode.geometry.setAttribute(name, data); + }); + } const vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; this.immediateMode.geometry.vertexColors.push( vertexColor[0], From ae52039870d88b321db838ea53df892747ec28b8 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:42:38 +0100 Subject: [PATCH 08/94] refactor setAttribute method to update a userAttributes object and increase the tesselation vertex size info --- src/webgl/p5.RendererGL.Immediate.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 16bb97d4bf..73daa1ac6b 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -186,6 +186,29 @@ p5.RendererGL.prototype.vertex = function(x, y) { return this; }; +p5.RendererGL.prototype.setAttribute = function(attributeName, data){ + // if attributeName is in one of default, throw some warning + if(!this._useUserAttributes){ + this._useUserAttributes = true; + this.userAttributes = {}; + } + const size = data.length ? data.length : 1; + if (!this.userAttributes.hasOwnProperty(attributeName)){ + this.tessyVertexSize += size; + } + this.userAttributes[attributeName] = data; + const buff = attributeName.concat('Buffer'); + const bufferExists = this.immediateMode + .buffers + .user + .some(buffer => buffer.dst === buff); + if(!bufferExists){ + this.immediateMode.buffers.user.push( + new p5.RenderBuffer(size, attributeName, buff, attributeName, this) + ); + } +}; + /** * Sets the normal to use for subsequent vertices. * @private From 72cf3bc89aef90c63d862e57ce60abf9bcf9d422 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:44:29 +0100 Subject: [PATCH 09/94] _tesselateShape works with custom attributes --- src/webgl/p5.RendererGL.Immediate.js | 31 ++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 73daa1ac6b..5c3b633383 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -312,6 +312,7 @@ p5.RendererGL.prototype.endShape = function( this.immediateMode._bezierVertex.length = 0; this.immediateMode._quadraticVertex.length = 0; this.immediateMode._curveVertex.length = 0; + return this; }; @@ -326,7 +327,6 @@ p5.RendererGL.prototype.endShape = function( */ p5.RendererGL.prototype._processVertices = function(mode) { if (this.immediateMode.geometry.vertices.length === 0) return; - const calculateStroke = this._doStroke; const shouldClose = mode === constants.CLOSE; if (calculateStroke) { @@ -479,20 +479,47 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals[i].y, this.immediateMode.geometry.vertexNormals[i].z ); + if (this._useUserAttributes){ + const userAttributesArray = Object.entries(this.userAttributes); + for (let [name, data] of userAttributesArray){ + const size = data.length ? data.length : 1; + for (let j = 0; j < size; j++){ + contours[contours.length-1].push( + this.immediateMode.geometry[name][i * size + j] + );} + } + } } const polyTriangles = this._triangulate(contours); const originalVertices = this.immediateMode.geometry.vertices; this.immediateMode.geometry.vertices = []; this.immediateMode.geometry.vertexNormals = []; this.immediateMode.geometry.uvs = []; + if (this._useUserAttributes){ + const userAttributeNames = Object.keys(this.userAttributes); + for (let name of userAttributeNames){ + delete this.immediateMode.geometry[name]; + } + } const colors = []; for ( let j = 0, polyTriLength = polyTriangles.length; j < polyTriLength; - j = j + p5.RendererGL.prototype.tessyVertexSize + j = j + this.tessyVertexSize ) { colors.push(...polyTriangles.slice(j + 5, j + 9)); this.normal(...polyTriangles.slice(j + 9, j + 12)); + if(this._useUserAttributes){ + let offset = 12; + const userAttributesArray = Object.entries(this.userAttributes); + for (let [name, data] of userAttributesArray){ + const size = data.length ? data.length : 1; + const start = j + offset; + const end = start + size; + this.setAttribute(name, polyTriangles.slice(start, end)); + offset = end; + } + } this.vertex(...polyTriangles.slice(j, j + 5)); } if (this.geometryBuilder) { From 03d2e5ef20441ea80d0a4a19adfa65ef8d410fe1 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:45:37 +0100 Subject: [PATCH 10/94] changed references to prototype.tessyvertexSize to this.tessyVertexSize so that custom attributes can increase size of vertex data --- src/webgl/p5.RendererGL.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index f712787233..4c4b274183 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -2475,7 +2475,7 @@ p5.RendererGL = class RendererGL extends Renderer { for ( let j = 0; j < contour.length; - j += p5.RendererGL.prototype.tessyVertexSize + j += this.tessyVertexSize ) { if (contour[j + 2] !== z) { allSameZ = false; @@ -2498,11 +2498,11 @@ p5.RendererGL = class RendererGL extends Renderer { for ( let j = 0; j < contour.length; - j += p5.RendererGL.prototype.tessyVertexSize + j += this.tessyVertexSize ) { const coords = contour.slice( j, - j + p5.RendererGL.prototype.tessyVertexSize + j + this.tessyVertexSize ); this._tessy.gluTessVertex(coords, coords); } From 616d7cac6c05837fcd32b6b452c45f4c6792282d Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:46:30 +0100 Subject: [PATCH 11/94] free user buffers --- src/webgl/p5.RendererGL.Retained.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 49f2dd772b..2405aabe0b 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -62,6 +62,7 @@ p5.RendererGL.prototype._freeBuffers = function(gId) { // free all the buffers freeBuffers(this.retainedMode.buffers.stroke); freeBuffers(this.retainedMode.buffers.fill); + freeBuffers(this.retainedMode.buffers.user); }; /** From 8d2656f9c0d66f1485cc0f5db34bf14db4f297af Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:49:45 +0100 Subject: [PATCH 12/94] moved user attributes section to the bottom of createBuffers() --- src/webgl/p5.RendererGL.Retained.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 2405aabe0b..0a47229225 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -115,6 +115,20 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { ? model.lineVertices.length / 3 : 0; + if (model.userAttributes.length > 0){ + for (const attr of model.userAttributes){ + const buff = attr.name.concat('Buffer'); + const bufferExists = this.retainedMode + .buffers + .user + .some(buffer => buffer.dst === buff); + if(!bufferExists){ + this.retainedMode.buffers.user.push( + new p5.RenderBuffer(attr.size, attr.name, buff, attr.name, this) + ); + } + } + } return buffers; }; From 859ac70625456f2ab99b164b1e29ce8173a4fc31 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 15 Sep 2024 15:50:42 +0100 Subject: [PATCH 13/94] ignore my dev folder --- .gitignore | 51 ++++++++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 7883f080bb..9af31927d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,26 @@ -*.DS_Store -.project -node_modules/* -experiments/* -lib_old/* -lib/p5.* -lib/modules -docs/reference/* -!*.gitkeep -examples/3d/ -.idea -dist/ -p5.zip -bower-repo/ -p5-website/ -.vscode/settings.json -.nyc_output/* -coverage/ -lib/p5-test.js -release/ -yarn.lock -docs/data.json -analyzer/ -preview/ -__screenshots__/ \ No newline at end of file +*.DS_Store +.project +node_modules/* +experiments/* +lib_old/* +lib/p5.* +lib/modules +docs/reference/* +!*.gitkeep +examples/3d/ +.idea +dist/ +p5.zip +bower-repo/ +p5-website/ +.vscode/settings.json +.nyc_output/* +coverage/ +lib/p5-test.js +release/ +yarn.lock +docs/data.json +analyzer/ +preview/ +__screenshots__/ +attributes-example/ \ No newline at end of file From cafdc1bc30ce98a8734fab47f192a77daf71d0a8 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Thu, 19 Sep 2024 13:10:51 +0100 Subject: [PATCH 14/94] fixed a bug in _tesselateShape miscounting memory offset for custom attributes --- src/webgl/p5.RendererGL.Immediate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 5c3b633383..a36880c56e 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -517,7 +517,7 @@ p5.RendererGL.prototype._tesselateShape = function() { const start = j + offset; const end = start + size; this.setAttribute(name, polyTriangles.slice(start, end)); - offset = end; + offset += size; } } this.vertex(...polyTriangles.slice(j, j + 5)); From 0e35884216e0614e5c37f019f43f4fb79568a313 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 12:07:06 +0100 Subject: [PATCH 15/94] define user attributes array upfront --- src/webgl/p5.Geometry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 9aae274b0d..0414b0b30c 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -283,6 +283,8 @@ p5.Geometry = class Geometry { // One color per vertex representing the stroke color at that vertex this.vertexStrokeColors = []; + this.userAttributes = []; + // One color per line vertex, generated automatically based on // vertexStrokeColors in _edgesToVertices() this.lineVertexColors = new p5.DataArray(); From d842f056a15a4e3a48347cddeb512b1eb731ee2b Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 12:07:26 +0100 Subject: [PATCH 16/94] prepare immediate user buffers --- src/webgl/p5.RendererGL.Immediate.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index a36880c56e..296103afe6 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -590,6 +590,11 @@ p5.RendererGL.prototype._drawImmediateFill = function(count = 1) { for (const buff of this.immediateMode.buffers.fill) { buff._prepareBuffer(this.immediateMode.geometry, shader); } + if (this._useUserAttributes){ + for (const buff of this.immediateMode.buffers.user){ + buff._prepareBuffer(this.immediateMode.geometry, shader); + } + } shader.disableRemainingAttributes(); this._applyColorBlend( @@ -636,6 +641,11 @@ p5.RendererGL.prototype._drawImmediateStroke = function() { for (const buff of this.immediateMode.buffers.stroke) { buff._prepareBuffer(this.immediateMode.geometry, shader); } + if (this._useUserAttributes){ + for (const buff of this.immediateMode.buffers.user){ + buff._prepareBuffer(this.immediateMode.geometry, shader); + } + } shader.disableRemainingAttributes(); this._applyColorBlend( this.curStrokeColor, From 33259c01d74521e7cadc3e19cf694ced3c82a4da Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 12:08:18 +0100 Subject: [PATCH 17/94] bool for immediate user attributes, arrays for user attributes p5.Renderbuffers --- src/webgl/p5.RendererGL.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 4c4b274183..6834cf0ece 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -575,7 +575,7 @@ p5.RendererGL = class RendererGL extends Renderer { this.userFillShader = undefined; this.userStrokeShader = undefined; this.userPointShader = undefined; - + this._useUserAttributes = undefined; // Default drawing is done in Retained Mode // Geometry and Material hashes stored here this.retainedMode = { @@ -599,7 +599,8 @@ p5.RendererGL = class RendererGL extends Renderer { text: [ new p5.RenderBuffer(3, 'vertices', 'vertexBuffer', 'aPosition', this, this._vToNArray), new p5.RenderBuffer(2, 'uvs', 'uvBuffer', 'aTexCoord', this, this._flatten) - ] + ], + user:[] } }; @@ -627,7 +628,8 @@ p5.RendererGL = class RendererGL extends Renderer { new p5.RenderBuffer(3, 'lineTangentsOut', 'lineTangentsOutBuffer', 'aTangentOut', this), new p5.RenderBuffer(1, 'lineSides', 'lineSidesBuffer', 'aSide', this) ], - point: this.GL.createBuffer() + point: this.GL.createBuffer(), + user:[] } }; From b5c3213dfad49c504bfb3d5efe4044864e1ab88b Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 12:09:00 +0100 Subject: [PATCH 18/94] used const reference to geometry which was already defined but unused --- src/webgl/p5.RendererGL.Retained.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 0a47229225..cffdb35729 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -145,7 +145,7 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { if ( !this.geometryBuilder && this._doFill && - this.retainedMode.geometry[gId].vertexCount > 0 + geometry.vertexCount > 0 ) { this._useVertexColor = (geometry.model.vertexColors.length > 0); const fillShader = this._getRetainedFillShader(); From e038c9c730d9092b7e208de06c1db4c9d14bb0b3 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 12:09:30 +0100 Subject: [PATCH 19/94] prepare user attribute buffers --- src/webgl/p5.RendererGL.Retained.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index cffdb35729..c30bae7a08 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -153,6 +153,11 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { for (const buff of this.retainedMode.buffers.fill) { buff._prepareBuffer(geometry, fillShader); } + if (geometry.model.userAttributes.length > 0){ + for (const buff of this.retainedMode.buffers.user){ + buff._prepareBuffer(geometry, fillShader); + } + } fillShader.disableRemainingAttributes(); if (geometry.indexBuffer) { //vertex index buffer @@ -173,6 +178,11 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { for (const buff of this.retainedMode.buffers.stroke) { buff._prepareBuffer(geometry, strokeShader); } + if (geometry.model.userAttributes.length > 0){ + for (const buff of this.retainedMode.buffers.user){ + buff._prepareBuffer(geometry, strokeShader); + } + } strokeShader.disableRemainingAttributes(); this._applyColorBlend( this.curStrokeColor, From f7d2b992cf28eed42974963ce7c1cbb334f86ab1 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 15:06:46 +0100 Subject: [PATCH 20/94] handled differences in custom attributes between geometries in the builder --- src/webgl/GeometryBuilder.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 4f9a0d4e8d..256420f9a2 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -59,12 +59,32 @@ class GeometryBuilder { ...this.transformNormals(input.vertexNormals) ); this.geometry.uvs.push(...input.uvs); - const userAttributes = input.userAttributes; - if (userAttributes.length > 0){ - for (const attr of userAttributes){ - const name = attr.name; - const size = attr.size; + + const inputUserAttributes = Object.entries(input.userAttributes); + const currentUserAttributes = Object.entries(this.geometry.userAttributes); + + if (currentUserAttributes.length > 0){ + for (const [name, size] of currentUserAttributes){ + if (name in input.userAttributes){ + continue; + } + const numMissingValues = size * input.vertices.length; + // this.geometry[name].concat(Array(numMissingValues).fill(0)); + this.geometry[name] = (this.geometry[name] || []).concat(Array(numMissingValues).fill(0)); + } + } + if (inputUserAttributes.length > 0){ + for (const [name, size] of inputUserAttributes){ const data = input[name]; + if (!(name in this.geometry.userAttributes) && this.geometry.vertices.length - input.vertices.length > 0){ + const numMissingValues = size * (this.geometry.vertices.length - input.vertices.length) - size; + this.geometry.setAttribute(name, Array(size).fill(0)); + // this.geometry[name].concat(Array(numMissingValues).fill(0)); + this.geometry[name] = this.geometry[name].concat(Array(numMissingValues).fill(0)); + } + if (this.geometry.userAttributes[name] != size){ + console.log("This user attribute has different sizes"); + } for (let i = 0; i < data.length; i+=size){ this.geometry.setAttribute(name, data.slice(i, i + size)); } From 55a37af5097c2e3a36070de3ab7c3334d0bf31a3 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 15:07:19 +0100 Subject: [PATCH 21/94] user attributes is now a single Object instead of an array of them --- src/webgl/p5.Geometry.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 0414b0b30c..fb2328de44 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -283,7 +283,7 @@ p5.Geometry = class Geometry { // One color per vertex representing the stroke color at that vertex this.vertexStrokeColors = []; - this.userAttributes = []; + this.userAttributes = {}; // One color per line vertex, generated automatically based on // vertexStrokeColors in _edgesToVertices() @@ -452,10 +452,10 @@ p5.Geometry = class Geometry { this.vertexNormals.length = 0; this.uvs.length = 0; - for (const attr of this.userAttributes){ + for (const attr of Object.keys(this.userAttributes)){ delete this[attr.name]; } - this.userAttributes.length = 0; + this.userAttributes = {}; this.dirtyFlags = {}; } @@ -1922,10 +1922,7 @@ p5.Geometry = class Geometry { const size = data.length ? data.length : 1; if (!this.hasOwnProperty(attributeName)){ this[attributeName] = []; - this.userAttributes.push({ - name: attributeName, - size: size - }); + this.userAttributes[attributeName] = size; } if (size > 1){ this[attributeName].push(...data); From dd0801467be738ad0742155c35ca1f816e76f5e3 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sun, 22 Sep 2024 15:08:00 +0100 Subject: [PATCH 22/94] reflect the change of geometry custom attributes from objects in an array to single object --- src/webgl/p5.RendererGL.Retained.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index c30bae7a08..4351d7c067 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -115,16 +115,16 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { ? model.lineVertices.length / 3 : 0; - if (model.userAttributes.length > 0){ - for (const attr of model.userAttributes){ - const buff = attr.name.concat('Buffer'); + if (Object.keys(model.userAttributes).length > 0){ + for (const [name, size] of Object.entries(model.userAttributes)){ + const buff = name.concat('Buffer'); const bufferExists = this.retainedMode .buffers .user .some(buffer => buffer.dst === buff); if(!bufferExists){ this.retainedMode.buffers.user.push( - new p5.RenderBuffer(attr.size, attr.name, buff, attr.name, this) + new p5.RenderBuffer(size, name, buff, name, this) ); } } @@ -153,7 +153,7 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { for (const buff of this.retainedMode.buffers.fill) { buff._prepareBuffer(geometry, fillShader); } - if (geometry.model.userAttributes.length > 0){ + if (Object.keys(geometry.model.userAttributes).length > 0){ for (const buff of this.retainedMode.buffers.user){ buff._prepareBuffer(geometry, fillShader); } @@ -178,7 +178,7 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { for (const buff of this.retainedMode.buffers.stroke) { buff._prepareBuffer(geometry, strokeShader); } - if (geometry.model.userAttributes.length > 0){ + if (Object.keys(geometry.model.userAttributes).length > 0){ for (const buff of this.retainedMode.buffers.user){ buff._prepareBuffer(geometry, strokeShader); } From a801e4973a9a39c8eff2ccff0b4cba59c1552928 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:18:46 +0100 Subject: [PATCH 23/94] Remove Object.keys intermediate array and loop directly on user attributes object --- src/webgl/p5.RendererGL.Immediate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 296103afe6..1ce2c97b9c 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -34,7 +34,7 @@ p5.RendererGL.prototype.beginShape = function(mode) { this.immediateMode.shapeMode = mode !== undefined ? mode : constants.TESS; if (this._useUserAttributes === true){ - for (const name of Object.keys(this.userAttributes)){ + for (const name in this.userAttributes){ delete this.immediateMode.geometry[name]; } delete this.userAttributes; From 6ec85c118074d4b03f56504ab2535b8c2f7d8c56 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:21:20 +0100 Subject: [PATCH 24/94] Added an optional size parameter to setAttribute so that bigger chunks of data (without loops) can be submitted so long as the size is defined. --- src/webgl/p5.Geometry.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index fb2328de44..6e41e06efa 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1918,13 +1918,16 @@ p5.Geometry = class Geometry { return this; } - setAttribute(attributeName, data){ - const size = data.length ? data.length : 1; + setAttribute(attributeName, data, size = data.length ? data.length : 1){ if (!this.hasOwnProperty(attributeName)){ this[attributeName] = []; this.userAttributes[attributeName] = size; } - if (size > 1){ + if (size != this.userAttributes[attributeName]){ + p5._friendlyError(`Custom attribute ${attributeName} has been set with various data sizes. You can change it's name, + or if it was an accident, set ${attributeName} to have the same number of inputs each time!`, 'setAttribute()'); + } + if (data.length){ this[attributeName].push(...data); } else{ this[attributeName].push(data); From 59ed53bd1a7d3a0c8a6910ca4cfaa8911ed18fe6 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:25:13 +0100 Subject: [PATCH 25/94] Remove object.entries iteration for 'in' loop and changed to use the new optional size parameter on set attribute, which cuts a layer of loops --- src/webgl/GeometryBuilder.js | 78 ++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 256420f9a2..b4380cc65c 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -60,37 +60,65 @@ class GeometryBuilder { ); this.geometry.uvs.push(...input.uvs); - const inputUserAttributes = Object.entries(input.userAttributes); - const currentUserAttributes = Object.entries(this.geometry.userAttributes); + const inputAttrs = input.userAttributes; + const builtAttrs = this.geometry.userAttributes; + const numPreviousVertices = this.geometry.vertices.length - input.vertices.length; - if (currentUserAttributes.length > 0){ - for (const [name, size] of currentUserAttributes){ - if (name in input.userAttributes){ - continue; - } - const numMissingValues = size * input.vertices.length; - // this.geometry[name].concat(Array(numMissingValues).fill(0)); - this.geometry[name] = (this.geometry[name] || []).concat(Array(numMissingValues).fill(0)); + for (const attr in builtAttrs){ + if (attr in inputAttrs){ + continue; } + const size = builtAttrs[attr]; + const numMissingValues = size * input.vertices.length; + const missingValues = Array(numMissingValues).fill(0); + this.geometry.setAttribute(attr, missingValues, size); } - if (inputUserAttributes.length > 0){ - for (const [name, size] of inputUserAttributes){ - const data = input[name]; - if (!(name in this.geometry.userAttributes) && this.geometry.vertices.length - input.vertices.length > 0){ - const numMissingValues = size * (this.geometry.vertices.length - input.vertices.length) - size; - this.geometry.setAttribute(name, Array(size).fill(0)); - // this.geometry[name].concat(Array(numMissingValues).fill(0)); - this.geometry[name] = this.geometry[name].concat(Array(numMissingValues).fill(0)); - } - if (this.geometry.userAttributes[name] != size){ - console.log("This user attribute has different sizes"); - } - for (let i = 0; i < data.length; i+=size){ - this.geometry.setAttribute(name, data.slice(i, i + size)); - } + for (const attr in inputAttrs){ + const data = input[attr]; + const size = inputAttrs[attr]; + if (numPreviousVertices > 0 && !(attr in this.geometry.userAttributes)){ + const numMissingValues = size * numPreviousVertices; + const missingValues = Array(numMissingValues).fill(0); + console.log(`ATTR: ${attr}, SIZE@ ${size}, NUMMISSINVALS: ${numMissingValues}`); + this.geometry.setAttribute(attr, missingValues, size); + } + if (this.geometry.userAttributes[attr] != size){ + console.log("This user attribute has different sizes"); } + this.geometry.setAttribute(attr, data, size); } + // const inputUserAttributes = Object.entries(input.userAttributes); + // const currentUserAttributes = Object.entries(this.geometry.userAttributes); + + // if (currentUserAttributes.length > 0){ + // for (const [name, size] of currentUserAttributes){ + // if (name in input.userAttributes){ + // continue; + // } + // const numMissingValues = size * input.vertices.length; + // // this.geometry[name].concat(Array(numMissingValues).fill(0)); + // this.geometry[name] = (this.geometry[name] || []).concat(Array(numMissingValues).fill(0)); + // } + // } + // if (inputUserAttributes.length > 0){ + // for (const [name, size] of inputUserAttributes){ + // const data = input[name]; + // if (!(name in this.geometry.userAttributes) && this.geometry.vertices.length - input.vertices.length > 0){ + // const numMissingValues = size * (this.geometry.vertices.length - input.vertices.length) - size; + // this.geometry.setAttribute(name, Array(size).fill(0)); + // console.log(`ATTR: ${name}, SIZE@ ${size}, NUMMISSINVALS: ${numMissingValues}`); + // this.geometry[name] = this.geometry[name].concat(Array(numMissingValues).fill(0)); + // } + // if (this.geometry.userAttributes[name] != size){ + // console.log("This user attribute has different sizes"); + // } + // for (let i = 0; i < data.length; i+=size){ + // this.geometry.setAttribute(name, data.slice(i, i + size)); + // } + // } + // } + if (this.renderer._doFill) { this.geometry.faces.push( ...input.faces.map(f => f.map(idx => idx + startIdx)) From 3d195217c34e26cc86edab8208cabaacf7afe52c Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:26:32 +0100 Subject: [PATCH 26/94] remove old implementation with extra loop --- src/webgl/GeometryBuilder.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index b4380cc65c..d535914635 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -79,46 +79,11 @@ class GeometryBuilder { if (numPreviousVertices > 0 && !(attr in this.geometry.userAttributes)){ const numMissingValues = size * numPreviousVertices; const missingValues = Array(numMissingValues).fill(0); - console.log(`ATTR: ${attr}, SIZE@ ${size}, NUMMISSINVALS: ${numMissingValues}`); this.geometry.setAttribute(attr, missingValues, size); } - if (this.geometry.userAttributes[attr] != size){ - console.log("This user attribute has different sizes"); - } this.geometry.setAttribute(attr, data, size); } - // const inputUserAttributes = Object.entries(input.userAttributes); - // const currentUserAttributes = Object.entries(this.geometry.userAttributes); - - // if (currentUserAttributes.length > 0){ - // for (const [name, size] of currentUserAttributes){ - // if (name in input.userAttributes){ - // continue; - // } - // const numMissingValues = size * input.vertices.length; - // // this.geometry[name].concat(Array(numMissingValues).fill(0)); - // this.geometry[name] = (this.geometry[name] || []).concat(Array(numMissingValues).fill(0)); - // } - // } - // if (inputUserAttributes.length > 0){ - // for (const [name, size] of inputUserAttributes){ - // const data = input[name]; - // if (!(name in this.geometry.userAttributes) && this.geometry.vertices.length - input.vertices.length > 0){ - // const numMissingValues = size * (this.geometry.vertices.length - input.vertices.length) - size; - // this.geometry.setAttribute(name, Array(size).fill(0)); - // console.log(`ATTR: ${name}, SIZE@ ${size}, NUMMISSINVALS: ${numMissingValues}`); - // this.geometry[name] = this.geometry[name].concat(Array(numMissingValues).fill(0)); - // } - // if (this.geometry.userAttributes[name] != size){ - // console.log("This user attribute has different sizes"); - // } - // for (let i = 0; i < data.length; i+=size){ - // this.geometry.setAttribute(name, data.slice(i, i + size)); - // } - // } - // } - if (this.renderer._doFill) { this.geometry.faces.push( ...input.faces.map(f => f.map(idx => idx + startIdx)) From 04201c524535a5f325ca6d8403b6d7c4e2bca5f1 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:27:27 +0100 Subject: [PATCH 27/94] Early return if the does not have a custom attribute required by the render buffer --- src/webgl/p5.RenderBuffer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/webgl/p5.RenderBuffer.js b/src/webgl/p5.RenderBuffer.js index 146d63e039..96fd2fc5e9 100644 --- a/src/webgl/p5.RenderBuffer.js +++ b/src/webgl/p5.RenderBuffer.js @@ -35,6 +35,9 @@ p5.RenderBuffer = class { // check if the model has the appropriate source array let buffer = geometry[this.dst]; const src = model[this.src]; + if (!src){ + return; + } if (src.length > 0) { // check if we need to create the GL buffer const createBuffer = !buffer; From 68d9132f30beb53fe2b9c12993dc6983d75d74fb Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:28:35 +0100 Subject: [PATCH 28/94] Replaced Object.keys/entries arrays with 'for in' loops and use new setAttribute size parameter to optimize --- src/webgl/p5.RendererGL.Immediate.js | 52 ++++++++++++---------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 1ce2c97b9c..5195d47566 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -121,19 +121,20 @@ p5.RendererGL.prototype.vertex = function(x, y) { const vert = new p5.Vector(x, y, z); this.immediateMode.geometry.vertices.push(vert); this.immediateMode.geometry.vertexNormals.push(this._currentNormal); - if (this._useUserAttributes){ + + for (const attr in this.userAttributes){ const geom = this.immediateMode.geometry; const verts = geom.vertices; - Object.entries(this.userAttributes).forEach(([name, data]) => { - const size = data.length ? data.length : 1; - if (verts.length > 0 && !geom.hasOwnProperty(name)) { - for (let i = 0; i < verts.length - 1; i++) { - this.immediateMode.geometry.setAttribute(name, Array(size).fill(0)); - } - } - this.immediateMode.geometry.setAttribute(name, data); - }); + const data = this.userAttributes[attr]; + const size = data.length ? data.length : 1; + if (!geom.hasOwnProperty(attr) && verts.length > 1) { + const numMissingValues = size * (verts.length - 1); + const missingValues = Array(numMissingValues).fill(0); + geom.setAttribute(attr, missingValues, size); + } + geom.setAttribute(attr, data); } + const vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; this.immediateMode.geometry.vertexColors.push( vertexColor[0], @@ -479,15 +480,12 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals[i].y, this.immediateMode.geometry.vertexNormals[i].z ); - if (this._useUserAttributes){ - const userAttributesArray = Object.entries(this.userAttributes); - for (let [name, data] of userAttributesArray){ - const size = data.length ? data.length : 1; - for (let j = 0; j < size; j++){ - contours[contours.length-1].push( - this.immediateMode.geometry[name][i * size + j] - );} - } + for (const attr in this.userAttributes){ + const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; + const start = i * size; + const end = start + size; + const vals = this.immediateMode.geometry[attr].slice(start, end); + contours[contours.length-1].push(...vals); } } const polyTriangles = this._triangulate(contours); @@ -495,11 +493,8 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertices = []; this.immediateMode.geometry.vertexNormals = []; this.immediateMode.geometry.uvs = []; - if (this._useUserAttributes){ - const userAttributeNames = Object.keys(this.userAttributes); - for (let name of userAttributeNames){ - delete this.immediateMode.geometry[name]; - } + for (const attr in this.userAttributes){ + delete this.immediateMode.geometry[attr] } const colors = []; for ( @@ -509,14 +504,13 @@ p5.RendererGL.prototype._tesselateShape = function() { ) { colors.push(...polyTriangles.slice(j + 5, j + 9)); this.normal(...polyTriangles.slice(j + 9, j + 12)); - if(this._useUserAttributes){ + { let offset = 12; - const userAttributesArray = Object.entries(this.userAttributes); - for (let [name, data] of userAttributesArray){ - const size = data.length ? data.length : 1; + for (const attr in this.userAttributes){ + const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; const start = j + offset; const end = start + size; - this.setAttribute(name, polyTriangles.slice(start, end)); + this.setAttribute(attr, polyTriangles.slice(start, end), size); offset += size; } } From 504cf5aefff369671da9af335b33d83992cd87d8 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 11:45:14 +0100 Subject: [PATCH 29/94] simplify for loop and remove unnecessary check --- src/webgl/p5.RendererGL.Retained.js | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 4351d7c067..630256fe64 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -115,18 +115,17 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { ? model.lineVertices.length / 3 : 0; - if (Object.keys(model.userAttributes).length > 0){ - for (const [name, size] of Object.entries(model.userAttributes)){ - const buff = name.concat('Buffer'); - const bufferExists = this.retainedMode - .buffers - .user - .some(buffer => buffer.dst === buff); - if(!bufferExists){ - this.retainedMode.buffers.user.push( - new p5.RenderBuffer(size, name, buff, name, this) - ); - } + for (const attr in model.userAttributes){ + const buff = attr.concat('Buffer'); + const size = model.userAttributes[attr]; + const bufferExists = this.retainedMode + .buffers + .user + .some(buffer => buffer.dst === buff); + if (!bufferExists){ + this.retainedMode.buffers.user.push( + new p5.RenderBuffer(size, attr, buff, attr, this) + ); } } return buffers; From e64b3f09c2d92c99a39ac52e0e78f2c4acd14213 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 12:46:46 +0100 Subject: [PATCH 30/94] Fixed a bug where if an attribute was set but not applied to any vertices it would not render properly when tessellated --- src/webgl/p5.RendererGL.Immediate.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 5195d47566..5824f06fec 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -484,8 +484,13 @@ p5.RendererGL.prototype._tesselateShape = function() { const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; const start = i * size; const end = start + size; - const vals = this.immediateMode.geometry[attr].slice(start, end); - contours[contours.length-1].push(...vals); + if (this.immediateMode.geometry[attr]){ + const vals = this.immediateMode.geometry[attr].slice(start, end); + contours[contours.length-1].push(...vals); + } else{ + delete this.userAttributes[attr]; + this.tessyVertexSize -= size; + } } } const polyTriangles = this._triangulate(contours); @@ -507,11 +512,11 @@ p5.RendererGL.prototype._tesselateShape = function() { { let offset = 12; for (const attr in this.userAttributes){ - const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; - const start = j + offset; - const end = start + size; - this.setAttribute(attr, polyTriangles.slice(start, end), size); - offset += size; + const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; + const start = j + offset; + const end = start + size; + this.setAttribute(attr, polyTriangles.slice(start, end), size); + offset += size; } } this.vertex(...polyTriangles.slice(j, j + 5)); From f38819b39acc1599227a64236e803844fb5756f1 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 15:35:01 +0100 Subject: [PATCH 31/94] Changed block to backticks in normal documentation --- src/core/shape/vertex.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index a2e02a976b..3e134e7844 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2091,7 +2091,7 @@ p5.prototype.vertex = function(x, y, moveTo, u, v) { * `normal()` will affect all following vertices until `normal()` is called * again: * - * + * ```javascript * beginShape(); * * // Set the vertex normal. @@ -2114,7 +2114,7 @@ p5.prototype.vertex = function(x, y, moveTo, u, v) { * vertex(-30, 30, 0); * * endShape(); - * + * ``` * * @method normal * @param {p5.Vector} vector vertex normal as a p5.Vector object. From f30e3ee8273558ff8495dffd4b8eaeb0d0ee1af6 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 15:37:54 +0100 Subject: [PATCH 32/94] Initial draft of setAttribute() function documentation --- src/core/shape/vertex.js | 71 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 3e134e7844..3f04b8ea3b 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2253,6 +2253,77 @@ p5.prototype.normal = function(x, y, z) { return this; }; +/** Sets the shader's vertex attribute variables. + * + * Shader programs run on the computer's graphics processing unit (GPU) + * They live in a part of the computer's memory that's completely separate from + * the sketch that runs them. Attributes are variables attached to vertices + * within a shader program. They provide a way to attach data to vertices + * and pass values from a sketch running on the CPU to a shader program. + * + * The first parameter, `attributeName`, is a string with the attribute's name. + * + * The second parameter, `data`, is the value that should be assigned to the + * attribute. This value will be applied to subsequent vertices created with + * vertex(). It can be a Number or an array of numbers, + * and in the shader program the type can be interpreted according to the WebGL + * specification. Common types include `float`, `vec2`, `vec3`, `vec4` or matrices. + * + * @example + *
+ * + * let vertSrc = ` + * precision highp float; + * uniform mat4 uModelViewMatrix; + * uniform mat4 uProjectionMatrix; + * + * attribute vec3 aPosition; + * attribute vec2 aOffset; + * + * void main(){ + * vec4 positionVec4 = vec4(aPosition.xyz, 1.0); + * positionVec4.xy += aOffset; + * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4; + * } + * `; + * + * let fragSrc = ` + * precision highp float; + * + * void main(){ + * gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); + * } + * `; + * + * function setup(){ + * createCanvas(100, 100, WEBGL); + * let myShader = createShader(vertSrc, fragSrc); + * shader(myShader); + * noStroke(); + * describe('A wobbly, cyan circle on a grey background.'); + * } + * + * function draw(){ + * background(125); + * beginShape(); + * for (let i = 0; i < 30; i++){ + * let x = 40 * cos(i/30 * TWO_PI); + * let y = 40 * sin(i/30 * TWO_PI); + * let xOff = 10 * noise(x + millis()/1000) - 5; + * let yOff = 10 * noise(y + millis()/1000) - 5; + * setAttribute('aOffset', [xOff, yOff]); + * vertex(x, y); + * } + * endShape(CLOSE); + * } + * + *
+/ +/** + * @method setAttribute + * @param {String} attributeName the name of the vertex attribute. + * @param {Number|Number[]} data the data tied to the vertex attribute. + */ p5.prototype.setAttribute = function(attributeName, data){ // this._assert3d('setAttribute'); // p5._validateParameters('setAttribute', arguments); From fc09ce1505ef35db1372c03cda56dff440708a2a Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 15:41:40 +0100 Subject: [PATCH 33/94] added old documentation files back for compatibility & preview --- docs/documented-method.js | 60 ++++++++ docs/preprocessor.js | 314 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 374 insertions(+) create mode 100644 docs/documented-method.js create mode 100644 docs/preprocessor.js diff --git a/docs/documented-method.js b/docs/documented-method.js new file mode 100644 index 0000000000..1a7e62a283 --- /dev/null +++ b/docs/documented-method.js @@ -0,0 +1,60 @@ +// https://github.com/umdjs/umd/blob/main/templates/returnExports.js +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof module === 'object' && module.exports) { + module.exports = factory(); + } else { + root.DocumentedMethod = factory(); + } + }(this, function () { + function extend(target, src) { + Object.keys(src).forEach(function(prop) { + target[prop] = src[prop]; + }); + return target; + } + + function DocumentedMethod(classitem) { + extend(this, classitem); + + if (this.overloads) { + // Make each overload inherit properties from their parent + // classitem. + this.overloads = this.overloads.map(function(overload) { + return extend(Object.create(this), overload); + }, this); + + if (this.params) { + throw new Error('params for overloaded methods should be undefined'); + } + + this.params = this._getMergedParams(); + } + } + + DocumentedMethod.prototype = { + // Merge parameters across all overloaded versions of this item. + _getMergedParams: function() { + const paramNames = {}; + const params = []; + + this.overloads.forEach(function(overload) { + if (!overload.params) { + return; + } + overload.params.forEach(function(param) { + if (param.name in paramNames) { + return; + } + paramNames[param.name] = param; + params.push(param); + }); + }); + + return params; + } + }; + + return DocumentedMethod; + })); \ No newline at end of file diff --git a/docs/preprocessor.js b/docs/preprocessor.js new file mode 100644 index 0000000000..531b034f28 --- /dev/null +++ b/docs/preprocessor.js @@ -0,0 +1,314 @@ +const marked = require('marked'); +const Entities = require('html-entities').AllHtmlEntities; + +const DocumentedMethod = require('./documented-method'); + +function smokeTestMethods(data) { + data.classitems.forEach(function(classitem) { + if (classitem.itemtype === 'method') { + new DocumentedMethod(classitem); + + if ( + classitem.access !== 'private' && + classitem.file.slice(0, 3) === 'src' && + classitem.name && + !classitem.example + ) { + console.log( + classitem.file + + ':' + + classitem.line + + ': ' + + classitem.itemtype + + ' ' + + classitem.class + + '.' + + classitem.name + + ' missing example' + ); + } + } + }); +} + +function mergeOverloadedMethods(data) { + let methodsByFullName = {}; + let paramsForOverloadedMethods = {}; + + let consts = (data.consts = {}); + + data.classitems = data.classitems.filter(function(classitem) { + if (classitem.access === 'private') { + return false; + } + + const itemClass = data.classes[classitem.class]; + if (!itemClass || itemClass.private) { + return false; + } + + let methodConsts = {}; + + let fullName, method; + + var assertEqual = function(a, b, msg) { + if (a !== b) { + throw new Error( + 'for ' + + fullName + + '() defined in ' + + classitem.file + + ':' + + classitem.line + + ', ' + + msg + + ' (' + + JSON.stringify(a) + + ' !== ' + + JSON.stringify(b) + + ')' + ); + } + }; + + var extractConsts = function(param) { + if (!param.type) { + console.log(param); + } + if (param.type.split('|').indexOf('Constant') >= 0) { + let match; + if (classitem.name === 'endShape' && param.name === 'mode') { + match = 'CLOSE'; + } else { + const constantRe = /either\s+(?:[A-Z0-9_]+\s*,?\s*(?:or)?\s*)+/g; + const execResult = constantRe.exec(param.description); + match = execResult && execResult[0]; + if (!match) { + throw new Error( + classitem.file + + ':' + + classitem.line + + ', Constant-typed parameter ' + + fullName + + '(...' + + param.name + + '...) is missing valid value enumeration. ' + + 'See inline_documentation.md#specify-parameters.' + ); + } + } + if (match) { + const reConst = /[A-Z0-9_]+/g; + let matchConst; + while ((matchConst = reConst.exec(match)) !== null) { + methodConsts[matchConst] = true; + } + } + } + }; + + var processOverloadedParams = function(params) { + let paramNames; + + if (!(fullName in paramsForOverloadedMethods)) { + paramsForOverloadedMethods[fullName] = {}; + } + + paramNames = paramsForOverloadedMethods[fullName]; + + params.forEach(function(param) { + const origParam = paramNames[param.name]; + + if (origParam) { + assertEqual( + origParam.type, + param.type, + 'types for param "' + + param.name + + '" must match ' + + 'across all overloads' + ); + assertEqual( + param.description, + '', + 'description for param "' + + param.name + + '" should ' + + 'only be defined in its first use; subsequent ' + + 'overloads should leave it empty' + ); + } else { + paramNames[param.name] = param; + extractConsts(param); + } + }); + + return params; + }; + + if (classitem.itemtype && classitem.itemtype === 'method') { + fullName = classitem.class + '.' + classitem.name; + if (fullName in methodsByFullName) { + // It's an overloaded version of a method that we've already + // indexed. We need to make sure that we don't list it multiple + // times in our index pages and such. + + method = methodsByFullName[fullName]; + + assertEqual( + method.file, + classitem.file, + 'all overloads must be defined in the same file' + ); + assertEqual( + method.module, + classitem.module, + 'all overloads must be defined in the same module' + ); + assertEqual( + method.submodule, + classitem.submodule, + 'all overloads must be defined in the same submodule' + ); + assertEqual( + classitem.description || '', + '', + 'additional overloads should have no description' + ); + + var makeOverload = function(method) { + const overload = { + line: method.line, + params: processOverloadedParams(method.params || []) + }; + // TODO: the doc renderer assumes (incorrectly) that + // these are the same for all overrides + if (method.static) overload.static = method.static; + if (method.chainable) overload.chainable = method.chainable; + if (method.return) overload.return = method.return; + return overload; + }; + + if (!method.overloads) { + method.overloads = [makeOverload(method)]; + delete method.params; + } + method.overloads.push(makeOverload(classitem)); + return false; + } else { + if (classitem.params) { + classitem.params.forEach(function(param) { + extractConsts(param); + }); + } + methodsByFullName[fullName] = classitem; + } + + Object.keys(methodConsts).forEach(constName => + (consts[constName] || (consts[constName] = [])).push(fullName) + ); + } + return true; + }); +} + +// build a copy of data.json for the FES, restructured for object lookup on +// classitems and removing all the parts not needed by the FES +function buildParamDocs(docs) { + let newClassItems = {}; + // the fields we need for the FES, discard everything else + let allowed = new Set(['name', 'class', 'module', 'params', 'overloads']); + for (let classitem of docs.classitems) { + if (classitem.name && classitem.class) { + for (let key in classitem) { + if (!allowed.has(key)) { + delete classitem[key]; + } + } + if (classitem.hasOwnProperty('overloads')) { + for (let overload of classitem.overloads) { + // remove line number and return type + if (overload.line) { + delete overload.line; + } + + if (overload.return) { + delete overload.return; + } + } + } + if (!newClassItems[classitem.class]) { + newClassItems[classitem.class] = {}; + } + + newClassItems[classitem.class][classitem.name] = classitem; + } + } + + let fs = require('fs'); + let path = require('path'); + let out = fs.createWriteStream( + path.join(process.cwd(), 'docs', 'parameterData.json'), + { + flags: 'w', + mode: '0644' + } + ); + out.write(JSON.stringify(newClassItems, null, 2)); + out.end(); +} + +function renderItemDescriptionsAsMarkdown(item) { + if (item.description) { + const entities = new Entities(); + item.description = entities.decode(marked.parse(item.description)); + } + if (item.params) { + item.params.forEach(renderItemDescriptionsAsMarkdown); + } +} + +function renderDescriptionsAsMarkdown(data) { + Object.keys(data.modules).forEach(function(moduleName) { + renderItemDescriptionsAsMarkdown(data.modules[moduleName]); + }); + Object.keys(data.classes).forEach(function(className) { + renderItemDescriptionsAsMarkdown(data.classes[className]); + }); + data.classitems.forEach(function(classitem) { + renderItemDescriptionsAsMarkdown(classitem); + }); +} + +module.exports = (data, options) => { + data.classitems + .filter( + ci => !ci.itemtype && (ci.params || ci.return) && ci.access !== 'private' + ) + .forEach(ci => { + console.error(ci.file + ':' + ci.line + ': unnamed public member'); + }); + + Object.keys(data.classes) + .filter(k => data.classes[k].access === 'private') + .forEach(k => delete data.classes[k]); + + renderDescriptionsAsMarkdown(data); + mergeOverloadedMethods(data); + smokeTestMethods(data); + buildParamDocs(JSON.parse(JSON.stringify(data))); +}; + +module.exports.mergeOverloadedMethods = mergeOverloadedMethods; +module.exports.renderDescriptionsAsMarkdown = renderDescriptionsAsMarkdown; + +module.exports.register = (Handlebars, options) => { + Handlebars.registerHelper('root', function(context, options) { + // if (this.language === 'en') { + // return ''; + // } else { + // return '/'+this.language; + // } + return window.location.pathname; + }); +}; \ No newline at end of file From d308bd8bb29c57295c59ae6c477df26927447922 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 16:26:42 +0100 Subject: [PATCH 34/94] add custom vertex attributes to the 'TESS preserves vertex data' test --- test/unit/webgl/p5.RendererGL.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 66e9008713..1bc4ed6e0a 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -1577,15 +1577,19 @@ suite('p5.RendererGL', function() { renderer.beginShape(myp5.TESS); renderer.fill(255, 255, 255); renderer.normal(-1, -1, 1); + renderer.setAttribute('aCustom', [1, 1, 1]) renderer.vertex(-10, -10, 0, 0); renderer.fill(255, 0, 0); renderer.normal(1, -1, 1); + renderer.setAttribute('aCustom', [1, 0, 0]) renderer.vertex(10, -10, 1, 0); renderer.fill(0, 255, 0); renderer.normal(1, 1, 1); + renderer.setAttribute('aCustom', [0, 1, 0]) renderer.vertex(10, 10, 1, 1); renderer.fill(0, 0, 255); renderer.normal(-1, 1, 1); + renderer.setAttribute('aCustom', [0, 0, 1]) renderer.vertex(-10, 10, 0, 1); renderer.endShape(myp5.CLOSE); @@ -1641,6 +1645,15 @@ suite('p5.RendererGL', function() { [1, 1, 1] ); + assert.deepEqual(renderer.immediateMode.geometry.aCustom, [ + 1, 0, 0, + 0, 0, 1, + 1, 1, 1, + 0, 0, 1, + 1, 0, 0, + 0, 1, 0 + ]); + assert.deepEqual(renderer.immediateMode.geometry.vertexColors, [ 1, 0, 0, 1, 0, 0, 1, 1, From cf0383603b51a8f17cfc8a1359334711a6345036 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 16:33:59 +0100 Subject: [PATCH 35/94] delete custom attributes on reset, change Object.keys to 'for in' loop --- src/webgl/p5.Geometry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 6e41e06efa..f9f93d7306 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -452,8 +452,8 @@ p5.Geometry = class Geometry { this.vertexNormals.length = 0; this.uvs.length = 0; - for (const attr of Object.keys(this.userAttributes)){ - delete this[attr.name]; + for (const attr in this.userAttributes){ + delete this[attr]; } this.userAttributes = {}; From 9f91d519b727436458c33011a4aa6b82296969b3 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 16:59:47 +0100 Subject: [PATCH 36/94] Add check in case there is an incorrect number of attributes on a geometry --- src/webgl/p5.RendererGL.Retained.js | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 630256fe64..3bebcd3d0a 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -152,10 +152,18 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { for (const buff of this.retainedMode.buffers.fill) { buff._prepareBuffer(geometry, fillShader); } - if (Object.keys(geometry.model.userAttributes).length > 0){ - for (const buff of this.retainedMode.buffers.user){ - buff._prepareBuffer(geometry, fillShader); + for (const buff of this.retainedMode.buffers.user){ + if(!geometry.model[buff.src]){ + continue; } + const adjustedLength = geometry.model[buff.src].length / buff.size; + if(adjustedLength != geometry.model.vertices.length){ + p5._friendlyError(`One of the geometries has a custom attribute with + either too many or too few values compared to vertices. + There may be unexpected results from the shaders. + `, 'setAttribute()'); + } + buff._prepareBuffer(geometry, fillShader); } fillShader.disableRemainingAttributes(); if (geometry.indexBuffer) { @@ -177,10 +185,18 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { for (const buff of this.retainedMode.buffers.stroke) { buff._prepareBuffer(geometry, strokeShader); } - if (Object.keys(geometry.model.userAttributes).length > 0){ - for (const buff of this.retainedMode.buffers.user){ - buff._prepareBuffer(geometry, strokeShader); + for (const buff of this.retainedMode.buffers.user){ + if(!geometry.model[buff.src]){ + continue; } + const adjustedLength = geometry.model[buff.src].length / buff.size; + if(adjustedLength != geometry.model.vertices.length){ + p5._friendlyError(`One of the geometries has a custom attribute with + either too many or too few values compared to vertices. + There may be unexpected results from the shaders. + `, 'setAttribute()'); + } + buff._prepareBuffer(geometry, strokeShader); } strokeShader.disableRemainingAttributes(); this._applyColorBlend( From fc47dcbf4a91b16b74cb7678eb6c97741694d481 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 17:00:10 +0100 Subject: [PATCH 37/94] remove redundant checks --- src/webgl/p5.RendererGL.Immediate.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 5824f06fec..1643e9bfe3 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -589,10 +589,8 @@ p5.RendererGL.prototype._drawImmediateFill = function(count = 1) { for (const buff of this.immediateMode.buffers.fill) { buff._prepareBuffer(this.immediateMode.geometry, shader); } - if (this._useUserAttributes){ - for (const buff of this.immediateMode.buffers.user){ - buff._prepareBuffer(this.immediateMode.geometry, shader); - } + for (const buff of this.immediateMode.buffers.user){ + buff._prepareBuffer(this.immediateMode.geometry, shader); } shader.disableRemainingAttributes(); @@ -640,10 +638,8 @@ p5.RendererGL.prototype._drawImmediateStroke = function() { for (const buff of this.immediateMode.buffers.stroke) { buff._prepareBuffer(this.immediateMode.geometry, shader); } - if (this._useUserAttributes){ - for (const buff of this.immediateMode.buffers.user){ - buff._prepareBuffer(this.immediateMode.geometry, shader); - } + for (const buff of this.immediateMode.buffers.user){ + buff._prepareBuffer(this.immediateMode.geometry, shader); } shader.disableRemainingAttributes(); this._applyColorBlend( From 874c363d1f3c2f6e2eed911d8dbee20999e8da4d Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 23:35:49 +0100 Subject: [PATCH 38/94] preparing to add immediate buffer strides re:custom attributes --- src/webgl/p5.RendererGL.Immediate.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 1643e9bfe3..78734d9d58 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -46,7 +46,7 @@ p5.RendererGL.prototype.beginShape = function(mode) { return this; }; -const immediateBufferStrides = { +p5.RendererGL.prototype.immediateBufferStrides = { vertices: 1, vertexNormals: 1, vertexColors: 4, @@ -86,8 +86,8 @@ p5.RendererGL.prototype.vertex = function(x, y) { // 1--2 1--2 4 // When vertex index 3 is being added, add the necessary duplicates. if (this.immediateMode.geometry.vertices.length % 6 === 3) { - for (const key in immediateBufferStrides) { - const stride = immediateBufferStrides[key]; + for (const key in this.immediateBufferStrides) { + const stride = this.immediateBufferStrides[key]; const buffer = this.immediateMode.geometry[key]; buffer.push( ...buffer.slice( From 27610ecb7b795b642428202a20ae8bd48cd07dde Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 23 Sep 2024 23:36:46 +0100 Subject: [PATCH 39/94] comment to remind me to convert custom attributes edgesToVertices --- src/webgl/p5.Geometry.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index f9f93d7306..45a2127996 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1599,6 +1599,7 @@ p5.Geometry = class Geometry { * @chainable */ _edgesToVertices() { + // probably needs to add something in here for custom attributes this.lineVertices.clear(); this.lineTangentsIn.clear(); this.lineTangentsOut.clear(); From 103d6eb69b35cf1d1ccaba120dd2b9cc131a42fe Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 11:42:04 +0100 Subject: [PATCH 40/94] update the libtess combineCallback to handle custom attributes/ vertex sizes --- src/webgl/p5.RendererGL.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 6834cf0ece..3eb6a69b84 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -671,7 +671,8 @@ p5.RendererGL = class RendererGL extends Renderer { // Used to distinguish between user calls to vertex() and internal calls this.isProcessingVertices = false; - this._tessy = this._initTessy(); + this.tessyVertexSize = 12; + this._tessy = this._initTessy(this.tessyVertexSize); this.fontInfos = {}; @@ -2417,7 +2418,7 @@ p5.RendererGL = class RendererGL extends Renderer { const p = [p1, p2, p3, p4]; return p; } - _initTessy() { + _initTessy(tessyVertexSize) { // function called for each vertex of tesselator output function vertexCallback(data, polyVertArray) { for (const element of data) { @@ -2437,7 +2438,7 @@ p5.RendererGL = class RendererGL extends Renderer { } // callback for when segments intersect and must be split function combinecallback(coords, data, weight) { - const result = new Array(p5.RendererGL.prototype.tessyVertexSize).fill(0); + const result = new Array(tessyVertexSize).fill(0); for (let i = 0; i < weight.length; i++) { for (let j = 0; j < result.length; j++) { if (weight[i] === 0 || !data[i]) continue; @@ -2527,8 +2528,4 @@ p5.prototype._assert3d = function (name) { ); }; -// function to initialize GLU Tesselator - -p5.RendererGL.prototype.tessyVertexSize = 12; - export default p5.RendererGL; From e004a8d12fe434f23d5d27610b62a00bc194f170 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 11:42:54 +0100 Subject: [PATCH 41/94] rebuild the tesselator in case tessyvertexsize changes (custom attributes) --- src/webgl/p5.RendererGL.Immediate.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 78734d9d58..bcc13d936d 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -41,6 +41,7 @@ p5.RendererGL.prototype.beginShape = function(mode) { this._useUserAttributes = false; } this.tessyVertexSize = 12; + this.tessy = this._initTessy(this.tessyVertexSize); this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; return this; @@ -456,6 +457,9 @@ p5.RendererGL.prototype._calculateEdges = function( */ p5.RendererGL.prototype._tesselateShape = function() { // TODO: handle non-TESS shape modes that have contours + if (this.tessyVertexSize > 12){ + this._tessy = this._initTessy(this.tessyVertexSize); + } this.immediateMode.shapeMode = constants.TRIANGLES; const contours = [[]]; for (let i = 0; i < this.immediateMode.geometry.vertices.length; i++) { @@ -490,6 +494,7 @@ p5.RendererGL.prototype._tesselateShape = function() { } else{ delete this.userAttributes[attr]; this.tessyVertexSize -= size; + this._tessy = this._initTessy(this.tessyVertexSize); } } } @@ -499,7 +504,7 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals = []; this.immediateMode.geometry.uvs = []; for (const attr in this.userAttributes){ - delete this.immediateMode.geometry[attr] + this.immediateMode.geometry[attr] = []; } const colors = []; for ( From 7bae702f908331f1179dba93dcb729bb17fcc2f5 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 12:16:54 +0100 Subject: [PATCH 42/94] Added a function to update the libtess tesselator's combine callback in case custom attributes have been added --- src/webgl/p5.RendererGL.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 3eb6a69b84..7b604b6e36 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -671,8 +671,7 @@ p5.RendererGL = class RendererGL extends Renderer { // Used to distinguish between user calls to vertex() and internal calls this.isProcessingVertices = false; - this.tessyVertexSize = 12; - this._tessy = this._initTessy(this.tessyVertexSize); + this._tessy = this._initTessy(); this.fontInfos = {}; @@ -2418,7 +2417,7 @@ p5.RendererGL = class RendererGL extends Renderer { const p = [p1, p2, p3, p4]; return p; } - _initTessy(tessyVertexSize) { + _initTessy() { // function called for each vertex of tesselator output function vertexCallback(data, polyVertArray) { for (const element of data) { @@ -2438,7 +2437,7 @@ p5.RendererGL = class RendererGL extends Renderer { } // callback for when segments intersect and must be split function combinecallback(coords, data, weight) { - const result = new Array(tessyVertexSize).fill(0); + const result = new Array(p5.RendererGL.prototype.tessyVertexSize).fill(0); for (let i = 0; i < weight.length; i++) { for (let j = 0; j < result.length; j++) { if (weight[i] === 0 || !data[i]) continue; @@ -2466,6 +2465,23 @@ p5.RendererGL = class RendererGL extends Renderer { return tessy; } + _updateTessyCombineCallback() { + // If custom attributes have been used, the vertex data which needs to be + // combined has changed, so libtess must have its combine callback updated + const combinecallback = (coords, data, weight) => { + const result = new Array(this.tessyVertexSize).fill(0); + for (let i = 0; i < weight.length; i++) { + for (let j = 0; j < result.length; j++) { + if (weight[i] === 0 || !data[i]) continue; + result[j] += data[i][j] * weight[i]; + } + } + return result; + }; + + this._tessy.gluTessCallback(libtess.gluEnum.GLU_TESS_COMBINE, combinecallback); + } + _triangulate(contours) { // libtess will take 3d verts and flatten to a plane for tesselation. // libtess is capable of calculating a plane to tesselate on, but @@ -2528,4 +2544,8 @@ p5.prototype._assert3d = function (name) { ); }; +// Initial vertex data size for libtess + +p5.RendererGL.prototype.tessyVertexSize = 12; + export default p5.RendererGL; From c3ad1fc9199852ffc6624f81f8bb7ccad0c78c10 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 12:17:54 +0100 Subject: [PATCH 43/94] update the tesselators combine callback instead of reinitializing the entire tessellator, and only if necessary --- src/webgl/p5.RendererGL.Immediate.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index bcc13d936d..9c221ddfe9 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -40,8 +40,10 @@ p5.RendererGL.prototype.beginShape = function(mode) { delete this.userAttributes; this._useUserAttributes = false; } - this.tessyVertexSize = 12; - this.tessy = this._initTessy(this.tessyVertexSize); + if (this.tessyVertexSize > 12){ + this.tessyVertexSize = 12; + this._updateTessyCombineCallback(); + } this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; return this; @@ -457,9 +459,6 @@ p5.RendererGL.prototype._calculateEdges = function( */ p5.RendererGL.prototype._tesselateShape = function() { // TODO: handle non-TESS shape modes that have contours - if (this.tessyVertexSize > 12){ - this._tessy = this._initTessy(this.tessyVertexSize); - } this.immediateMode.shapeMode = constants.TRIANGLES; const contours = [[]]; for (let i = 0; i < this.immediateMode.geometry.vertices.length; i++) { @@ -494,10 +493,12 @@ p5.RendererGL.prototype._tesselateShape = function() { } else{ delete this.userAttributes[attr]; this.tessyVertexSize -= size; - this._tessy = this._initTessy(this.tessyVertexSize); } } } + if (this.tessyVertexSize > 12){ + this._updateTessyCombineCallback(); + } const polyTriangles = this._triangulate(contours); const originalVertices = this.immediateMode.geometry.vertices; this.immediateMode.geometry.vertices = []; From 5bb41b5bfc6d96fe28ae1e63837597e61f29e160 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 12:32:34 +0100 Subject: [PATCH 44/94] remove old scripts --- docs/documented-method.js | 60 -------- docs/preprocessor.js | 314 -------------------------------------- 2 files changed, 374 deletions(-) delete mode 100644 docs/documented-method.js delete mode 100644 docs/preprocessor.js diff --git a/docs/documented-method.js b/docs/documented-method.js deleted file mode 100644 index 1a7e62a283..0000000000 --- a/docs/documented-method.js +++ /dev/null @@ -1,60 +0,0 @@ -// https://github.com/umdjs/umd/blob/main/templates/returnExports.js -(function (root, factory) { - if (typeof define === 'function' && define.amd) { - define([], factory); - } else if (typeof module === 'object' && module.exports) { - module.exports = factory(); - } else { - root.DocumentedMethod = factory(); - } - }(this, function () { - function extend(target, src) { - Object.keys(src).forEach(function(prop) { - target[prop] = src[prop]; - }); - return target; - } - - function DocumentedMethod(classitem) { - extend(this, classitem); - - if (this.overloads) { - // Make each overload inherit properties from their parent - // classitem. - this.overloads = this.overloads.map(function(overload) { - return extend(Object.create(this), overload); - }, this); - - if (this.params) { - throw new Error('params for overloaded methods should be undefined'); - } - - this.params = this._getMergedParams(); - } - } - - DocumentedMethod.prototype = { - // Merge parameters across all overloaded versions of this item. - _getMergedParams: function() { - const paramNames = {}; - const params = []; - - this.overloads.forEach(function(overload) { - if (!overload.params) { - return; - } - overload.params.forEach(function(param) { - if (param.name in paramNames) { - return; - } - paramNames[param.name] = param; - params.push(param); - }); - }); - - return params; - } - }; - - return DocumentedMethod; - })); \ No newline at end of file diff --git a/docs/preprocessor.js b/docs/preprocessor.js deleted file mode 100644 index 531b034f28..0000000000 --- a/docs/preprocessor.js +++ /dev/null @@ -1,314 +0,0 @@ -const marked = require('marked'); -const Entities = require('html-entities').AllHtmlEntities; - -const DocumentedMethod = require('./documented-method'); - -function smokeTestMethods(data) { - data.classitems.forEach(function(classitem) { - if (classitem.itemtype === 'method') { - new DocumentedMethod(classitem); - - if ( - classitem.access !== 'private' && - classitem.file.slice(0, 3) === 'src' && - classitem.name && - !classitem.example - ) { - console.log( - classitem.file + - ':' + - classitem.line + - ': ' + - classitem.itemtype + - ' ' + - classitem.class + - '.' + - classitem.name + - ' missing example' - ); - } - } - }); -} - -function mergeOverloadedMethods(data) { - let methodsByFullName = {}; - let paramsForOverloadedMethods = {}; - - let consts = (data.consts = {}); - - data.classitems = data.classitems.filter(function(classitem) { - if (classitem.access === 'private') { - return false; - } - - const itemClass = data.classes[classitem.class]; - if (!itemClass || itemClass.private) { - return false; - } - - let methodConsts = {}; - - let fullName, method; - - var assertEqual = function(a, b, msg) { - if (a !== b) { - throw new Error( - 'for ' + - fullName + - '() defined in ' + - classitem.file + - ':' + - classitem.line + - ', ' + - msg + - ' (' + - JSON.stringify(a) + - ' !== ' + - JSON.stringify(b) + - ')' - ); - } - }; - - var extractConsts = function(param) { - if (!param.type) { - console.log(param); - } - if (param.type.split('|').indexOf('Constant') >= 0) { - let match; - if (classitem.name === 'endShape' && param.name === 'mode') { - match = 'CLOSE'; - } else { - const constantRe = /either\s+(?:[A-Z0-9_]+\s*,?\s*(?:or)?\s*)+/g; - const execResult = constantRe.exec(param.description); - match = execResult && execResult[0]; - if (!match) { - throw new Error( - classitem.file + - ':' + - classitem.line + - ', Constant-typed parameter ' + - fullName + - '(...' + - param.name + - '...) is missing valid value enumeration. ' + - 'See inline_documentation.md#specify-parameters.' - ); - } - } - if (match) { - const reConst = /[A-Z0-9_]+/g; - let matchConst; - while ((matchConst = reConst.exec(match)) !== null) { - methodConsts[matchConst] = true; - } - } - } - }; - - var processOverloadedParams = function(params) { - let paramNames; - - if (!(fullName in paramsForOverloadedMethods)) { - paramsForOverloadedMethods[fullName] = {}; - } - - paramNames = paramsForOverloadedMethods[fullName]; - - params.forEach(function(param) { - const origParam = paramNames[param.name]; - - if (origParam) { - assertEqual( - origParam.type, - param.type, - 'types for param "' + - param.name + - '" must match ' + - 'across all overloads' - ); - assertEqual( - param.description, - '', - 'description for param "' + - param.name + - '" should ' + - 'only be defined in its first use; subsequent ' + - 'overloads should leave it empty' - ); - } else { - paramNames[param.name] = param; - extractConsts(param); - } - }); - - return params; - }; - - if (classitem.itemtype && classitem.itemtype === 'method') { - fullName = classitem.class + '.' + classitem.name; - if (fullName in methodsByFullName) { - // It's an overloaded version of a method that we've already - // indexed. We need to make sure that we don't list it multiple - // times in our index pages and such. - - method = methodsByFullName[fullName]; - - assertEqual( - method.file, - classitem.file, - 'all overloads must be defined in the same file' - ); - assertEqual( - method.module, - classitem.module, - 'all overloads must be defined in the same module' - ); - assertEqual( - method.submodule, - classitem.submodule, - 'all overloads must be defined in the same submodule' - ); - assertEqual( - classitem.description || '', - '', - 'additional overloads should have no description' - ); - - var makeOverload = function(method) { - const overload = { - line: method.line, - params: processOverloadedParams(method.params || []) - }; - // TODO: the doc renderer assumes (incorrectly) that - // these are the same for all overrides - if (method.static) overload.static = method.static; - if (method.chainable) overload.chainable = method.chainable; - if (method.return) overload.return = method.return; - return overload; - }; - - if (!method.overloads) { - method.overloads = [makeOverload(method)]; - delete method.params; - } - method.overloads.push(makeOverload(classitem)); - return false; - } else { - if (classitem.params) { - classitem.params.forEach(function(param) { - extractConsts(param); - }); - } - methodsByFullName[fullName] = classitem; - } - - Object.keys(methodConsts).forEach(constName => - (consts[constName] || (consts[constName] = [])).push(fullName) - ); - } - return true; - }); -} - -// build a copy of data.json for the FES, restructured for object lookup on -// classitems and removing all the parts not needed by the FES -function buildParamDocs(docs) { - let newClassItems = {}; - // the fields we need for the FES, discard everything else - let allowed = new Set(['name', 'class', 'module', 'params', 'overloads']); - for (let classitem of docs.classitems) { - if (classitem.name && classitem.class) { - for (let key in classitem) { - if (!allowed.has(key)) { - delete classitem[key]; - } - } - if (classitem.hasOwnProperty('overloads')) { - for (let overload of classitem.overloads) { - // remove line number and return type - if (overload.line) { - delete overload.line; - } - - if (overload.return) { - delete overload.return; - } - } - } - if (!newClassItems[classitem.class]) { - newClassItems[classitem.class] = {}; - } - - newClassItems[classitem.class][classitem.name] = classitem; - } - } - - let fs = require('fs'); - let path = require('path'); - let out = fs.createWriteStream( - path.join(process.cwd(), 'docs', 'parameterData.json'), - { - flags: 'w', - mode: '0644' - } - ); - out.write(JSON.stringify(newClassItems, null, 2)); - out.end(); -} - -function renderItemDescriptionsAsMarkdown(item) { - if (item.description) { - const entities = new Entities(); - item.description = entities.decode(marked.parse(item.description)); - } - if (item.params) { - item.params.forEach(renderItemDescriptionsAsMarkdown); - } -} - -function renderDescriptionsAsMarkdown(data) { - Object.keys(data.modules).forEach(function(moduleName) { - renderItemDescriptionsAsMarkdown(data.modules[moduleName]); - }); - Object.keys(data.classes).forEach(function(className) { - renderItemDescriptionsAsMarkdown(data.classes[className]); - }); - data.classitems.forEach(function(classitem) { - renderItemDescriptionsAsMarkdown(classitem); - }); -} - -module.exports = (data, options) => { - data.classitems - .filter( - ci => !ci.itemtype && (ci.params || ci.return) && ci.access !== 'private' - ) - .forEach(ci => { - console.error(ci.file + ':' + ci.line + ': unnamed public member'); - }); - - Object.keys(data.classes) - .filter(k => data.classes[k].access === 'private') - .forEach(k => delete data.classes[k]); - - renderDescriptionsAsMarkdown(data); - mergeOverloadedMethods(data); - smokeTestMethods(data); - buildParamDocs(JSON.parse(JSON.stringify(data))); -}; - -module.exports.mergeOverloadedMethods = mergeOverloadedMethods; -module.exports.renderDescriptionsAsMarkdown = renderDescriptionsAsMarkdown; - -module.exports.register = (Handlebars, options) => { - Handlebars.registerHelper('root', function(context, options) { - // if (this.language === 'en') { - // return ''; - // } else { - // return '/'+this.language; - // } - return window.location.pathname; - }); -}; \ No newline at end of file From 3ffe8cce0090db04cadc5c0eb46de6245098a904 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 15:05:29 +0100 Subject: [PATCH 45/94] convert tessy combinecallback to arrow function so that custom attributes can work with overlap vertices --- src/webgl/p5.RendererGL.js | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 7b604b6e36..ec7d52e1c6 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -671,6 +671,7 @@ p5.RendererGL = class RendererGL extends Renderer { // Used to distinguish between user calls to vertex() and internal calls this.isProcessingVertices = false; + this.tessyVertexSize = 12; this._tessy = this._initTessy(); this.fontInfos = {}; @@ -2436,8 +2437,8 @@ p5.RendererGL = class RendererGL extends Renderer { console.log(`error number: ${errno}`); } // callback for when segments intersect and must be split - function combinecallback(coords, data, weight) { - const result = new Array(p5.RendererGL.prototype.tessyVertexSize).fill(0); + const combinecallback = (coords, data, weight) => { + const result = new Array(this.tessyVertexSize).fill(0); for (let i = 0; i < weight.length; i++) { for (let j = 0; j < result.length; j++) { if (weight[i] === 0 || !data[i]) continue; @@ -2445,7 +2446,7 @@ p5.RendererGL = class RendererGL extends Renderer { } } return result; - } + }; function edgeCallback(flag) { // don't really care about the flag, but need no-strip/no-fan behavior @@ -2465,23 +2466,6 @@ p5.RendererGL = class RendererGL extends Renderer { return tessy; } - _updateTessyCombineCallback() { - // If custom attributes have been used, the vertex data which needs to be - // combined has changed, so libtess must have its combine callback updated - const combinecallback = (coords, data, weight) => { - const result = new Array(this.tessyVertexSize).fill(0); - for (let i = 0; i < weight.length; i++) { - for (let j = 0; j < result.length; j++) { - if (weight[i] === 0 || !data[i]) continue; - result[j] += data[i][j] * weight[i]; - } - } - return result; - }; - - this._tessy.gluTessCallback(libtess.gluEnum.GLU_TESS_COMBINE, combinecallback); - } - _triangulate(contours) { // libtess will take 3d verts and flatten to a plane for tesselation. // libtess is capable of calculating a plane to tesselate on, but @@ -2544,8 +2528,4 @@ p5.prototype._assert3d = function (name) { ); }; -// Initial vertex data size for libtess - -p5.RendererGL.prototype.tessyVertexSize = 12; - -export default p5.RendererGL; +export default p5.RendererGL; \ No newline at end of file From afdf29d5e021a6a1a3715070c55430b0bf4790f5 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 24 Sep 2024 15:05:59 +0100 Subject: [PATCH 46/94] remove redundant function calls to update tessy combine callback --- src/webgl/p5.RendererGL.Immediate.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 9c221ddfe9..7b8360efd7 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -42,7 +42,6 @@ p5.RendererGL.prototype.beginShape = function(mode) { } if (this.tessyVertexSize > 12){ this.tessyVertexSize = 12; - this._updateTessyCombineCallback(); } this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; @@ -496,9 +495,6 @@ p5.RendererGL.prototype._tesselateShape = function() { } } } - if (this.tessyVertexSize > 12){ - this._updateTessyCombineCallback(); - } const polyTriangles = this._triangulate(contours); const originalVertices = this.immediateMode.geometry.vertices; this.immediateMode.geometry.vertices = []; From 383fe8ea230f1aa5ca157d97ddcdb0f2af620fa9 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Thu, 26 Sep 2024 11:45:43 +0100 Subject: [PATCH 47/94] Changed user attributes source array on geometry to include 'Src' on the end. This avoids name clashes with default p5 attributes. --- src/webgl/GeometryBuilder.js | 3 ++- src/webgl/p5.Geometry.js | 12 +++++++----- src/webgl/p5.RendererGL.Immediate.js | 16 ++++++++++------ src/webgl/p5.RendererGL.Retained.js | 3 ++- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index d535914635..0bc98e1b99 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -74,7 +74,8 @@ class GeometryBuilder { this.geometry.setAttribute(attr, missingValues, size); } for (const attr in inputAttrs){ - const data = input[attr]; + const src = attr.concat('Src'); + const data = input[src]; const size = inputAttrs[attr]; if (numPreviousVertices > 0 && !(attr in this.geometry.userAttributes)){ const numMissingValues = size * numPreviousVertices; diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 45a2127996..b1f6bc08d3 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -453,7 +453,8 @@ p5.Geometry = class Geometry { this.uvs.length = 0; for (const attr in this.userAttributes){ - delete this[attr]; + const src = attr.concat('Src'); + delete this[src]; } this.userAttributes = {}; @@ -1920,8 +1921,9 @@ p5.Geometry = class Geometry { } setAttribute(attributeName, data, size = data.length ? data.length : 1){ - if (!this.hasOwnProperty(attributeName)){ - this[attributeName] = []; + const attributeSrc = attributeName.concat('Src'); + if (!this.hasOwnProperty(attributeSrc)){ + this[attributeSrc] = []; this.userAttributes[attributeName] = size; } if (size != this.userAttributes[attributeName]){ @@ -1929,9 +1931,9 @@ p5.Geometry = class Geometry { or if it was an accident, set ${attributeName} to have the same number of inputs each time!`, 'setAttribute()'); } if (data.length){ - this[attributeName].push(...data); + this[attributeSrc].push(...data); } else{ - this[attributeName].push(data); + this[attributeSrc].push(data); } } }; diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 7b8360efd7..72d4039346 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -128,8 +128,9 @@ p5.RendererGL.prototype.vertex = function(x, y) { const geom = this.immediateMode.geometry; const verts = geom.vertices; const data = this.userAttributes[attr]; + const src = attr.concat('Src'); const size = data.length ? data.length : 1; - if (!geom.hasOwnProperty(attr) && verts.length > 1) { + if (!geom.hasOwnProperty(src) && verts.length > 1) { const numMissingValues = size * (verts.length - 1); const missingValues = Array(numMissingValues).fill(0); geom.setAttribute(attr, missingValues, size); @@ -199,15 +200,16 @@ p5.RendererGL.prototype.setAttribute = function(attributeName, data){ if (!this.userAttributes.hasOwnProperty(attributeName)){ this.tessyVertexSize += size; } - this.userAttributes[attributeName] = data; const buff = attributeName.concat('Buffer'); + const attributeSrc = attributeName.concat('Src'); + this.userAttributes[attributeName] = data; const bufferExists = this.immediateMode .buffers .user .some(buffer => buffer.dst === buff); if(!bufferExists){ this.immediateMode.buffers.user.push( - new p5.RenderBuffer(size, attributeName, buff, attributeName, this) + new p5.RenderBuffer(size, attributeSrc, buff, attributeName, this) ); } }; @@ -483,11 +485,12 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals[i].z ); for (const attr in this.userAttributes){ + const attributeSrc = attr.concat('Src'); const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; const start = i * size; const end = start + size; - if (this.immediateMode.geometry[attr]){ - const vals = this.immediateMode.geometry[attr].slice(start, end); + if (this.immediateMode.geometry[attributeSrc]){ + const vals = this.immediateMode.geometry[attributeSrc].slice(start, end); contours[contours.length-1].push(...vals); } else{ delete this.userAttributes[attr]; @@ -501,7 +504,8 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals = []; this.immediateMode.geometry.uvs = []; for (const attr in this.userAttributes){ - this.immediateMode.geometry[attr] = []; + const attributeSrc = attr.concat('Src'); + this.immediateMode.geometry[attributeSrc] = []; } const colors = []; for ( diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 3bebcd3d0a..37bf95457d 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -117,6 +117,7 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { for (const attr in model.userAttributes){ const buff = attr.concat('Buffer'); + const attributeSrc = attr.concat('Src'); const size = model.userAttributes[attr]; const bufferExists = this.retainedMode .buffers @@ -124,7 +125,7 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { .some(buffer => buffer.dst === buff); if (!bufferExists){ this.retainedMode.buffers.user.push( - new p5.RenderBuffer(size, attr, buff, attr, this) + new p5.RenderBuffer(size, attributeSrc, buff, attr, this) ); } } From c2fd8b7609101808776076e2775af2bac13e1fa0 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Thu, 26 Sep 2024 12:30:51 +0100 Subject: [PATCH 48/94] interpolate custom attributes on quadratic curves --- src/webgl/3d_primitives.js | 52 ++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 9dc5d65622..d87f3647e8 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3018,6 +3018,10 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0] = this.immediateMode.geometry.vertexStrokeColors.slice(-4); strokeColors[3] = this.curStrokeColor.slice(); + // Do the same for custom attributes + const customAttributes = []; + + if (argLength === 6) { this.isBezier = true; @@ -3163,21 +3167,34 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { } const LUTLength = this._lookUpTableQuadratic.length; + const immediateGeometry = this.immediateMode.geometry; // fillColors[0]: start point color // fillColors[1]: control point color // fillColors[2]: end point color const fillColors = []; - for (m = 0; m < 3; m++) fillColors.push([]); - fillColors[0] = this.immediateMode.geometry.vertexColors.slice(-4); + for (let m = 0; m < 3; m++) fillColors.push([]); + fillColors[0] = immediateGeometry.vertexColors.slice(-4); fillColors[2] = this.curFillColor.slice(); // Do the same for strokeColor. const strokeColors = []; - for (m = 0; m < 3; m++) strokeColors.push([]); - strokeColors[0] = this.immediateMode.geometry.vertexStrokeColors.slice(-4); + for (let m = 0; m < 3; m++) strokeColors.push([]); + strokeColors[0] = immediateGeometry.vertexStrokeColors.slice(-4); strokeColors[2] = this.curStrokeColor.slice(); + // Do the same for custom (user defined) attributes + const userAttributes = {}; + for (const attr in immediateGeometry.userAttributes){ + const attributeSrc = attr.concat('Src'); + const size = immediateGeometry.userAttributes[attr]; + const curData = this.userAttributes[attr]; + userAttributes[attr] = []; + for (let m = 0; m < 3; m++) userAttributes[attr].push([]); + userAttributes[attr][0] = immediateGeometry[attributeSrc].slice(-size); + userAttributes[attr][2] = curData; + } + if (argLength === 4) { this.isQuadratic = true; @@ -3190,7 +3207,7 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { let d1 = Math.hypot(w_x[1]-w_x[2], w_y[1]-w_y[2]); const totalLength = d0 + d1; d0 /= totalLength; - for (k = 0; k < 4; k++) { + for (let k = 0; k < 4; k++) { fillColors[1].push( fillColors[0][k] * (1-d0) + fillColors[2][k] * d0 ); @@ -3198,14 +3215,22 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { strokeColors[0][k] * (1-d0) + strokeColors[2][k] * d0 ); } + for (const attr in immediateGeometry.userAttributes){ + const size = immediateGeometry.userAttributes[attr]; + for (let k = 0; k < size; k++){ + userAttributes[attr][1].push( + userAttributes[attr][0][k] * (1-d0) + userAttributes[attr][2][k] * d0 + ); + } + } - for (i = 0; i < LUTLength; i++) { + for (let i = 0; i < LUTLength; i++) { // Interpolate colors using control points this.curFillColor = [0, 0, 0, 0]; this.curStrokeColor = [0, 0, 0, 0]; _x = _y = 0; - for (m = 0; m < 3; m++) { - for (k = 0; k < 4; k++) { + for (let m = 0; m < 3; m++) { + for (let k = 0; k < 4; k++) { this.curFillColor[k] += this._lookUpTableQuadratic[i][m] * fillColors[m][k]; this.curStrokeColor[k] += @@ -3214,6 +3239,17 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { _x += w_x[m] * this._lookUpTableQuadratic[i][m]; _y += w_y[m] * this._lookUpTableQuadratic[i][m]; } + + for (const attr in immediateGeometry.userAttributes) { + const size = immediateGeometry.userAttributes[attr]; + this.userAttributes[attr] = Array(size).fill(0); + for (let m = 0; m < 3; m++){ + for (let k = 0; k < size; k++){ + this.userAttributes[attr][k] += + this._lookUpTableQuadratic[i][m] * userAttributes[attr][m][k]; + } + } + } this.vertex(_x, _y); } From 7107d7c06977412380b43b488a7c92dfa4c39c17 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Thu, 26 Sep 2024 12:36:28 +0100 Subject: [PATCH 49/94] Update custom attribute test as geometrie's source attribute names now have 'Src' appended to them --- test/unit/webgl/p5.RendererGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 1bc4ed6e0a..0fcbf5df6c 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -1645,7 +1645,7 @@ suite('p5.RendererGL', function() { [1, 1, 1] ); - assert.deepEqual(renderer.immediateMode.geometry.aCustom, [ + assert.deepEqual(renderer.immediateMode.geometry.aCustomSrc, [ 1, 0, 0, 0, 0, 1, 1, 1, 1, From bb5ac025773a99e9c6186fe2773fd0434513a68c Mon Sep 17 00:00:00 2001 From: 23036879 Date: Thu, 26 Sep 2024 16:57:49 +0100 Subject: [PATCH 50/94] Interpolate user attributes across bezierVertex --- src/webgl/3d_primitives.js | 54 +++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index d87f3647e8..0599c24b6d 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3003,24 +3003,33 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { } const LUTLength = this._lookUpTableBezier.length; + const immediateGeometry = this.immediateMode.geometry; // fillColors[0]: start point color // fillColors[1],[2]: control point color // fillColors[3]: end point color const fillColors = []; for (m = 0; m < 4; m++) fillColors.push([]); - fillColors[0] = this.immediateMode.geometry.vertexColors.slice(-4); + fillColors[0] = immediateGeometry.vertexColors.slice(-4); fillColors[3] = this.curFillColor.slice(); // Do the same for strokeColor. const strokeColors = []; for (m = 0; m < 4; m++) strokeColors.push([]); - strokeColors[0] = this.immediateMode.geometry.vertexStrokeColors.slice(-4); + strokeColors[0] = immediateGeometry.vertexStrokeColors.slice(-4); strokeColors[3] = this.curStrokeColor.slice(); // Do the same for custom attributes - const customAttributes = []; - + const userAttributes = {}; + for (const attr in immediateGeometry.userAttributes){ + const attributeSrc = attr.concat('Src'); + const size = immediateGeometry.userAttributes[attr]; + const curData = this.userAttributes[attr]; + userAttributes[attr] = []; + for (m = 0; m < 4; m++) userAttributes[attr].push([]); + userAttributes[attr][0] = immediateGeometry[attributeSrc].slice(-size); + userAttributes[attr][3] = curData; + } if (argLength === 6) { this.isBezier = true; @@ -3049,14 +3058,25 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2) ); } + for (const attr in immediateGeometry.userAttributes){ + const size = immediateGeometry.userAttributes[attr]; + for (k = 0; k < size; k++){ + userAttributes[attr][1].push( + userAttributes[attr][0][k] * (1-d0) + userAttributes[attr][3][k] * d0 + ); + userAttributes[attr][2].push( + userAttributes[attr][0][k] * (1-d2) + userAttributes[attr][3][k] * d2 + ); + } + } - for (i = 0; i < LUTLength; i++) { + for (let i = 0; i < LUTLength; i++) { // Interpolate colors using control points this.curFillColor = [0, 0, 0, 0]; this.curStrokeColor = [0, 0, 0, 0]; _x = _y = 0; - for (m = 0; m < 4; m++) { - for (k = 0; k < 4; k++) { + for (let m = 0; m < 4; m++) { + for (let k = 0; k < 4; k++) { this.curFillColor[k] += this._lookUpTableBezier[i][m] * fillColors[m][k]; this.curStrokeColor[k] += @@ -3065,6 +3085,16 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { _x += w_x[m] * this._lookUpTableBezier[i][m]; _y += w_y[m] * this._lookUpTableBezier[i][m]; } + for (const attr in immediateGeometry.userAttributes){ + const size = immediateGeometry.userAttributes[attr]; + this.userAttributes[attr] = Array(size).fill(0); + for (let m = 0; m < 4; m++){ + for (let k = 0; k < size; k++){ + this.userAttributes[attr][k] += + this._lookUpTableBezier[i][m] * userAttributes[attr][m][k]; + } + } + } this.vertex(_x, _y); } // so that we leave currentColor with the last value the user set it to @@ -3086,7 +3116,7 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { const totalLength = d0 + d1 + d2; d0 /= totalLength; d2 /= totalLength; - for (k = 0; k < 4; k++) { + for (let k = 0; k < 4; k++) { fillColors[1].push( fillColors[0][k] * (1-d0) + fillColors[3][k] * d0 ); @@ -3100,7 +3130,7 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2) ); } - for (i = 0; i < LUTLength; i++) { + for (let i = 0; i < LUTLength; i++) { // Interpolate colors using control points this.curFillColor = [0, 0, 0, 0]; this.curStrokeColor = [0, 0, 0, 0]; @@ -3173,13 +3203,13 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { // fillColors[1]: control point color // fillColors[2]: end point color const fillColors = []; - for (let m = 0; m < 3; m++) fillColors.push([]); + for (m = 0; m < 3; m++) fillColors.push([]); fillColors[0] = immediateGeometry.vertexColors.slice(-4); fillColors[2] = this.curFillColor.slice(); // Do the same for strokeColor. const strokeColors = []; - for (let m = 0; m < 3; m++) strokeColors.push([]); + for (m = 0; m < 3; m++) strokeColors.push([]); strokeColors[0] = immediateGeometry.vertexStrokeColors.slice(-4); strokeColors[2] = this.curStrokeColor.slice(); @@ -3190,7 +3220,7 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { const size = immediateGeometry.userAttributes[attr]; const curData = this.userAttributes[attr]; userAttributes[attr] = []; - for (let m = 0; m < 3; m++) userAttributes[attr].push([]); + for (m = 0; m < 3; m++) userAttributes[attr].push([]); userAttributes[attr][0] = immediateGeometry[attributeSrc].slice(-size); userAttributes[attr][2] = curData; } From 233ed4b51938502ccde2e611a696ad3fc409cb37 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Thu, 26 Sep 2024 17:24:53 +0100 Subject: [PATCH 51/94] updated setAttribute documentation --- src/core/shape/vertex.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 3f04b8ea3b..1eab0fc475 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2255,30 +2255,31 @@ p5.prototype.normal = function(x, y, z) { /** Sets the shader's vertex attribute variables. * - * Shader programs run on the computer's graphics processing unit (GPU) - * They live in a part of the computer's memory that's completely separate from - * the sketch that runs them. Attributes are variables attached to vertices - * within a shader program. They provide a way to attach data to vertices - * and pass values from a sketch running on the CPU to a shader program. + * An attribute is a variable belonging to a vertex in a shader. p5.js provides some + * default attributes, such as `aPosition`, `aNormal`, `aVertexColor`, etc. Custom + * attributes can also be defined within `beginShape()` and `endShape()`. * * The first parameter, `attributeName`, is a string with the attribute's name. + * This is the same variable name which should be declared in the shader, similar to + * `setUniform()`. * - * The second parameter, `data`, is the value that should be assigned to the - * attribute. This value will be applied to subsequent vertices created with + * The second parameter, `data`, is the value assigned to the attribute. This + * value will be applied to subsequent vertices created with * vertex(). It can be a Number or an array of numbers, - * and in the shader program the type can be interpreted according to the WebGL + * and in the shader program the type can be declared according to the WebGL * specification. Common types include `float`, `vec2`, `vec3`, `vec4` or matrices. * * @example *
* * let vertSrc = ` + * #version 300 es * precision highp float; * uniform mat4 uModelViewMatrix; * uniform mat4 uProjectionMatrix; * - * attribute vec3 aPosition; - * attribute vec2 aOffset; + * in vec3 aPosition; + * in vec2 aOffset; * * void main(){ * vec4 positionVec4 = vec4(aPosition.xyz, 1.0); @@ -2288,6 +2289,7 @@ p5.prototype.normal = function(x, y, z) { * `; * * let fragSrc = ` + * #version 300 es * precision highp float; * * void main(){ From 9f2457619220351ab54305f4e3099344c4a6670f Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 12:28:12 +0100 Subject: [PATCH 52/94] Added a second example for setAttribute() --- src/core/shape/vertex.js | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 1eab0fc475..3f4afdea22 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2320,6 +2320,76 @@ p5.prototype.normal = function(x, y, z) { * } * *
+ * + *
+ * + * let myShader; + * const cols = 10; + * const rows = 10; + * const cellSize = 16; + * + * const vertSrc = `#version 300 es + * precision mediump float; + * uniform mat4 uProjectionMatrix; + * uniform mat4 uModelViewMatrix; + * + * in vec3 aPosition; + * in vec3 aNormal; + * in vec3 aVertexColor; + * in float aDistance; + * + * out vec3 vVertexColor; + * + * void main(){ + * vec4 positionVec4 = vec4(aPosition, 1.0); + * positionVec4.xyz += aDistance * aNormal; + * vVertexColor = aVertexColor; + * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4; + * } + * `; + * + * const fragSrc = `#version 300 es + * precision mediump float; + * + * in vec3 vVertexColor; + * out vec4 outColor; + * + * void main(){ + * outColor = vec4(vVertexColor, 1.0); + * } + * `; + * + * async function setup(){ + * myShader = createShader(vertSrc, fragSrc); + * createCanvas(200, 200, WEBGL); + * shader(myShader); + * noStroke(); + * describe('A blue grid, which moves away from the mouse position, on a grey background.'); + * } + * + * function draw(){ + * background(200); + * translate(-cols*cellSize/2, -rows*cellSize/2); + * beginShape(QUADS); + * for (let x = 0; x < cols; x++) { + * for (let y = 0; y < rows; y++) { + * let x1 = x * cellSize; + * let y1 = y * cellSize; + * let x2 = x1 + cellSize; + * let y2 = y1 + cellSize; + * fill(x/rows*255, y/cols*255, 255); + * let distance = dist(x1,y1, mouseX, mouseY); + * setAttribute('aDistance', min(distance, 200)); + * vertex(x1, y1); + * vertex(x2, y1); + * vertex(x2, y2); + * vertex(x1, y2); + * } + * } + * endShape(); + * } + * + *
/ /** * @method setAttribute From b41b0136fa7d7e67b761cad94f1c25f8688f2f34 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 12:28:45 +0100 Subject: [PATCH 53/94] add custom attributes to buffer strides for quad strip/ quad --- src/webgl/p5.RendererGL.Immediate.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 72d4039346..965904ff86 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -36,11 +36,10 @@ p5.RendererGL.prototype.beginShape = function(mode) { if (this._useUserAttributes === true){ for (const name in this.userAttributes){ delete this.immediateMode.geometry[name]; + delete this.immediateBufferStrides[name.concat('Src')]; } delete this.userAttributes; this._useUserAttributes = false; - } - if (this.tessyVertexSize > 12){ this.tessyVertexSize = 12; } this.immediateMode.geometry.reset(); @@ -196,12 +195,12 @@ p5.RendererGL.prototype.setAttribute = function(attributeName, data){ this._useUserAttributes = true; this.userAttributes = {}; } - const size = data.length ? data.length : 1; + const buff = attributeName.concat('Buffer'); + const attributeSrc = attributeName.concat('Src'); const size = data.length ? data.length : 1; if (!this.userAttributes.hasOwnProperty(attributeName)){ this.tessyVertexSize += size; + this.immediateBufferStrides[attributeSrc] = size; } - const buff = attributeName.concat('Buffer'); - const attributeSrc = attributeName.concat('Src'); this.userAttributes[attributeName] = data; const bufferExists = this.immediateMode .buffers From ebce73e8e5fd522de245ecaf1945ebf59028b327 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 14:23:44 +0100 Subject: [PATCH 54/94] added visual tests for setAttribute() --- test/unit/visual/cases/webgl.js | 96 ++++++++++++++++++ .../setAttribute/on QUADS shape mode/000.png | Bin 0 -> 421 bytes .../on QUADS shape mode/metadata.json | 3 + .../setAttribute/on TESS shape mode/000.png | Bin 0 -> 883 bytes .../on TESS shape mode/metadata.json | 3 + .../000.png | Bin 0 -> 630 bytes .../metadata.json | 3 + 7 files changed, 105 insertions(+) create mode 100644 test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/000.png create mode 100644 test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/metadata.json create mode 100644 test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/000.png create mode 100644 test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/metadata.json create mode 100644 test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/000.png create mode 100644 test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/metadata.json diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index e5e8a9be7a..def61f978e 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -130,4 +130,100 @@ visualSuite('WebGL', function() { } ); }); + + visualSuite('setAttribute', function(){ + const vertSrc = `#version 300 es + precision mediump float; + uniform mat4 uProjectionMatrix; + uniform mat4 uModelViewMatrix; + in vec3 aPosition; + in vec3 aCol; + out vec3 vCol; + void main(){ + vCol = aCol; + gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0); + }`; + const fragSrc = `#version 300 es + precision mediump float; + in vec3 vCol; + out vec4 outColor; + void main(){ + outColor = vec4(vCol, 1.0); + }`; + visualTest( + 'on TESS shape mode', function(p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + p5.background('white'); + const myShader = p5.createShader(vertSrc, fragSrc); + p5.shader(myShader); + p5.beginShape(p5.TESS); + p5.noStroke(); + for (let i = 0; i < 20; i++){ + let x = 20 * p5.sin(i/20*p5.TWO_PI); + let y = 20 * p5.cos(i/20*p5.TWO_PI); + p5.setAttribute('aCol', [x/20, -y/20, 0]); + p5.vertex(x, y); + } + p5.endShape(); + screenshot(); + } + ); + visualTest( + 'on QUADS shape mode', function(p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + p5.background('white'); + const myShader = p5.createShader(vertSrc, fragSrc); + p5.shader(myShader) + p5.beginShape(p5.QUADS); + p5.noStroke(); + p5.translate(-25,-25); + for (let i = 0; i < 5; i++){ + for (let j = 0; j < 5; j++){ + let x1 = i * 10; + let x2 = x1 + 10; + let y1 = j * 10; + let y2 = y1 + 10; + p5.setAttribute('aCol', [1, 0, 0]); + p5.vertex(x1, y1); + p5.setAttribute('aCol', [0, 0, 1]); + p5.vertex(x2, y1); + p5.setAttribute('aCol', [0, 1, 1]); + p5.vertex(x2, y2); + p5.setAttribute('aCol', [1, 1, 1]); + p5.vertex(x1, y2); + } + } + p5.endShape(); + screenshot(); + } + ); + visualTest( + 'on buildGeometry outputs containing 3D primitives', function(p5, screenshot) { + p5.createCanvas(50, 50, p5.WEBGL); + p5.background('white'); + const myShader = p5.createShader(vertSrc, fragSrc); + p5.shader(myShader); + const shape = p5.buildGeometry(() => { + p5.push(); + p5.translate(15,-10,0); + p5.sphere(5); + p5.pop(); + p5.beginShape(p5.TRIANGLES); + p5.setAttribute('aCol', [1,0,0]) + p5.vertex(-5, 5, 0); + p5.setAttribute('aCol', [0,1,0]) + p5.vertex(5, 5, 0); + p5.setAttribute('aCol', [0,0,1]) + p5.vertex(0, -5, 0); + p5.endShape(p5.CLOSE); + p5.push(); + p5.translate(-15,10,0); + p5.box(10); + p5.pop(); + }) + p5.model(shape); + screenshot(); + } + ); + }); }); diff --git a/test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/000.png b/test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/000.png new file mode 100644 index 0000000000000000000000000000000000000000..75018122a4f9c6100832d4a7c6fcee5ace5965d4 GIT binary patch literal 421 zcmV;W0b2fvP)Px$Ur9tkRA@u(nXz%gKoCX$7C-^Eb1HBFR6tGzE`SQ0Q-KSh0_Rjf0Ti%Sz761E zbVdkiPZ!+G-gthbfX@tnPw4Ic5?igV9bWK*SLCjWx9sPC-;dIXt=HEMC@}JlmUUvA zO)`Ro6WJysZR*5!y9Xow$tXS^jI^f{+wY%@%uihL`DCOmo!H?pV`MsU#n+6Hc64IL zqhn+|amBY|qz#?e>69@tOk8o!7>U=3T`mho`iU#L1tYOKF|z_Afa3N;ZkJB7Rkz(I z*-7@)=GV^lY~y_(vW<5VXITfp%+Aby2V&j#_H5(X#-~24_Ivw(li7a(-B$ZOUeb?w P00000NkvXXu0mjfI)lLG literal 0 HcmV?d00001 diff --git a/test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/metadata.json b/test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/setAttribute/on QUADS shape mode/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/000.png b/test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/000.png new file mode 100644 index 0000000000000000000000000000000000000000..5a5164da2f41e42175fa6b9d21f38e0a11a3587b GIT binary patch literal 883 zcmV-(1C0EMP)Px&ElET{RA@u(*}rQOVHgMS@1>@#t(H{L3N;`Nf~{C$hg7iG_a%#i(5?mlfr6lm zgVXcg*j0rtu5N-jI^CN=aFQbU1EdcA7lmr4H+$H(k_j&Gl z?-F&p-7b#6BN{?Vmf|pxk_Cd`iO78k*_AzKXTQ^XJqqAv#4rKiMr9St5yLS6ixn2^ zcm#onUjp&(IP*s-$jQFviT|&!RT(yp6E0Q#L0%BCClK#t15#NQB*k4FXSh`(%#FK) zoFL+dK>V{9U z!Jl)hdOf(YZI2x6nYk8Wrj?H=gz%>v^2@&g=lbx4DA<(;`HS>ii2N+UpZKNWTUx5T z6r|oJ)Y{ppeLejZ1|98t?8oxImH;JG>zUn@L7K=glzawiOePms+zLOg@i1x5RfyB;ao-< zg^*CGgn(R35{zf0Q3wf@iZ5h_G0f$pVFU@0iZ5g_Nidm{h7m+<m(j9y-u8<}X8U_gCM0_zWkQNaZ3=!54*D|w(tPr7TfpCJTozz-Pt&UBX`Zka@ z5n7fAXGmIVrXUl@IuY6qfb_Q*H+$Z-ZxJXDa+?V29)O^b{)E!f>vTH3w-vJY_cUYR ztE(6Tu*?{i3g&|CO@knb*na6@Y(In?#!>b8%wZxvNAEE9{R?;Ux_YR26U+br002ov JPDHLkV1ffDkUIbX literal 0 HcmV?d00001 diff --git a/test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/metadata.json b/test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/setAttribute/on TESS shape mode/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file diff --git a/test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/000.png b/test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/000.png new file mode 100644 index 0000000000000000000000000000000000000000..596ec54c90d7b36a606d8145e1332a4822a29f51 GIT binary patch literal 630 zcmV-+0*U>JP)Px%FiAu~RA@u(mp@CxKor1V?4W}~K^G-L#l;rCfPaRb5}`U2EGUA~#jzqC6o*LC zPvIBP(GMV4+DQ=HGPo!R6&+l}O&lUGn9wGd_G<6aLoOgKa=m-M_j~W|qPec?!asO4 z1tOV6WkfO|po}OZf-;~=OHg8iX>HpMzUT9Kz`t&fbVi!ZCJ;g*F(Zg&S+PJ;fy6%zRpaW}#{K-T#b2xf}bBdi(ZJx&=>MtIlM?|iDXq%SQD zkoDAM9Ve)ol6gZ+BKY<-xZi%02JqKYAj8Uh+`7t$G9oAgsM*`v3p{ literal 0 HcmV?d00001 diff --git a/test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/metadata.json b/test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/metadata.json new file mode 100644 index 0000000000..2d4bfe30da --- /dev/null +++ b/test/unit/visual/screenshots/WebGL/setAttribute/on buildGeometry outputs containing 3D primitives/metadata.json @@ -0,0 +1,3 @@ +{ + "numScreenshots": 1 +} \ No newline at end of file From e46c2d7777389bdba8c5d8566adc8a9dc0f24afa Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 16:22:29 +0100 Subject: [PATCH 55/94] improved error message for when working directly with geometry.setattribute --- src/webgl/p5.RendererGL.Retained.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 37bf95457d..ddb4c8d70e 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -158,11 +158,12 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { continue; } const adjustedLength = geometry.model[buff.src].length / buff.size; - if(adjustedLength != geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with - either too many or too few values compared to vertices. - There may be unexpected results from the shaders. - `, 'setAttribute()'); + if(adjustedLength > geometry.model.vertices.length){ + p5._friendlyError(`One of the geometries has a custom attribute with more values than vertices. + This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + } else if(adjustedLength < geometry.model.vertices.length){ + p5._friendlyError(`One of the geometries has a custom attribute with less values than vertices. + This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } buff._prepareBuffer(geometry, fillShader); } @@ -191,11 +192,12 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { continue; } const adjustedLength = geometry.model[buff.src].length / buff.size; - if(adjustedLength != geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with - either too many or too few values compared to vertices. - There may be unexpected results from the shaders. - `, 'setAttribute()'); + if(adjustedLength > geometry.model.vertices.length){ + p5._friendlyError(`One of the geometries has a custom attribute with more values than vertices. + This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + } else if(adjustedLength < geometry.model.vertices.length){ + p5._friendlyError(`One of the geometries has a custom attribute with fewer values than vertices. + This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } buff._prepareBuffer(geometry, strokeShader); } From d8318e1ba42d01902493b78a6a2fbca206d42466 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 16:22:53 +0100 Subject: [PATCH 56/94] reset userattributes object instead of deleting (its likely to be used again --- src/webgl/p5.RendererGL.Immediate.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 965904ff86..a9386be051 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -38,9 +38,10 @@ p5.RendererGL.prototype.beginShape = function(mode) { delete this.immediateMode.geometry[name]; delete this.immediateBufferStrides[name.concat('Src')]; } - delete this.userAttributes; + this.userAttributes = {}; this._useUserAttributes = false; this.tessyVertexSize = 12; + this.immediateMode.buffers.user = []; } this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; From 968a05ec8b338f58ef1440bf85b3187d1f6ab1f4 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 17:44:17 +0100 Subject: [PATCH 57/94] remove old user defined render buffers when model is called --- src/webgl/p5.RendererGL.Retained.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index ddb4c8d70e..5adff5010d 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -63,6 +63,7 @@ p5.RendererGL.prototype._freeBuffers = function(gId) { freeBuffers(this.retainedMode.buffers.stroke); freeBuffers(this.retainedMode.buffers.fill); freeBuffers(this.retainedMode.buffers.user); + this.retainedMode.buffers.user = []; }; /** @@ -162,7 +163,7 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { p5._friendlyError(`One of the geometries has a custom attribute with more values than vertices. This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } else if(adjustedLength < geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with less values than vertices. + p5._friendlyError(`One of the geometries has a custom attribute with fewer values than vertices. This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } buff._prepareBuffer(geometry, fillShader); @@ -213,7 +214,7 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { if (this.geometryBuilder) { this.geometryBuilder.addRetained(geometry); } - + return this; }; From a66af6955c533958677d31f16b230e181dacd24e Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 17:44:42 +0100 Subject: [PATCH 58/94] Added a bunch of tests for setAttribute() --- test/unit/webgl/p5.RendererGL.js | 176 +++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 0fcbf5df6c..962b734186 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -2503,4 +2503,180 @@ suite('p5.RendererGL', function() { } ); }); + + suite('setAttribute()', function() { + test('Immediate mode data and buffers created in beginShape', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + + myp5.beginShape(); + myp5.setAttribute('aCustom', 1); + myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertex(0,0,0); + assert.deepEqual(myp5._renderer.userAttributes,{ + aCustom: 1, + aCustomVec3: [1,2,3] + }); + assert.deepEqual(myp5._renderer.immediateMode.geometry.aCustomSrc, [1]); + assert.deepEqual(myp5._renderer.immediateMode.geometry.aCustomVec3Src, [1,2,3]); + assert.deepEqual(myp5._renderer.immediateMode.geometry.userAttributes, { + aCustom: 1, + aCustomVec3: 3 + }); + assert.deepEqual(myp5._renderer.immediateMode.buffers.user, [ + { + size: 1, + src: 'aCustomSrc', + dst: 'aCustomBuffer', + attr: 'aCustom', + _renderer: myp5._renderer, + map: undefined + }, + { + size: 3, + src: 'aCustomVec3Src', + dst: 'aCustomVec3Buffer', + attr: 'aCustomVec3', + _renderer: myp5._renderer, + map: undefined + } + ]); + myp5.endShape(); + + } + ); + test('Immediate mode data and buffers deleted after beginShape', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + + myp5.beginShape(); + myp5.setAttribute('aCustom', 1); + myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertex(0,0,0); + myp5.endShape(); + + myp5.beginShape(); + assert.isUndefined(myp5._renderer.immediateMode.geometry.aCustomSrc); + assert.isUndefined(myp5._renderer.immediateMode.geometry.aCustomVec3Src); + assert.deepEqual(myp5._renderer.immediateMode.geometry.userAttributes, {}); + assert.deepEqual(myp5._renderer.userAttributes, {}); + assert.deepEqual(myp5._renderer.immediateMode.buffers.user, []); + myp5.endShape(); + } + ); + test('Data copied over from beginGeometry', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + myp5.beginGeometry(); + myp5.beginShape(); + myp5.setAttribute('aCustom', 1); + myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertex(0,1,0); + myp5.vertex(-1,0,0); + myp5.vertex(1,0,0); + const immediateCopy = myp5._renderer.immediateMode.geometry; + myp5.endShape(); + const myGeo = myp5.endGeometry(); + assert.deepEqual(immediateCopy.aCustomSrc, myGeo.aCustomSrc); + assert.deepEqual(immediateCopy.aCustomVec3Src, myGeo.aCustomVec3Src); + assert.deepEqual(immediateCopy.userAttributes, myGeo.userAttributes); + } + ); + test('Retained mode buffers are created for rendering', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + myp5.beginGeometry(); + myp5.beginShape(); + myp5.setAttribute('aCustom', 1); + myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertex(0,0,0); + myp5.vertex(1,0,0); + myp5.endShape(); + const myGeo = myp5.endGeometry(); + myp5._renderer.createBuffers(myGeo.gId, myGeo); + assert.deepEqual(myp5._renderer.retainedMode.buffers.user, [ + { + size: 1, + src: 'aCustomSrc', + dst: 'aCustomBuffer', + attr: 'aCustom', + _renderer: myp5._renderer, + map: undefined + }, + { + size: 3, + src: 'aCustomVec3Src', + dst: 'aCustomVec3Buffer', + attr: 'aCustomVec3', + _renderer: myp5._renderer, + map: undefined + } + ]); + } + ); + test('Retained mode buffers deleted after rendering', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + myp5.beginGeometry(); + myp5.beginShape(); + myp5.setAttribute('aCustom', 1); + myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertex(0,0,0); + myp5.vertex(1,0,0); + myp5.endShape(); + const myGeo = myp5.endGeometry(); + myp5.model(myGeo); + assert.equal(myp5._renderer.retainedMode.buffers.user.length, 0); + } + ); + test('Friendly error if different sizes used', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + const logs = []; + const myLog = (...data) => logs.push(data.join(', ')); + const oldLog = console.log; + console.log = myLog; + myp5.beginShape(); + myp5.setAttribute('aCustom', [1,2,3]); + myp5.vertex(0,0,0); + myp5.setAttribute('aCustom', [1,2]); + myp5.vertex(1,0,0); + myp5.endShape(); + console.log = oldLog; + expect(logs.join('\n')).to.match(/Custom attribute aCustom has been set with various data sizes/); + } + ); + test('Friendly error too many values set', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + const logs = []; + const myLog = (...data) => logs.push(data.join(', ')); + const oldLog = console.log; + console.log = myLog; + let myGeo = new p5.Geometry(); + myGeo.vertices.push(new p5.Vector(0,0,0)); + myGeo.setAttribute('aCustom', 1); + myGeo.setAttribute('aCustom', 2); + myp5.model(myGeo); + console.log = oldLog; + expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute with more values than vertices./); + } + ); + test('Friendly error if too few values set', + function() { + myp5.createCanvas(50, 50, myp5.WEBGL); + const logs = []; + const myLog = (...data) => logs.push(data.join(', ')); + const oldLog = console.log; + console.log = myLog; + let myGeo = new p5.Geometry(); + myGeo.vertices.push(new p5.Vector(0,0,0)); + myGeo.vertices.push(new p5.Vector(0,0,0)); + myGeo.setAttribute('aCustom', 1); + myp5.model(myGeo); + console.log = oldLog; + expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute with fewer values than vertices./); + } + ); + }) }); From 62a4afaa6e8463dfe903c45f4da8b846ad8dd14f Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 17:53:52 +0100 Subject: [PATCH 59/94] fixed some bugs in examples for setAttribute --- src/core/shape/vertex.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 3f4afdea22..6ebdff7b77 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2272,9 +2272,8 @@ p5.prototype.normal = function(x, y, z) { * @example *
* - * let vertSrc = ` - * #version 300 es - * precision highp float; + * const vertSrc = `#version 300 es + * precision mediump float; * uniform mat4 uModelViewMatrix; * uniform mat4 uProjectionMatrix; * @@ -2288,18 +2287,17 @@ p5.prototype.normal = function(x, y, z) { * } * `; * - * let fragSrc = ` - * #version 300 es - * precision highp float; - * + * const fragSrc = `#version 300 es + * precision mediump float; + * out vec4 outColor; * void main(){ - * gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); + * outColor = vec4(0.0, 1.0, 1.0, 1.0); * } * `; * * function setup(){ * createCanvas(100, 100, WEBGL); - * let myShader = createShader(vertSrc, fragSrc); + * const myShader = createShader(vertSrc, fragSrc); * shader(myShader); * noStroke(); * describe('A wobbly, cyan circle on a grey background.'); @@ -2309,10 +2307,10 @@ p5.prototype.normal = function(x, y, z) { * background(125); * beginShape(); * for (let i = 0; i < 30; i++){ - * let x = 40 * cos(i/30 * TWO_PI); - * let y = 40 * sin(i/30 * TWO_PI); - * let xOff = 10 * noise(x + millis()/1000) - 5; - * let yOff = 10 * noise(y + millis()/1000) - 5; + * const x = 40 * cos(i/30 * TWO_PI); + * const y = 40 * sin(i/30 * TWO_PI); + * const xOff = 10 * noise(x + millis()/1000) - 5; + * const yOff = 10 * noise(y + millis()/1000) - 5; * setAttribute('aOffset', [xOff, yOff]); * vertex(x, y); * } @@ -2361,7 +2359,7 @@ p5.prototype.normal = function(x, y, z) { * * async function setup(){ * myShader = createShader(vertSrc, fragSrc); - * createCanvas(200, 200, WEBGL); + * createCanvas(100, 100, WEBGL); * shader(myShader); * noStroke(); * describe('A blue grid, which moves away from the mouse position, on a grey background.'); From 01c2d3ba5b961bd5b165c2bc36948553821ce132 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 17:59:44 +0100 Subject: [PATCH 60/94] scaled down the example canvas size to correct (100,100) size --- src/core/shape/vertex.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 6ebdff7b77..3916aa5722 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2324,7 +2324,7 @@ p5.prototype.normal = function(x, y, z) { * let myShader; * const cols = 10; * const rows = 10; - * const cellSize = 16; + * const cellSize = 9; * * const vertSrc = `#version 300 es * precision mediump float; @@ -2377,7 +2377,7 @@ p5.prototype.normal = function(x, y, z) { * let y2 = y1 + cellSize; * fill(x/rows*255, y/cols*255, 255); * let distance = dist(x1,y1, mouseX, mouseY); - * setAttribute('aDistance', min(distance, 200)); + * setAttribute('aDistance', min(distance, 100)); * vertex(x1, y1); * vertex(x2, y1); * vertex(x2, y2); From d8db76e4ffc71309e119a73b7d2bc54249c84da2 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 18:11:08 +0100 Subject: [PATCH 61/94] Added explanation to the setAttribute method in geometry. --- src/webgl/p5.Geometry.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index b1f6bc08d3..eb14c1deaa 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1920,6 +1920,35 @@ p5.Geometry = class Geometry { return this; } +/** Sets the shader's vertex attribute variables. + * + * An attribute is a variable belonging to a vertex in a shader. p5.js provides some + * default attributes, such as `aPosition`, `aNormal`, `aVertexColor`, etc. Custom + * attributes can also be defined within `beginShape()` and `endShape()`. + * + * The first parameter, `attributeName`, is a string with the attribute's name. + * This is the same variable name which should be declared in the shader, similar to + * `setUniform()`. + * + * The second parameter, `data`, is the value assigned to the attribute. This value + * will be pushed directly onto the Geometry object. There should be the same number + * of custom attribute values as vertices. + * + * The `data` can be a Number or an array of numbers. Tn the shader program the type + * can be declared according to the WebGL specification. Common types include `float`, + * `vec2`, `vec3`, `vec4` or matrices. + * + * @example + *
+ * + * + *
+/ +/** + * @method setAttribute + * @param {String} attributeName the name of the vertex attribute. + * @param {Number|Number[]} data the data tied to the vertex attribute. + */ setAttribute(attributeName, data, size = data.length ? data.length : 1){ const attributeSrc = attributeName.concat('Src'); if (!this.hasOwnProperty(attributeSrc)){ From 8d97dc1d00ae2415308153b5b3212fe507b208d1 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 18:11:37 +0100 Subject: [PATCH 62/94] added initial docs for Geometry.setAttribute --- src/webgl/p5.Geometry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index eb14c1deaa..4c123b6f05 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1938,6 +1938,8 @@ p5.Geometry = class Geometry { * can be declared according to the WebGL specification. Common types include `float`, * `vec2`, `vec3`, `vec4` or matrices. * + * See also the global setAttribute() function. + * * @example *
* From 65f8a08fd6692f38e6302d2bdc1df8c5423183db Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 18:12:06 +0100 Subject: [PATCH 63/94] added a reference to the geometry method at the end of setAttribute inline docs --- src/core/shape/vertex.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 3916aa5722..4d9de88075 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2269,6 +2269,9 @@ p5.prototype.normal = function(x, y, z) { * and in the shader program the type can be declared according to the WebGL * specification. Common types include `float`, `vec2`, `vec3`, `vec4` or matrices. * + * See also the setAttribute() method on + * Geometry objects. + * * @example *
* From eb18e6fd67324bd46c98140e22c178e4286024d8 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 18:54:54 +0100 Subject: [PATCH 64/94] added an example to set geometry method --- src/webgl/p5.Geometry.js | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 4c123b6f05..51b3ea42ca 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1943,6 +1943,56 @@ p5.Geometry = class Geometry { * @example *
* + * let geo; + * + * function cartesianToSpherical(x, y, z) { + * let r = sqrt(x * x + y * y + z * z); + * let theta = acos(z / r); + * let phi = atan2(y, x); + * return { theta, phi }; + * } + * + * function setup() { + * createCanvas(100, 100, WEBGL); + * myShader = materialShader().modify({ + * vertexDeclarations:`in float aRoughness; + * out float vRoughness;`, + * fragmentDeclarations: 'in float vRoughness;', + * 'void afterVertex': `() { + * vRoughness = aRoughness; + * }`, + * 'vec4 combineColors': `(ColorComponents components) { + * vec4 color = vec4(0.); + * color.rgb += components.diffuse * components.baseColor * (1.0-vRoughness); + * color.rgb += components.ambient * components.ambientColor; + * color.rgb += components.specular * components.specularColor * (1.0-vRoughness); + * color.a = components.opacity; + * return color; + * }` + * }); + * beginGeometry(); + * fill('hotpink'); + * sphere(45, 50, 50); + * geo = endGeometry(); + * for (let v of geo.vertices){ + * let spherical = cartesianToSpherical(v.x, v.y, v.z); + * let roughness = noise(spherical.theta*5, spherical.phi*5); + * geo.setAttribute('aRoughness', roughness); + * } + * shader(myShader); + * noStroke(); + * describe('A rough pink sphere rotating on a blue background.'); + * } + * + * function draw() { + * rotateY(millis()*0.001); + * background('lightblue'); + * directionalLight('white', -1, 1, -1); + * ambientLight(320); + * shininess(2); + * specularMaterial(255,125,100); + * model(geo); + * } * *
/ From 6b0ad95a6469a4854bd54012e6acbec3b7dbba85 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Fri, 27 Sep 2024 18:56:50 +0100 Subject: [PATCH 65/94] fixed syntax error in set attribute example --- src/webgl/p5.Geometry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 51b3ea42ca..38608e6e69 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1954,7 +1954,7 @@ p5.Geometry = class Geometry { * * function setup() { * createCanvas(100, 100, WEBGL); - * myShader = materialShader().modify({ + * const myShader = materialShader().modify({ * vertexDeclarations:`in float aRoughness; * out float vRoughness;`, * fragmentDeclarations: 'in float vRoughness;', From 70d64c76becad6b4184b7608caaae18b1f4914a6 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 10:20:55 +0100 Subject: [PATCH 66/94] changed some tests from assert.deepEqual() to expect().to.containSubset --- test/unit/webgl/p5.RendererGL.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 962b734186..d8977adf0a 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -2523,22 +2523,18 @@ suite('p5.RendererGL', function() { aCustom: 1, aCustomVec3: 3 }); - assert.deepEqual(myp5._renderer.immediateMode.buffers.user, [ + expect(myp5._renderer.immediateMode.buffers.user).to.containSubset([ { size: 1, src: 'aCustomSrc', dst: 'aCustomBuffer', attr: 'aCustom', - _renderer: myp5._renderer, - map: undefined }, { size: 3, src: 'aCustomVec3Src', dst: 'aCustomVec3Buffer', attr: 'aCustomVec3', - _renderer: myp5._renderer, - map: undefined } ]); myp5.endShape(); @@ -2594,22 +2590,18 @@ suite('p5.RendererGL', function() { myp5.endShape(); const myGeo = myp5.endGeometry(); myp5._renderer.createBuffers(myGeo.gId, myGeo); - assert.deepEqual(myp5._renderer.retainedMode.buffers.user, [ + expect(myp5._renderer.retainedMode.buffers.user).to.containSubset([ { size: 1, src: 'aCustomSrc', dst: 'aCustomBuffer', attr: 'aCustom', - _renderer: myp5._renderer, - map: undefined }, { size: 3, src: 'aCustomVec3Src', dst: 'aCustomVec3Buffer', attr: 'aCustomVec3', - _renderer: myp5._renderer, - map: undefined } ]); } From 9d1cd6ee3a85b1ffeb8e1739d9639cab4852adfe Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 10:30:44 +0100 Subject: [PATCH 67/94] add some comments to the example --- src/webgl/p5.Geometry.js | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 38608e6e69..f06e159f63 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1943,7 +1943,7 @@ p5.Geometry = class Geometry { * @example *
* - * let geo; + * let geo; * * function cartesianToSpherical(x, y, z) { * let r = sqrt(x * x + y * y + z * z); @@ -1954,6 +1954,8 @@ p5.Geometry = class Geometry { * * function setup() { * createCanvas(100, 100, WEBGL); + * + * // Modify the material shader to display roughness. * const myShader = materialShader().modify({ * vertexDeclarations:`in float aRoughness; * out float vRoughness;`, @@ -1970,27 +1972,40 @@ p5.Geometry = class Geometry { * return color; * }` * }); + * + * // Create the Geometry object. * beginGeometry(); * fill('hotpink'); * sphere(45, 50, 50); * geo = endGeometry(); + * + * // Set the roughness value for every vertex. * for (let v of geo.vertices){ + * // convert coordinates to spherical coordinates * let spherical = cartesianToSpherical(v.x, v.y, v.z); * let roughness = noise(spherical.theta*5, spherical.phi*5); * geo.setAttribute('aRoughness', roughness); * } + * + * // Use the custom shader. * shader(myShader); - * noStroke(); * describe('A rough pink sphere rotating on a blue background.'); * } * * function draw() { - * rotateY(millis()*0.001); + * // Set some styles and lighting * background('lightblue'); + * noStroke(); + * + * specularMaterial(255,125,100); + * shininess(2); + * * directionalLight('white', -1, 1, -1); * ambientLight(320); - * shininess(2); - * specularMaterial(255,125,100); + * + * rotateY(millis()*0.001); + * + * // Draw the geometry * model(geo); * } * From 656d37fd841d45a6436c420f2346ff2338794234 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 10:33:48 +0100 Subject: [PATCH 68/94] change x * x to pow(x, x) --- src/webgl/p5.Geometry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index f06e159f63..98faa330b7 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -1946,7 +1946,7 @@ p5.Geometry = class Geometry { * let geo; * * function cartesianToSpherical(x, y, z) { - * let r = sqrt(x * x + y * y + z * z); + * let r = sqrt(pow(x, x) + pow(y, y) + pow(z, z)); * let theta = acos(z / r); * let phi = atan2(y, x); * return { theta, phi }; From 5c967dceb89976e2780b73c9d9350afc71f401da Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 10:43:22 +0100 Subject: [PATCH 69/94] Updated setAttribute Documentation. --- src/core/shape/vertex.js | 50 ++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 4d9de88075..f224c65bd6 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2256,8 +2256,11 @@ p5.prototype.normal = function(x, y, z) { /** Sets the shader's vertex attribute variables. * * An attribute is a variable belonging to a vertex in a shader. p5.js provides some - * default attributes, such as `aPosition`, `aNormal`, `aVertexColor`, etc. Custom - * attributes can also be defined within `beginShape()` and `endShape()`. + * default attributes, such as `aPosition`, `aNormal`, `aVertexColor`, etc. These are + * set using vertex(), normal() + * and fill() respectively. Custom attribute data can also + * be defined within beginShape() and + * endShape(). * * The first parameter, `attributeName`, is a string with the attribute's name. * This is the same variable name which should be declared in the shader, similar to @@ -2300,20 +2303,30 @@ p5.prototype.normal = function(x, y, z) { * * function setup(){ * createCanvas(100, 100, WEBGL); + * + * // Create and use the custom shader. * const myShader = createShader(vertSrc, fragSrc); * shader(myShader); - * noStroke(); + * * describe('A wobbly, cyan circle on a grey background.'); * } * * function draw(){ + * // Set the styles * background(125); + * noStroke(); + * + * // 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; + * + * // Apply these noise values to the following vertex. * setAttribute('aOffset', [xOff, yOff]); * vertex(x, y); * } @@ -2360,9 +2373,11 @@ p5.prototype.normal = function(x, y, z) { * } * `; * - * async function setup(){ - * myShader = createShader(vertSrc, fragSrc); + * function setup(){ * createCanvas(100, 100, WEBGL); + * + * // Create and apply the custom shader. + * myShader = createShader(vertSrc, fragSrc); * shader(myShader); * noStroke(); * describe('A blue grid, which moves away from the mouse position, on a grey background.'); @@ -2370,21 +2385,26 @@ p5.prototype.normal = function(x, y, z) { * * function draw(){ * background(200); + * + * // Draw the grid in the middle of the screen. * translate(-cols*cellSize/2, -rows*cellSize/2); * beginShape(QUADS); - * for (let x = 0; x < cols; x++) { - * for (let y = 0; y < rows; y++) { - * let x1 = x * cellSize; - * let y1 = y * cellSize; - * let x2 = x1 + cellSize; - * let y2 = y1 + cellSize; + * for (let i = 0; i < cols; i++) { + * for (let j = 0; j < rows; j++) { + * let x = i * cellSize; + * let y = j * cellSize; * fill(x/rows*255, y/cols*255, 255); + * + * // Calculate the distance from the corner of each cell to the mouse. * let distance = dist(x1,y1, mouseX, mouseY); + * + * // Send the distance to the shader. * setAttribute('aDistance', min(distance, 100)); - * vertex(x1, y1); - * vertex(x2, y1); - * vertex(x2, y2); - * vertex(x1, y2); + * + * vertex(x, y); + * vertex(x + cellsize, y); + * vertex(x + cellsize, y1 + cellsize); + * vertex(x, y + cellsize); * } * } * endShape(); From a2dd8e4005c559b5d6fd55a9f87b5d5b5a7d1331 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 11:22:05 +0100 Subject: [PATCH 70/94] Changed 'grey' to 'gray' --- src/core/shape/vertex.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index f224c65bd6..71c8229bea 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2308,7 +2308,7 @@ p5.prototype.normal = function(x, y, z) { * const myShader = createShader(vertSrc, fragSrc); * shader(myShader); * - * describe('A wobbly, cyan circle on a grey background.'); + * describe('A wobbly, cyan circle on a gray background.'); * } * * function draw(){ @@ -2380,7 +2380,7 @@ p5.prototype.normal = function(x, y, z) { * myShader = createShader(vertSrc, fragSrc); * shader(myShader); * noStroke(); - * describe('A blue grid, which moves away from the mouse position, on a grey background.'); + * describe('A blue grid, which moves away from the mouse position, on a gray background.'); * } * * function draw(){ From 86cabd4af3b61e7227d9195dcc6c93cea268b2d9 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 11:46:09 +0100 Subject: [PATCH 71/94] fix some bugs in one example --- src/core/shape/vertex.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 71c8229bea..fe815cb339 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2356,7 +2356,7 @@ p5.prototype.normal = function(x, y, z) { * * void main(){ * vec4 positionVec4 = vec4(aPosition, 1.0); - * positionVec4.xyz += aDistance * aNormal; + * positionVec4.xyz += aDistance * aNormal * 2.0;; * vVertexColor = aVertexColor; * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4; * } @@ -2391,9 +2391,12 @@ p5.prototype.normal = function(x, y, z) { * beginShape(QUADS); * for (let i = 0; i < cols; i++) { * for (let j = 0; j < rows; j++) { + * + * // Calculate the cell position. * let x = i * cellSize; * let y = j * cellSize; - * fill(x/rows*255, y/cols*255, 255); + * + * 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); @@ -2402,9 +2405,9 @@ p5.prototype.normal = function(x, y, z) { * setAttribute('aDistance', min(distance, 100)); * * vertex(x, y); - * vertex(x + cellsize, y); - * vertex(x + cellsize, y1 + cellsize); - * vertex(x, y + cellsize); + * vertex(x + cellSize, y); + * vertex(x + cellSize, y + cellSize); + * vertex(x, y + cellSize); * } * } * endShape(); From 7539a7b84edaf1c293fef9164df387ca85a1babd Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 11:49:11 +0100 Subject: [PATCH 72/94] change cellSize in example --- src/core/shape/vertex.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index fe815cb339..0027509860 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2340,7 +2340,7 @@ p5.prototype.normal = function(x, y, z) { * let myShader; * const cols = 10; * const rows = 10; - * const cellSize = 9; + * const cellSize = 6; * * const vertSrc = `#version 300 es * precision mediump float; From 3b7d8f928cfeff946963b1012404e1aff53bb93b Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 15:22:40 +0100 Subject: [PATCH 73/94] add a custom attribute helper type and refactor to use it --- src/webgl/p5.Geometry.js | 84 +++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 98faa330b7..a8086da024 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -453,8 +453,7 @@ p5.Geometry = class Geometry { this.uvs.length = 0; for (const attr in this.userAttributes){ - const src = attr.concat('Src'); - delete this[src]; + this.userAttributes[attr].delete(); } this.userAttributes = {}; @@ -2016,22 +2015,79 @@ p5.Geometry = class Geometry { * @param {String} attributeName the name of the vertex attribute. * @param {Number|Number[]} data the data tied to the vertex attribute. */ - setAttribute(attributeName, data, size = data.length ? data.length : 1){ - const attributeSrc = attributeName.concat('Src'); - if (!this.hasOwnProperty(attributeSrc)){ - this[attributeSrc] = []; - this.userAttributes[attributeName] = size; + setAttribute(attributeName, data, size){ + let attr; + if (!this.userAttributes[attributeName]){ + attr = this.userAttributes[attributeName] = + this._createUserAttributeHelper(attributeName, data, this); } - if (size != this.userAttributes[attributeName]){ - p5._friendlyError(`Custom attribute ${attributeName} has been set with various data sizes. You can change it's name, - or if it was an accident, set ${attributeName} to have the same number of inputs each time!`, 'setAttribute()'); - } - if (data.length){ - this[attributeSrc].push(...data); + attr = this.userAttributes[attributeName] + if (size){ + attr.pushDirect(data); } else{ - this[attributeSrc].push(data); + attr.setCurrentData(data); + attr.pushCurrentData(); } } + + _createUserAttributeHelper(attributeName, data){ + const geometryInstace = this; + const attr = this.userAttributes[attributeName] = { + name: attributeName, + currentData: data, + dataSize: data.length ? data.length : 1, + geometry: geometryInstace, + // Getters + getCurrentData(){ + return this.currentData; + }, + getDataSize() { + return this.dataSize; + }, + getSrcName() { + const src = this.name.concat('Src'); + return src; + }, + getDstName() { + const dst = this.name.concat('Buffer'); + return dst; + }, + getSrcArray() { + const srcName = this.getSrcName(); + return this.geometry[srcName]; + }, + //Setters + setCurrentData(data) { + const size = data.length ? data.length : 1; + if (size != this.getDataSize()){ + p5._friendlyError(`Custom attribute ${this.name} has been set with various data sizes. You can change it's name, or if it was an accident, set ${this.name} to have the same number of inputs each time!`, 'setAttribute()'); + } + this.currentData = data; + }, + // Utilities + pushCurrentData(){ + const data = this.getCurrentData(); + this.pushDirect(data); + }, + pushDirect(data) { + if (data.length){ + this.getSrcArray().push(...data); + } else{ + this.getSrcArray().push(data); + } + }, + resetSrcArray(){ + this.geometry[this.getSrcName] = []; + }, + delete() { + const srcName = this.getSrcName(); + delete this.geometry[srcName]; + delete this; + } + }; + this[attr.getSrcName()] = []; + return this.userAttributes[attributeName]; + } }; /** From 3bcce8bba48a42f6fb74423719ff30783a0b4b60 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 15:23:41 +0100 Subject: [PATCH 74/94] Refactor to use the geometry user attributes helper object --- src/webgl/p5.RendererGL.Immediate.js | 95 +++++++++++++--------------- 1 file changed, 44 insertions(+), 51 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index a9386be051..4664a300b8 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -34,14 +34,7 @@ p5.RendererGL.prototype.beginShape = function(mode) { this.immediateMode.shapeMode = mode !== undefined ? mode : constants.TESS; if (this._useUserAttributes === true){ - for (const name in this.userAttributes){ - delete this.immediateMode.geometry[name]; - delete this.immediateBufferStrides[name.concat('Src')]; - } - this.userAttributes = {}; - this._useUserAttributes = false; - this.tessyVertexSize = 12; - this.immediateMode.buffers.user = []; + this._resetUserAttributes(); } this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; @@ -124,18 +117,16 @@ p5.RendererGL.prototype.vertex = function(x, y) { this.immediateMode.geometry.vertices.push(vert); this.immediateMode.geometry.vertexNormals.push(this._currentNormal); - for (const attr in this.userAttributes){ + for (const attrName in this.immediateMode.geometry.userAttributes){ const geom = this.immediateMode.geometry; + const attr = geom.userAttributes[attrName]; const verts = geom.vertices; - const data = this.userAttributes[attr]; - const src = attr.concat('Src'); - const size = data.length ? data.length : 1; - if (!geom.hasOwnProperty(src) && verts.length > 1) { - const numMissingValues = size * (verts.length - 1); + if (!attr.getSrcArray() && verts.length > 1) { + const numMissingValues = attr.getDataSize() * (verts.length - 1); const missingValues = Array(numMissingValues).fill(0); - geom.setAttribute(attr, missingValues, size); + attr.pushDirect(missingValues); } - geom.setAttribute(attr, data); + attr.pushCurrentData(); } const vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; @@ -191,27 +182,35 @@ p5.RendererGL.prototype.vertex = function(x, y) { }; p5.RendererGL.prototype.setAttribute = function(attributeName, data){ - // if attributeName is in one of default, throw some warning if(!this._useUserAttributes){ this._useUserAttributes = true; - this.userAttributes = {}; - } - const buff = attributeName.concat('Buffer'); - const attributeSrc = attributeName.concat('Src'); const size = data.length ? data.length : 1; - if (!this.userAttributes.hasOwnProperty(attributeName)){ - this.tessyVertexSize += size; - this.immediateBufferStrides[attributeSrc] = size; } - this.userAttributes[attributeName] = data; - const bufferExists = this.immediateMode - .buffers - .user - .some(buffer => buffer.dst === buff); - if(!bufferExists){ + const attrExists = this.immediateMode.geometry.userAttributes[attributeName]; + let attr; + if (attrExists){ + attr = this.immediateMode.geometry.userAttributes[attributeName]; + } + else { + attr = this.immediateMode.geometry._createUserAttributeHelper(attributeName, data); + this.tessyVertexSize += attr.getDataSize(); + this.immediateBufferStrides[attr.getSrcName()] = attr.getDataSize(); this.immediateMode.buffers.user.push( - new p5.RenderBuffer(size, attributeSrc, buff, attributeName, this) + new p5.RenderBuffer(attr.getDataSize(), attr.getSrcName(), attr.getDstName(), attributeName, this) ); } + attr.setCurrentData(data); +}; + +p5.RendererGL.prototype._resetUserAttributes = function(){ + const attributes = this.immediateMode.geometry.userAttributes; + for (const attrName in attributes){ + const attr = attributes[attrName]; + delete this.immediateBufferStrides[attr.getSrcName()]; + attr.delete(); + } + this._userUserAttributes = false; + this.tessyVertexSize = 12; + this.immediateMode.buffers.user = []; }; /** @@ -484,18 +483,12 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals[i].y, this.immediateMode.geometry.vertexNormals[i].z ); - for (const attr in this.userAttributes){ - const attributeSrc = attr.concat('Src'); - const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; - const start = i * size; - const end = start + size; - if (this.immediateMode.geometry[attributeSrc]){ - const vals = this.immediateMode.geometry[attributeSrc].slice(start, end); - contours[contours.length-1].push(...vals); - } else{ - delete this.userAttributes[attr]; - this.tessyVertexSize -= size; - } + for (const attrName in this.immediateMode.geometry.userAttributes){ + const attr = this.immediateMode.geometry.userAttributes[attrName]; + const start = i * attr.getDataSize(); + const end = start + attr.getDataSize(); + const vals = attr.getSrcArray().slice(start, end); + contours[contours.length-1].push(...vals); } } const polyTriangles = this._triangulate(contours); @@ -503,9 +496,9 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertices = []; this.immediateMode.geometry.vertexNormals = []; this.immediateMode.geometry.uvs = []; - for (const attr in this.userAttributes){ - const attributeSrc = attr.concat('Src'); - this.immediateMode.geometry[attributeSrc] = []; + for (const attrName in this.immediateMode.geometry.userAttributes){ + const attr = this.immediateMode.geometry.userAttributes[attrName]; + attr.resetSrcArray(); } const colors = []; for ( @@ -517,12 +510,12 @@ p5.RendererGL.prototype._tesselateShape = function() { this.normal(...polyTriangles.slice(j + 9, j + 12)); { let offset = 12; - for (const attr in this.userAttributes){ - const size = this.userAttributes[attr].length ? this.userAttributes[attr].length : 1; + for (const attrName in this.immediateMode.geometry.userAttributes){ + const attr = this.immediateMode.geometry.userAttributes[attrName]; const start = j + offset; - const end = start + size; - this.setAttribute(attr, polyTriangles.slice(start, end), size); - offset += size; + const end = start + attr.getDataSize(); + attr.setCurrentData(polyTriangles.slice(start, end)) + offset += attr.getDataSize(); } } this.vertex(...polyTriangles.slice(j, j + 5)); From 0d6b6c9ea9538aa09c0abc98ebcce06e95e95da8 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 15:56:34 +0100 Subject: [PATCH 75/94] added getName to user attributes --- src/webgl/p5.Geometry.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index a8086da024..3f6d9cac4d 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -2038,6 +2038,9 @@ p5.Geometry = class Geometry { dataSize: data.length ? data.length : 1, geometry: geometryInstace, // Getters + getName(){ + return this.name; + }, getCurrentData(){ return this.currentData; }, From 1b9e569eb9b2e7edb34618165afa3f088daf086b Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 15:57:54 +0100 Subject: [PATCH 76/94] use new user Attribute Helper implementation of setAttribute --- src/webgl/p5.RendererGL.Retained.js | 42 +++++++++-------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 5adff5010d..86d0358ef2 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -116,19 +116,11 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { ? model.lineVertices.length / 3 : 0; - for (const attr in model.userAttributes){ - const buff = attr.concat('Buffer'); - const attributeSrc = attr.concat('Src'); - const size = model.userAttributes[attr]; - const bufferExists = this.retainedMode - .buffers - .user - .some(buffer => buffer.dst === buff); - if (!bufferExists){ - this.retainedMode.buffers.user.push( - new p5.RenderBuffer(size, attributeSrc, buff, attr, this) - ); - } + for (const attrName in model.userAttributes){ + const attr = model.userAttributes[attrName]; + this.retainedMode.buffers.user.push( + new p5.RenderBuffer(attr.getDataSize(), attr.getSrcName(), attr.getDstName(), attr.getName(), this) + ); } return buffers; }; @@ -155,16 +147,12 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { buff._prepareBuffer(geometry, fillShader); } for (const buff of this.retainedMode.buffers.user){ - if(!geometry.model[buff.src]){ - continue; - } - const adjustedLength = geometry.model[buff.src].length / buff.size; + const attr = geometry.model.userAttributes[buff.attr]; + const adjustedLength = attr.getSrcArray().length / attr.getDataSize(); if(adjustedLength > geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with more values than vertices. - This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with more values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } else if(adjustedLength < geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with fewer values than vertices. - This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with fewer values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } buff._prepareBuffer(geometry, fillShader); } @@ -189,16 +177,12 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { buff._prepareBuffer(geometry, strokeShader); } for (const buff of this.retainedMode.buffers.user){ - if(!geometry.model[buff.src]){ - continue; - } - const adjustedLength = geometry.model[buff.src].length / buff.size; + const attr = geometry.model.userAttributes[buff.attr]; + const adjustedLength = attr.getSrcArray().length / attr.getDataSize(); if(adjustedLength > geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with more values than vertices. - This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with more values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } else if(adjustedLength < geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute with fewer values than vertices. - This is probably from directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with fewer values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } buff._prepareBuffer(geometry, strokeShader); } From 69e3f0817eb16273daef5f0934acf3ed62181e26 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Sat, 28 Sep 2024 16:01:49 +0100 Subject: [PATCH 77/94] use new implementation for setAttribute with helper --- src/webgl/GeometryBuilder.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 0bc98e1b99..8aaaec7188 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -68,15 +68,15 @@ class GeometryBuilder { if (attr in inputAttrs){ continue; } - const size = builtAttrs[attr]; + const size = builtAttrs[attr].getDataSize(); const numMissingValues = size * input.vertices.length; const missingValues = Array(numMissingValues).fill(0); this.geometry.setAttribute(attr, missingValues, size); } for (const attr in inputAttrs){ const src = attr.concat('Src'); - const data = input[src]; - const size = inputAttrs[attr]; + const data = input.userAttributes[attr].getSrcArray(); + const size = inputAttrs[attr].getDataSize(); if (numPreviousVertices > 0 && !(attr in this.geometry.userAttributes)){ const numMissingValues = size * numPreviousVertices; const missingValues = Array(numMissingValues).fill(0); From 0ccfb5d274b6166428cdb44873c71b76082ae7d2 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 12:37:39 +0100 Subject: [PATCH 78/94] fix silling in missing values for custom attribs --- src/webgl/p5.RendererGL.Immediate.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 4664a300b8..e6bb51c2a1 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -121,7 +121,7 @@ p5.RendererGL.prototype.vertex = function(x, y) { const geom = this.immediateMode.geometry; const attr = geom.userAttributes[attrName]; const verts = geom.vertices; - if (!attr.getSrcArray() && verts.length > 1) { + if (attr.getSrcArray().length === 0 && verts.length > 1) { const numMissingValues = attr.getDataSize() * (verts.length - 1); const missingValues = Array(numMissingValues).fill(0); attr.pushDirect(missingValues); @@ -184,6 +184,7 @@ p5.RendererGL.prototype.vertex = function(x, y) { p5.RendererGL.prototype.setAttribute = function(attributeName, data){ if(!this._useUserAttributes){ this._useUserAttributes = true; + this.immediateMode.geometry.userAttributes = {}; } const attrExists = this.immediateMode.geometry.userAttributes[attributeName]; let attr; @@ -205,11 +206,12 @@ p5.RendererGL.prototype._resetUserAttributes = function(){ const attributes = this.immediateMode.geometry.userAttributes; for (const attrName in attributes){ const attr = attributes[attrName]; - delete this.immediateBufferStrides[attr.getSrcName()]; + delete this.immediateBufferStrides[attrName]; attr.delete(); } this._userUserAttributes = false; this.tessyVertexSize = 12; + this.immediateMode.geometry.userAttributes = {}; this.immediateMode.buffers.user = []; }; @@ -512,10 +514,11 @@ p5.RendererGL.prototype._tesselateShape = function() { let offset = 12; for (const attrName in this.immediateMode.geometry.userAttributes){ const attr = this.immediateMode.geometry.userAttributes[attrName]; + const size = attr.getDataSize(); const start = j + offset; - const end = start + attr.getDataSize(); - attr.setCurrentData(polyTriangles.slice(start, end)) - offset += attr.getDataSize(); + const end = start + size; + this.setAttribute(attrName, polyTriangles.slice(start, end), size); + offset += size; } } this.vertex(...polyTriangles.slice(j, j + 5)); From f5c667f2d1c48b6f9c3ee67717647f6279a5bd96 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 12:38:04 +0100 Subject: [PATCH 79/94] fix custom attributes geom building --- src/webgl/GeometryBuilder.js | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 8aaaec7188..9366bdf786 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -64,25 +64,26 @@ class GeometryBuilder { const builtAttrs = this.geometry.userAttributes; const numPreviousVertices = this.geometry.vertices.length - input.vertices.length; - for (const attr in builtAttrs){ - if (attr in inputAttrs){ + for (const attrName in builtAttrs){ + if (attrName in inputAttrs){ continue; } - const size = builtAttrs[attr].getDataSize(); + const attr = builtAttrs[attrName] + const size = attr.getDataSize(); const numMissingValues = size * input.vertices.length; const missingValues = Array(numMissingValues).fill(0); - this.geometry.setAttribute(attr, missingValues, size); + attr.pushDirect(missingValues); } - for (const attr in inputAttrs){ - const src = attr.concat('Src'); - const data = input.userAttributes[attr].getSrcArray(); - const size = inputAttrs[attr].getDataSize(); - if (numPreviousVertices > 0 && !(attr in this.geometry.userAttributes)){ + for (const attrName in inputAttrs){ + const attr = inputAttrs[attrName]; + const data = attr.getSrcArray(); + const size = attr.getDataSize(); + if (numPreviousVertices > 0 && !(attrName in builtAttrs)){ const numMissingValues = size * numPreviousVertices; const missingValues = Array(numMissingValues).fill(0); - this.geometry.setAttribute(attr, missingValues, size); + this.geometry.setAttribute(attrName, missingValues, size); } - this.geometry.setAttribute(attr, data, size); + this.geometry.setAttribute(attrName, data, size); } if (this.renderer._doFill) { From 550f666a5fba2788af3e05ff28db626200848f6b Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 12:39:14 +0100 Subject: [PATCH 80/94] add size parameter for geometry.setattribute --- src/webgl/p5.Geometry.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 3f6d9cac4d..d62d847d43 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -452,8 +452,8 @@ p5.Geometry = class Geometry { this.vertexNormals.length = 0; this.uvs.length = 0; - for (const attr in this.userAttributes){ - this.userAttributes[attr].delete(); + for (const attrName in this.userAttributes){ + this.userAttributes[attrName].delete(); } this.userAttributes = {}; @@ -2019,7 +2019,7 @@ p5.Geometry = class Geometry { let attr; if (!this.userAttributes[attributeName]){ attr = this.userAttributes[attributeName] = - this._createUserAttributeHelper(attributeName, data, this); + this._createUserAttributeHelper(attributeName, data, size); } attr = this.userAttributes[attributeName] if (size){ @@ -2030,12 +2030,12 @@ p5.Geometry = class Geometry { } } - _createUserAttributeHelper(attributeName, data){ + _createUserAttributeHelper(attributeName, data, size){ const geometryInstace = this; const attr = this.userAttributes[attributeName] = { name: attributeName, currentData: data, - dataSize: data.length ? data.length : 1, + dataSize: size ? size : data.length ? data.length : 1, geometry: geometryInstace, // Getters getName(){ From 5fc85022e28f1a67ba50749c90f283d0086576be Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 12:53:35 +0100 Subject: [PATCH 81/94] Typo --- src/webgl/p5.Geometry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index d62d847d43..23a92c7330 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -2031,12 +2031,12 @@ p5.Geometry = class Geometry { } _createUserAttributeHelper(attributeName, data, size){ - const geometryInstace = this; + const geometryInstance = this; const attr = this.userAttributes[attributeName] = { name: attributeName, currentData: data, dataSize: size ? size : data.length ? data.length : 1, - geometry: geometryInstace, + geometry: geometryInstance, // Getters getName(){ return this.name; From 2fc756764b40806f513ff971b641af9d880b2b03 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 13:25:36 +0100 Subject: [PATCH 82/94] Fixed documentation --- src/core/shape/vertex.js | 3 +-- src/webgl/p5.Geometry.js | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 0027509860..02b964c199 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2414,8 +2414,7 @@ p5.prototype.normal = function(x, y, z) { * } * *
-/ -/** + * * @method setAttribute * @param {String} attributeName the name of the vertex attribute. * @param {Number|Number[]} data the data tied to the vertex attribute. diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 98faa330b7..8b60da6994 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -2010,11 +2010,10 @@ p5.Geometry = class Geometry { * } *
*
-/ -/** - * @method setAttribute + * * @param {String} attributeName the name of the vertex attribute. * @param {Number|Number[]} data the data tied to the vertex attribute. + * @param {Number} size optional size of each unit of data */ setAttribute(attributeName, data, size = data.length ? data.length : 1){ const attributeSrc = attributeName.concat('Src'); From 1244d808d38ca2d8bc5309253536a81383868596 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 13:43:25 +0100 Subject: [PATCH 83/94] remove auto current data setting --- src/webgl/p5.Geometry.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 23a92c7330..214b3f90c6 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -2021,7 +2021,7 @@ p5.Geometry = class Geometry { attr = this.userAttributes[attributeName] = this._createUserAttributeHelper(attributeName, data, size); } - attr = this.userAttributes[attributeName] + attr = this.userAttributes[attributeName]; if (size){ attr.pushDirect(data); } else{ @@ -2034,7 +2034,6 @@ p5.Geometry = class Geometry { const geometryInstance = this; const attr = this.userAttributes[attributeName] = { name: attributeName, - currentData: data, dataSize: size ? size : data.length ? data.length : 1, geometry: geometryInstance, // Getters From daf9cf04534a6a10e010276728a0d6a89f760a4a Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 14:50:56 +0100 Subject: [PATCH 84/94] changed method call for more direct version --- src/webgl/p5.RendererGL.Immediate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index e6bb51c2a1..9e6695ee04 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -517,7 +517,7 @@ p5.RendererGL.prototype._tesselateShape = function() { const size = attr.getDataSize(); const start = j + offset; const end = start + size; - this.setAttribute(attrName, polyTriangles.slice(start, end), size); + attr.setCurrentData(polyTriangles.slice(start, end)); offset += size; } } From 4ad477034920fd4c05540ba1a0f6420c7c11c50f Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 14:51:19 +0100 Subject: [PATCH 85/94] Fixed bug where geometry custom src array was not reset properly --- src/webgl/p5.Geometry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 214b3f90c6..042aca84e6 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -2079,7 +2079,7 @@ p5.Geometry = class Geometry { } }, resetSrcArray(){ - this.geometry[this.getSrcName] = []; + this.geometry[this.getSrcName()] = []; }, delete() { const srcName = this.getSrcName(); From a91088f0bc00f3f004f408f807823b64e9d6879f Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 14:51:54 +0100 Subject: [PATCH 86/94] moved this.tessyVertexSize declaration into _initTessy() --- src/webgl/p5.RendererGL.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index ec7d52e1c6..65375fa48e 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -671,7 +671,6 @@ p5.RendererGL = class RendererGL extends Renderer { // Used to distinguish between user calls to vertex() and internal calls this.isProcessingVertices = false; - this.tessyVertexSize = 12; this._tessy = this._initTessy(); this.fontInfos = {}; @@ -2419,6 +2418,7 @@ p5.RendererGL = class RendererGL extends Renderer { return p; } _initTessy() { + this.tessyVertexSize = 12; // function called for each vertex of tesselator output function vertexCallback(data, polyVertArray) { for (const element of data) { From cdf73007f8b7558bda424169a9123e7e54d1a733 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 15:50:19 +0100 Subject: [PATCH 87/94] quadraticVertex working again. --- src/webgl/3d_primitives.js | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 0599c24b6d..fd2d00aaf3 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3215,14 +3215,13 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { // Do the same for custom (user defined) attributes const userAttributes = {}; - for (const attr in immediateGeometry.userAttributes){ - const attributeSrc = attr.concat('Src'); - const size = immediateGeometry.userAttributes[attr]; - const curData = this.userAttributes[attr]; - userAttributes[attr] = []; - for (m = 0; m < 3; m++) userAttributes[attr].push([]); - userAttributes[attr][0] = immediateGeometry[attributeSrc].slice(-size); - userAttributes[attr][2] = curData; + for (const attrName in immediateGeometry.userAttributes){ + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + userAttributes[attrName] = []; + for (m = 0; m < 3; m++) userAttributes[attrName].push([]); + userAttributes[attrName][0] = attr.getSrcArray().slice(-size); + userAttributes[attrName][2] = attr.getCurrentData(); } if (argLength === 4) { @@ -3245,11 +3244,12 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { strokeColors[0][k] * (1-d0) + strokeColors[2][k] * d0 ); } - for (const attr in immediateGeometry.userAttributes){ - const size = immediateGeometry.userAttributes[attr]; + for (const attrName in immediateGeometry.userAttributes){ + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); for (let k = 0; k < size; k++){ - userAttributes[attr][1].push( - userAttributes[attr][0][k] * (1-d0) + userAttributes[attr][2][k] * d0 + userAttributes[attrName][1].push( + userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][2][k] * d0 ); } } @@ -3270,15 +3270,16 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { _y += w_y[m] * this._lookUpTableQuadratic[i][m]; } - for (const attr in immediateGeometry.userAttributes) { - const size = immediateGeometry.userAttributes[attr]; - this.userAttributes[attr] = Array(size).fill(0); + for (const attrName in immediateGeometry.userAttributes) { + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + let newValues = Array(size).fill(0); for (let m = 0; m < 3; m++){ for (let k = 0; k < size; k++){ - this.userAttributes[attr][k] += - this._lookUpTableQuadratic[i][m] * userAttributes[attr][m][k]; + newValues[k] += this._lookUpTableQuadratic[i][m] * userAttributes[attrName][m][k]; } - } + } + attr.setCurrentData(newValues); } this.vertex(_x, _y); } From 556502328f7c70ef204ead5aef8175275738b8d6 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 15:59:16 +0100 Subject: [PATCH 88/94] custom attributes also on 6 vertices quadratic vertex --- src/webgl/3d_primitives.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index fd2d00aaf3..96cdff2be9 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3287,6 +3287,10 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[2]; this.curStrokeColor = strokeColors[2]; + for (const attrName in immediateGeometry.userAttributes) { + const attr = immediateGeometry.userAttributes[attrName]; + attr.setCurrentData(userAttributes[attrName][2]); + } this.immediateMode._quadraticVertex[0] = args[2]; this.immediateMode._quadraticVertex[1] = args[3]; } else if (argLength === 6) { @@ -3311,6 +3315,16 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { ); } + for (const attrName in immediateGeometry.userAttributes){ + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + for (let k = 0; k < size; k++){ + userAttributes[attrName][1].push( + userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][2][k] * d0 + ); + } + } + for (i = 0; i < LUTLength; i++) { // Interpolate colors using control points this.curFillColor = [0, 0, 0, 0]; @@ -3327,12 +3341,27 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { _y += w_y[m] * this._lookUpTableQuadratic[i][m]; _z += w_z[m] * this._lookUpTableQuadratic[i][m]; } + for (const attrName in immediateGeometry.userAttributes) { + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + let newValues = Array(size).fill(0); + for (let m = 0; m < 3; m++){ + for (let k = 0; k < size; k++){ + newValues[k] += this._lookUpTableQuadratic[i][m] * userAttributes[attrName][m][k]; + } + } + attr.setCurrentData(newValues); + } this.vertex(_x, _y, _z); } // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[2]; this.curStrokeColor = strokeColors[2]; + for (const attrName in immediateGeometry.userAttributes) { + const attr = immediateGeometry.userAttributes[attrName]; + attr.setCurrentData(userAttributes[attrName][2]); + } this.immediateMode._quadraticVertex[0] = args[3]; this.immediateMode._quadraticVertex[1] = args[4]; this.immediateMode._quadraticVertex[2] = args[5]; From 6b183b40860bc06e6df467b330e79c9fbc64e7ff Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 16:13:56 +0100 Subject: [PATCH 89/94] bezier vertex interpolates custom attributes --- src/webgl/3d_primitives.js | 68 +++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 96cdff2be9..05b067937e 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3021,14 +3021,13 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { // Do the same for custom attributes const userAttributes = {}; - for (const attr in immediateGeometry.userAttributes){ - const attributeSrc = attr.concat('Src'); - const size = immediateGeometry.userAttributes[attr]; - const curData = this.userAttributes[attr]; - userAttributes[attr] = []; - for (m = 0; m < 4; m++) userAttributes[attr].push([]); - userAttributes[attr][0] = immediateGeometry[attributeSrc].slice(-size); - userAttributes[attr][3] = curData; + for (const attrName in immediateGeometry.userAttributes){ + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + userAttributes[attrName] = []; + for (m = 0; m < 4; m++) userAttributes[attrName].push([]); + userAttributes[attrName][0] = attr.getSrcArray().slice(-size); + userAttributes[attrName][3] = attr.getCurrentData(); } if (argLength === 6) { @@ -3058,14 +3057,14 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2) ); } - for (const attr in immediateGeometry.userAttributes){ - const size = immediateGeometry.userAttributes[attr]; + for (const attrName in immediateGeometry.userAttributes){ + const size = immediateGeometry.userAttributes[attrName].getDataSize(); for (k = 0; k < size; k++){ - userAttributes[attr][1].push( - userAttributes[attr][0][k] * (1-d0) + userAttributes[attr][3][k] * d0 + userAttributes[attrName][1].push( + userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][3][k] * d0 ); - userAttributes[attr][2].push( - userAttributes[attr][0][k] * (1-d2) + userAttributes[attr][3][k] * d2 + userAttributes[attrName][2].push( + userAttributes[attrName][0][k] * (1-d2) + userAttributes[attrName][3][k] * d2 ); } } @@ -3085,21 +3084,26 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { _x += w_x[m] * this._lookUpTableBezier[i][m]; _y += w_y[m] * this._lookUpTableBezier[i][m]; } - for (const attr in immediateGeometry.userAttributes){ - const size = immediateGeometry.userAttributes[attr]; - this.userAttributes[attr] = Array(size).fill(0); + for (const attrName in immediateGeometry.userAttributes){ + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + let newValues = Array(size).fill(0); for (let m = 0; m < 4; m++){ for (let k = 0; k < size; k++){ - this.userAttributes[attr][k] += - this._lookUpTableBezier[i][m] * userAttributes[attr][m][k]; + newValues[k] += this._lookUpTableBezier[i][m] * userAttributes[attrName][m][k]; } } + attr.setCurrentData(newValues); } this.vertex(_x, _y); } // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[3]; this.curStrokeColor = strokeColors[3]; + for (const attrName in immediateGeometry.userAttributes) { + const attr = immediateGeometry.userAttributes[attrName]; + attr.setCurrentData(userAttributes[attrName][2]); + } this.immediateMode._bezierVertex[0] = args[4]; this.immediateMode._bezierVertex[1] = args[5]; } else if (argLength === 9) { @@ -3130,6 +3134,17 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2) ); } + for (const attrName in immediateGeometry.userAttributes){ + const size = immediateGeometry.userAttributes[attrName].getDataSize(); + for (k = 0; k < size; k++){ + userAttributes[attrName][1].push( + userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][3][k] * d0 + ); + userAttributes[attrName][2].push( + userAttributes[attrName][0][k] * (1-d2) + userAttributes[attrName][3][k] * d2 + ); + } + } for (let i = 0; i < LUTLength; i++) { // Interpolate colors using control points this.curFillColor = [0, 0, 0, 0]; @@ -3146,11 +3161,26 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { _y += w_y[m] * this._lookUpTableBezier[i][m]; _z += w_z[m] * this._lookUpTableBezier[i][m]; } + for (const attrName in immediateGeometry.userAttributes){ + const attr = immediateGeometry.userAttributes[attrName]; + const size = attr.getDataSize(); + let newValues = Array(size).fill(0); + for (let m = 0; m < 4; m++){ + for (let k = 0; k < size; k++){ + newValues[k] += this._lookUpTableBezier[i][m] * userAttributes[attrName][m][k]; + } + } + attr.setCurrentData(newValues); + } this.vertex(_x, _y, _z); } // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[3]; this.curStrokeColor = strokeColors[3]; + for (const attrName in immediateGeometry.userAttributes) { + const attr = immediateGeometry.userAttributes[attrName]; + attr.setCurrentData(userAttributes[attrName][2]); + } this.immediateMode._bezierVertex[0] = args[6]; this.immediateMode._bezierVertex[1] = args[7]; this.immediateMode._bezierVertex[2] = args[8]; From cff40744b890f4295fa11a62d3af9843b85edb60 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 16:55:07 +0100 Subject: [PATCH 90/94] updated tests for new custom attribute helper object --- test/unit/webgl/p5.RendererGL.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index d8977adf0a..400a1f62b0 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -2511,18 +2511,20 @@ suite('p5.RendererGL', function() { myp5.beginShape(); myp5.setAttribute('aCustom', 1); - myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.setAttribute('aCustomVec3', [1, 2, 3]); myp5.vertex(0,0,0); - assert.deepEqual(myp5._renderer.userAttributes,{ - aCustom: 1, - aCustomVec3: [1,2,3] + expect(myp5._renderer.immediateMode.geometry.userAttributes.aCustom).to.containSubset({ + name: 'aCustom', + currentData: 1, + dataSize: 1 + }); + expect(myp5._renderer.immediateMode.geometry.userAttributes.aCustomVec3).to.containSubset({ + name: 'aCustomVec3', + currentData: [1, 2, 3], + dataSize: 3 }); assert.deepEqual(myp5._renderer.immediateMode.geometry.aCustomSrc, [1]); assert.deepEqual(myp5._renderer.immediateMode.geometry.aCustomVec3Src, [1,2,3]); - assert.deepEqual(myp5._renderer.immediateMode.geometry.userAttributes, { - aCustom: 1, - aCustomVec3: 3 - }); expect(myp5._renderer.immediateMode.buffers.user).to.containSubset([ { size: 1, @@ -2538,7 +2540,6 @@ suite('p5.RendererGL', function() { } ]); myp5.endShape(); - } ); test('Immediate mode data and buffers deleted after beginShape', @@ -2555,7 +2556,6 @@ suite('p5.RendererGL', function() { assert.isUndefined(myp5._renderer.immediateMode.geometry.aCustomSrc); assert.isUndefined(myp5._renderer.immediateMode.geometry.aCustomVec3Src); assert.deepEqual(myp5._renderer.immediateMode.geometry.userAttributes, {}); - assert.deepEqual(myp5._renderer.userAttributes, {}); assert.deepEqual(myp5._renderer.immediateMode.buffers.user, []); myp5.endShape(); } @@ -2575,7 +2575,6 @@ suite('p5.RendererGL', function() { const myGeo = myp5.endGeometry(); assert.deepEqual(immediateCopy.aCustomSrc, myGeo.aCustomSrc); assert.deepEqual(immediateCopy.aCustomVec3Src, myGeo.aCustomVec3Src); - assert.deepEqual(immediateCopy.userAttributes, myGeo.userAttributes); } ); test('Retained mode buffers are created for rendering', @@ -2635,7 +2634,7 @@ suite('p5.RendererGL', function() { myp5.vertex(1,0,0); myp5.endShape(); console.log = oldLog; - expect(logs.join('\n')).to.match(/Custom attribute aCustom has been set with various data sizes/); + expect(logs.join('\n')).to.match(/Custom attribute 'aCustom' has been set with various data sizes/); } ); test('Friendly error too many values set', @@ -2651,7 +2650,7 @@ suite('p5.RendererGL', function() { myGeo.setAttribute('aCustom', 2); myp5.model(myGeo); console.log = oldLog; - expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute with more values than vertices./); + expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute 'aCustom' with more values than vertices./); } ); test('Friendly error if too few values set', @@ -2667,7 +2666,7 @@ suite('p5.RendererGL', function() { myGeo.setAttribute('aCustom', 1); myp5.model(myGeo); console.log = oldLog; - expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute with fewer values than vertices./); + expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute 'aCustom' with fewer values than vertices./); } ); }) From b086feac22cd82c1fda30eadee0e45f434097331 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 16:56:21 +0100 Subject: [PATCH 91/94] updated documentation --- src/webgl/p5.Geometry.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 042aca84e6..702fbdd75b 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -2009,11 +2009,11 @@ p5.Geometry = class Geometry { * } * *
-/ -/** + * * @method setAttribute * @param {String} attributeName the name of the vertex attribute. * @param {Number|Number[]} data the data tied to the vertex attribute. + * @param {Number} [size] optional size of each unit of data. */ setAttribute(attributeName, data, size){ let attr; @@ -2062,7 +2062,7 @@ p5.Geometry = class Geometry { setCurrentData(data) { const size = data.length ? data.length : 1; if (size != this.getDataSize()){ - p5._friendlyError(`Custom attribute ${this.name} has been set with various data sizes. You can change it's name, or if it was an accident, set ${this.name} to have the same number of inputs each time!`, 'setAttribute()'); + p5._friendlyError(`Custom attribute '${this.name}' has been set with various data sizes. You can change it's name, or if it was an accident, set '${this.name}' to have the same number of inputs each time!`, 'setAttribute()'); } this.currentData = data; }, From 9ab620f5d2c685b02c65909d6bc1aee31cfe213a Mon Sep 17 00:00:00 2001 From: 23036879 Date: Mon, 30 Sep 2024 16:56:58 +0100 Subject: [PATCH 92/94] Put quote marks in error message --- src/webgl/p5.RendererGL.Retained.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 86d0358ef2..6bcb3e8a2a 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -150,9 +150,9 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { const attr = geometry.model.userAttributes[buff.attr]; const adjustedLength = attr.getSrcArray().length / attr.getDataSize(); if(adjustedLength > geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with more values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom attribute '${attr.getName()}' with more values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } else if(adjustedLength < geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with fewer values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom attribute '${attr.getName()}' with fewer values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); } buff._prepareBuffer(geometry, fillShader); } From 0da4073f33099df57ca0172667ce42f2583471b4 Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 1 Oct 2024 10:49:44 +0100 Subject: [PATCH 93/94] changed 'setAttribute()' to 'vertexProperty()' and all references, variables & helpers to match --- src/core/shape/vertex.js | 32 +++--- src/webgl/3d_primitives.js | 144 +++++++++++++-------------- src/webgl/GeometryBuilder.js | 28 +++--- src/webgl/p5.Geometry.js | 75 +++++++------- src/webgl/p5.RendererGL.Immediate.js | 82 +++++++-------- src/webgl/p5.RendererGL.Retained.js | 22 ++-- test/unit/visual/cases/webgl.js | 18 ++-- test/unit/webgl/p5.RendererGL.js | 52 +++++----- 8 files changed, 230 insertions(+), 223 deletions(-) diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index 02b964c199..f1dd9ff214 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -2253,26 +2253,26 @@ p5.prototype.normal = function(x, y, z) { return this; }; -/** Sets the shader's vertex attribute variables. +/** Sets the shader's vertex property or attribute variables. * - * An attribute is a variable belonging to a vertex in a shader. p5.js provides some - * default attributes, such as `aPosition`, `aNormal`, `aVertexColor`, etc. These are + * An vertex property or vertex attribute is a variable belonging to a vertex in a shader. p5.js provides some + * default properties, such as `aPosition`, `aNormal`, `aVertexColor`, etc. These are * set using vertex(), normal() - * and fill() respectively. Custom attribute data can also + * and fill() respectively. Custom properties can also * be defined within beginShape() and * endShape(). * - * The first parameter, `attributeName`, is a string with the attribute's name. - * This is the same variable name which should be declared in the shader, similar to - * `setUniform()`. + * The first parameter, `propertyName`, is a string with the property's name. + * This is the same variable name which should be declared in the shader, such as + * `in vec3 aProperty`, similar to .`setUniform()`. * - * The second parameter, `data`, is the value assigned to the attribute. This + * The second parameter, `data`, is the value assigned to the shader variable. This * value will be applied to subsequent vertices created with * vertex(). It can be a Number or an array of numbers, * and in the shader program the type can be declared according to the WebGL * specification. Common types include `float`, `vec2`, `vec3`, `vec4` or matrices. * - * See also the setAttribute() method on + * See also the vertexProperty() method on * Geometry objects. * * @example @@ -2327,7 +2327,7 @@ p5.prototype.normal = function(x, y, z) { * const yOff = 10 * noise(y + millis()/1000) - 5; * * // Apply these noise values to the following vertex. - * setAttribute('aOffset', [xOff, yOff]); + * vertexProperty('aOffset', [xOff, yOff]); * vertex(x, y); * } * endShape(CLOSE); @@ -2402,7 +2402,7 @@ p5.prototype.normal = function(x, y, z) { * let distance = dist(x1,y1, mouseX, mouseY); * * // Send the distance to the shader. - * setAttribute('aDistance', min(distance, 100)); + * vertexProperty('aDistance', min(distance, 100)); * * vertex(x, y); * vertex(x + cellSize, y); @@ -2415,14 +2415,14 @@ p5.prototype.normal = function(x, y, z) { * *
* - * @method setAttribute + * @method vertexProperty * @param {String} attributeName the name of the vertex attribute. * @param {Number|Number[]} data the data tied to the vertex attribute. */ -p5.prototype.setAttribute = function(attributeName, data){ - // this._assert3d('setAttribute'); - // p5._validateParameters('setAttribute', arguments); - this._renderer.setAttribute(attributeName, data); +p5.prototype.vertexProperty = function(attributeName, data){ + // this._assert3d('vertexProperty'); + // p5._validateParameters('vertexProperty', arguments); + this._renderer.vertexProperty(attributeName, data); }; export default p5; diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 05b067937e..eea99ee2d8 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3019,15 +3019,15 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0] = immediateGeometry.vertexStrokeColors.slice(-4); strokeColors[3] = this.curStrokeColor.slice(); - // Do the same for custom attributes - const userAttributes = {}; - for (const attrName in immediateGeometry.userAttributes){ - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); - userAttributes[attrName] = []; - for (m = 0; m < 4; m++) userAttributes[attrName].push([]); - userAttributes[attrName][0] = attr.getSrcArray().slice(-size); - userAttributes[attrName][3] = attr.getCurrentData(); + // Do the same for custom vertex properties + const userVertexProperties = {}; + for (const propName in immediateGeometry.userVertexProperties){ + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); + userVertexProperties[propName] = []; + for (m = 0; m < 4; m++) userVertexProperties[propName].push([]); + userVertexProperties[propName][0] = prop.getSrcArray().slice(-size); + userVertexProperties[propName][3] = prop.getCurrentData(); } if (argLength === 6) { @@ -3057,14 +3057,14 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2) ); } - for (const attrName in immediateGeometry.userAttributes){ - const size = immediateGeometry.userAttributes[attrName].getDataSize(); + for (const propName in immediateGeometry.userVertexProperties){ + const size = immediateGeometry.userVertexProperties[propName].getDataSize(); for (k = 0; k < size; k++){ - userAttributes[attrName][1].push( - userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][3][k] * d0 + userVertexProperties[propName][1].push( + userVertexProperties[propName][0][k] * (1-d0) + userVertexProperties[propName][3][k] * d0 ); - userAttributes[attrName][2].push( - userAttributes[attrName][0][k] * (1-d2) + userAttributes[attrName][3][k] * d2 + userVertexProperties[propName][2].push( + userVertexProperties[propName][0][k] * (1-d2) + userVertexProperties[propName][3][k] * d2 ); } } @@ -3084,25 +3084,25 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { _x += w_x[m] * this._lookUpTableBezier[i][m]; _y += w_y[m] * this._lookUpTableBezier[i][m]; } - for (const attrName in immediateGeometry.userAttributes){ - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in immediateGeometry.userVertexProperties){ + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); let newValues = Array(size).fill(0); for (let m = 0; m < 4; m++){ for (let k = 0; k < size; k++){ - newValues[k] += this._lookUpTableBezier[i][m] * userAttributes[attrName][m][k]; + newValues[k] += this._lookUpTableBezier[i][m] * userVertexProperties[propName][m][k]; } } - attr.setCurrentData(newValues); + prop.setCurrentData(newValues); } this.vertex(_x, _y); } // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[3]; this.curStrokeColor = strokeColors[3]; - for (const attrName in immediateGeometry.userAttributes) { - const attr = immediateGeometry.userAttributes[attrName]; - attr.setCurrentData(userAttributes[attrName][2]); + for (const propName in immediateGeometry.userVertexProperties) { + const prop = immediateGeometry.userVertexProperties[propName]; + prop.setCurrentData(userVertexProperties[propName][2]); } this.immediateMode._bezierVertex[0] = args[4]; this.immediateMode._bezierVertex[1] = args[5]; @@ -3134,14 +3134,14 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { strokeColors[0][k] * d2 + strokeColors[3][k] * (1-d2) ); } - for (const attrName in immediateGeometry.userAttributes){ - const size = immediateGeometry.userAttributes[attrName].getDataSize(); + for (const propName in immediateGeometry.userVertexProperties){ + const size = immediateGeometry.userVertexProperties[propName].getDataSize(); for (k = 0; k < size; k++){ - userAttributes[attrName][1].push( - userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][3][k] * d0 + userVertexProperties[propName][1].push( + userVertexProperties[propName][0][k] * (1-d0) + userVertexProperties[propName][3][k] * d0 ); - userAttributes[attrName][2].push( - userAttributes[attrName][0][k] * (1-d2) + userAttributes[attrName][3][k] * d2 + userVertexProperties[propName][2].push( + userVertexProperties[propName][0][k] * (1-d2) + userVertexProperties[propName][3][k] * d2 ); } } @@ -3161,25 +3161,25 @@ p5.RendererGL.prototype.bezierVertex = function(...args) { _y += w_y[m] * this._lookUpTableBezier[i][m]; _z += w_z[m] * this._lookUpTableBezier[i][m]; } - for (const attrName in immediateGeometry.userAttributes){ - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in immediateGeometry.userVertexProperties){ + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); let newValues = Array(size).fill(0); for (let m = 0; m < 4; m++){ for (let k = 0; k < size; k++){ - newValues[k] += this._lookUpTableBezier[i][m] * userAttributes[attrName][m][k]; + newValues[k] += this._lookUpTableBezier[i][m] * userVertexProperties[propName][m][k]; } } - attr.setCurrentData(newValues); + prop.setCurrentData(newValues); } this.vertex(_x, _y, _z); } // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[3]; this.curStrokeColor = strokeColors[3]; - for (const attrName in immediateGeometry.userAttributes) { - const attr = immediateGeometry.userAttributes[attrName]; - attr.setCurrentData(userAttributes[attrName][2]); + for (const propName in immediateGeometry.userVertexProperties) { + const prop = immediateGeometry.userVertexProperties[propName]; + prop.setCurrentData(userVertexProperties[propName][2]); } this.immediateMode._bezierVertex[0] = args[6]; this.immediateMode._bezierVertex[1] = args[7]; @@ -3243,15 +3243,15 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { strokeColors[0] = immediateGeometry.vertexStrokeColors.slice(-4); strokeColors[2] = this.curStrokeColor.slice(); - // Do the same for custom (user defined) attributes - const userAttributes = {}; - for (const attrName in immediateGeometry.userAttributes){ - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); - userAttributes[attrName] = []; - for (m = 0; m < 3; m++) userAttributes[attrName].push([]); - userAttributes[attrName][0] = attr.getSrcArray().slice(-size); - userAttributes[attrName][2] = attr.getCurrentData(); + // Do the same for user defined vertex properties + const userVertexProperties = {}; + for (const propName in immediateGeometry.userVertexProperties){ + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); + userVertexProperties[propName] = []; + for (m = 0; m < 3; m++) userVertexProperties[propName].push([]); + userVertexProperties[propName][0] = prop.getSrcArray().slice(-size); + userVertexProperties[propName][2] = prop.getCurrentData(); } if (argLength === 4) { @@ -3274,12 +3274,12 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { strokeColors[0][k] * (1-d0) + strokeColors[2][k] * d0 ); } - for (const attrName in immediateGeometry.userAttributes){ - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in immediateGeometry.userVertexProperties){ + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); for (let k = 0; k < size; k++){ - userAttributes[attrName][1].push( - userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][2][k] * d0 + userVertexProperties[propName][1].push( + userVertexProperties[propName][0][k] * (1-d0) + userVertexProperties[propName][2][k] * d0 ); } } @@ -3300,16 +3300,16 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { _y += w_y[m] * this._lookUpTableQuadratic[i][m]; } - for (const attrName in immediateGeometry.userAttributes) { - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in immediateGeometry.userVertexProperties) { + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); let newValues = Array(size).fill(0); for (let m = 0; m < 3; m++){ for (let k = 0; k < size; k++){ - newValues[k] += this._lookUpTableQuadratic[i][m] * userAttributes[attrName][m][k]; + newValues[k] += this._lookUpTableQuadratic[i][m] * userVertexProperties[propName][m][k]; } } - attr.setCurrentData(newValues); + prop.setCurrentData(newValues); } this.vertex(_x, _y); } @@ -3317,9 +3317,9 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[2]; this.curStrokeColor = strokeColors[2]; - for (const attrName in immediateGeometry.userAttributes) { - const attr = immediateGeometry.userAttributes[attrName]; - attr.setCurrentData(userAttributes[attrName][2]); + for (const propName in immediateGeometry.userVertexProperties) { + const prop = immediateGeometry.userVertexProperties[propName]; + prop.setCurrentData(userVertexProperties[propName][2]); } this.immediateMode._quadraticVertex[0] = args[2]; this.immediateMode._quadraticVertex[1] = args[3]; @@ -3345,12 +3345,12 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { ); } - for (const attrName in immediateGeometry.userAttributes){ - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in immediateGeometry.userVertexProperties){ + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); for (let k = 0; k < size; k++){ - userAttributes[attrName][1].push( - userAttributes[attrName][0][k] * (1-d0) + userAttributes[attrName][2][k] * d0 + userVertexProperties[propName][1].push( + userVertexProperties[propName][0][k] * (1-d0) + userVertexProperties[propName][2][k] * d0 ); } } @@ -3371,16 +3371,16 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { _y += w_y[m] * this._lookUpTableQuadratic[i][m]; _z += w_z[m] * this._lookUpTableQuadratic[i][m]; } - for (const attrName in immediateGeometry.userAttributes) { - const attr = immediateGeometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in immediateGeometry.userVertexProperties) { + const prop = immediateGeometry.userVertexProperties[propName]; + const size = prop.getDataSize(); let newValues = Array(size).fill(0); for (let m = 0; m < 3; m++){ for (let k = 0; k < size; k++){ - newValues[k] += this._lookUpTableQuadratic[i][m] * userAttributes[attrName][m][k]; + newValues[k] += this._lookUpTableQuadratic[i][m] * userVertexProperties[propName][m][k]; } } - attr.setCurrentData(newValues); + prop.setCurrentData(newValues); } this.vertex(_x, _y, _z); } @@ -3388,9 +3388,9 @@ p5.RendererGL.prototype.quadraticVertex = function(...args) { // so that we leave currentColor with the last value the user set it to this.curFillColor = fillColors[2]; this.curStrokeColor = strokeColors[2]; - for (const attrName in immediateGeometry.userAttributes) { - const attr = immediateGeometry.userAttributes[attrName]; - attr.setCurrentData(userAttributes[attrName][2]); + for (const propName in immediateGeometry.userVertexProperties) { + const prop = immediateGeometry.userVertexProperties[propName]; + prop.setCurrentData(userVertexProperties[propName][2]); } this.immediateMode._quadraticVertex[0] = args[3]; this.immediateMode._quadraticVertex[1] = args[4]; diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 9366bdf786..195da6b181 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -60,30 +60,30 @@ class GeometryBuilder { ); this.geometry.uvs.push(...input.uvs); - const inputAttrs = input.userAttributes; - const builtAttrs = this.geometry.userAttributes; + const inputUserVertexProps = input.userVertexProperties; + const builtUserVertexProps = this.geometry.userVertexProperties; const numPreviousVertices = this.geometry.vertices.length - input.vertices.length; - for (const attrName in builtAttrs){ - if (attrName in inputAttrs){ + for (const propName in builtUserVertexProps){ + if (propName in inputUserVertexProps){ continue; } - const attr = builtAttrs[attrName] - const size = attr.getDataSize(); + const prop = builtUserVertexProps[propName] + const size = prop.getDataSize(); const numMissingValues = size * input.vertices.length; const missingValues = Array(numMissingValues).fill(0); - attr.pushDirect(missingValues); + prop.pushDirect(missingValues); } - for (const attrName in inputAttrs){ - const attr = inputAttrs[attrName]; - const data = attr.getSrcArray(); - const size = attr.getDataSize(); - if (numPreviousVertices > 0 && !(attrName in builtAttrs)){ + for (const propName in inputUserVertexProps){ + const prop = inputUserVertexProps[propName]; + const data = prop.getSrcArray(); + const size = prop.getDataSize(); + if (numPreviousVertices > 0 && !(propName in builtUserVertexProps)){ const numMissingValues = size * numPreviousVertices; const missingValues = Array(numMissingValues).fill(0); - this.geometry.setAttribute(attrName, missingValues, size); + this.geometry.vertexProperty(propName, missingValues, size); } - this.geometry.setAttribute(attrName, data, size); + this.geometry.vertexProperty(propName, data, size); } if (this.renderer._doFill) { diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index 702fbdd75b..12689acc86 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -283,7 +283,7 @@ p5.Geometry = class Geometry { // One color per vertex representing the stroke color at that vertex this.vertexStrokeColors = []; - this.userAttributes = {}; + this.userVertexProperties = {}; // One color per line vertex, generated automatically based on // vertexStrokeColors in _edgesToVertices() @@ -452,10 +452,10 @@ p5.Geometry = class Geometry { this.vertexNormals.length = 0; this.uvs.length = 0; - for (const attrName in this.userAttributes){ - this.userAttributes[attrName].delete(); + for (const propName in this.userVertexProperties){ + this.userVertexProperties[propName].delete(); } - this.userAttributes = {}; + this.userVertexProperties = {}; this.dirtyFlags = {}; } @@ -1599,7 +1599,6 @@ p5.Geometry = class Geometry { * @chainable */ _edgesToVertices() { - // probably needs to add something in here for custom attributes this.lineVertices.clear(); this.lineTangentsIn.clear(); this.lineTangentsOut.clear(); @@ -1919,25 +1918,29 @@ p5.Geometry = class Geometry { return this; } -/** Sets the shader's vertex attribute variables. +/** Sets the shader's vertex property or attribute variables. * - * An attribute is a variable belonging to a vertex in a shader. p5.js provides some - * default attributes, such as `aPosition`, `aNormal`, `aVertexColor`, etc. Custom - * attributes can also be defined within `beginShape()` and `endShape()`. + * An vertex property or vertex attribute is a variable belonging to a vertex in a shader. p5.js provides some + * default properties, such as `aPosition`, `aNormal`, `aVertexColor`, etc. These are + * set using vertex(), normal() + * and fill() respectively. Custom properties can also + * be defined within beginShape() and + * endShape(). * - * The first parameter, `attributeName`, is a string with the attribute's name. - * This is the same variable name which should be declared in the shader, similar to - * `setUniform()`. + * The first parameter, `propertyName`, is a string with the property's name. + * This is the same variable name which should be declared in the shader, as in + * `in vec3 aProperty`, similar to .`setUniform()`. * - * The second parameter, `data`, is the value assigned to the attribute. This value + * The second parameter, `data`, is the value assigned to the shader variable. This value * will be pushed directly onto the Geometry object. There should be the same number - * of custom attribute values as vertices. + * of custom property values as vertices, this method should be invoked once for each + * vertex. * * The `data` can be a Number or an array of numbers. Tn the shader program the type * can be declared according to the WebGL specification. Common types include `float`, * `vec2`, `vec3`, `vec4` or matrices. * - * See also the global setAttribute() function. + * See also the global vertexProperty() function. * * @example *
@@ -1980,14 +1983,18 @@ p5.Geometry = class Geometry { * * // Set the roughness value for every vertex. * for (let v of geo.vertices){ + * * // convert coordinates to spherical coordinates * let spherical = cartesianToSpherical(v.x, v.y, v.z); + * + * // Set the custom roughness vertex property. * let roughness = noise(spherical.theta*5, spherical.phi*5); - * geo.setAttribute('aRoughness', roughness); + * geo.vertexProperty('aRoughness', roughness); * } * * // Use the custom shader. * shader(myShader); + * * describe('A rough pink sphere rotating on a blue background.'); * } * @@ -2010,30 +2017,30 @@ p5.Geometry = class Geometry { * *
* - * @method setAttribute - * @param {String} attributeName the name of the vertex attribute. - * @param {Number|Number[]} data the data tied to the vertex attribute. + * @method vertexProperty + * @param {String} propertyName the name of the vertex property. + * @param {Number|Number[]} data the data tied to the vertex property. * @param {Number} [size] optional size of each unit of data. */ - setAttribute(attributeName, data, size){ - let attr; - if (!this.userAttributes[attributeName]){ - attr = this.userAttributes[attributeName] = - this._createUserAttributeHelper(attributeName, data, size); + vertexProperty(propertyName, data, size){ + let prop; + if (!this.userVertexProperties[propertyName]){ + prop = this.userVertexProperties[propertyName] = + this._userVertexPropertyHelper(propertyName, data, size); } - attr = this.userAttributes[attributeName]; + prop = this.userVertexProperties[propertyName]; if (size){ - attr.pushDirect(data); + prop.pushDirect(data); } else{ - attr.setCurrentData(data); - attr.pushCurrentData(); + prop.setCurrentData(data); + prop.pushCurrentData(); } } - _createUserAttributeHelper(attributeName, data, size){ + _userVertexPropertyHelper(propertyName, data, size){ const geometryInstance = this; - const attr = this.userAttributes[attributeName] = { - name: attributeName, + const prop = this.userVertexProperties[propertyName] = { + name: propertyName, dataSize: size ? size : data.length ? data.length : 1, geometry: geometryInstance, // Getters @@ -2062,7 +2069,7 @@ p5.Geometry = class Geometry { setCurrentData(data) { const size = data.length ? data.length : 1; if (size != this.getDataSize()){ - p5._friendlyError(`Custom attribute '${this.name}' has been set with various data sizes. You can change it's name, or if it was an accident, set '${this.name}' to have the same number of inputs each time!`, 'setAttribute()'); + p5._friendlyError(`Custom vertex property '${this.name}' has been set with various data sizes. You can change it's name, or if it was an accident, set '${this.name}' to have the same number of inputs each time!`, 'vertexProperty()'); } this.currentData = data; }, @@ -2087,8 +2094,8 @@ p5.Geometry = class Geometry { delete this; } }; - this[attr.getSrcName()] = []; - return this.userAttributes[attributeName]; + this[prop.getSrcName()] = []; + return this.userVertexProperties[propertyName]; } }; diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 9e6695ee04..357c1c5527 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -33,8 +33,8 @@ import './p5.RenderBuffer'; p5.RendererGL.prototype.beginShape = function(mode) { this.immediateMode.shapeMode = mode !== undefined ? mode : constants.TESS; - if (this._useUserAttributes === true){ - this._resetUserAttributes(); + if (this._useUserVertexProperties === true){ + this._resetUserVertexProperties(); } this.immediateMode.geometry.reset(); this.immediateMode.contourIndices = []; @@ -117,16 +117,16 @@ p5.RendererGL.prototype.vertex = function(x, y) { this.immediateMode.geometry.vertices.push(vert); this.immediateMode.geometry.vertexNormals.push(this._currentNormal); - for (const attrName in this.immediateMode.geometry.userAttributes){ + for (const propName in this.immediateMode.geometry.userVertexProperties){ const geom = this.immediateMode.geometry; - const attr = geom.userAttributes[attrName]; + const prop = geom.userVertexProperties[propName]; const verts = geom.vertices; - if (attr.getSrcArray().length === 0 && verts.length > 1) { - const numMissingValues = attr.getDataSize() * (verts.length - 1); + if (prop.getSrcArray().length === 0 && verts.length > 1) { + const numMissingValues = prop.getDataSize() * (verts.length - 1); const missingValues = Array(numMissingValues).fill(0); - attr.pushDirect(missingValues); + prop.pushDirect(missingValues); } - attr.pushCurrentData(); + prop.pushCurrentData(); } const vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; @@ -181,37 +181,37 @@ p5.RendererGL.prototype.vertex = function(x, y) { return this; }; -p5.RendererGL.prototype.setAttribute = function(attributeName, data){ - if(!this._useUserAttributes){ - this._useUserAttributes = true; - this.immediateMode.geometry.userAttributes = {}; +p5.RendererGL.prototype.vertexProperty = function(propertyName, data){ + if(!this._useUserVertexProperties){ + this._useUserVertexProperties = true; + this.immediateMode.geometry.userVertexProperties = {}; } - const attrExists = this.immediateMode.geometry.userAttributes[attributeName]; - let attr; - if (attrExists){ - attr = this.immediateMode.geometry.userAttributes[attributeName]; + const propertyExists = this.immediateMode.geometry.userVertexProperties[propertyName]; + let prop; + if (propertyExists){ + prop = this.immediateMode.geometry.userVertexProperties[propertyName]; } else { - attr = this.immediateMode.geometry._createUserAttributeHelper(attributeName, data); - this.tessyVertexSize += attr.getDataSize(); - this.immediateBufferStrides[attr.getSrcName()] = attr.getDataSize(); + prop = this.immediateMode.geometry._userVertexPropertyHelper(propertyName, data); + this.tessyVertexSize += prop.getDataSize(); + this.immediateBufferStrides[prop.getSrcName()] = prop.getDataSize(); this.immediateMode.buffers.user.push( - new p5.RenderBuffer(attr.getDataSize(), attr.getSrcName(), attr.getDstName(), attributeName, this) + new p5.RenderBuffer(prop.getDataSize(), prop.getSrcName(), prop.getDstName(), propertyName, this) ); } - attr.setCurrentData(data); + prop.setCurrentData(data); }; -p5.RendererGL.prototype._resetUserAttributes = function(){ - const attributes = this.immediateMode.geometry.userAttributes; - for (const attrName in attributes){ - const attr = attributes[attrName]; - delete this.immediateBufferStrides[attrName]; - attr.delete(); +p5.RendererGL.prototype._resetUserVertexProperties = function(){ + const properties = this.immediateMode.geometry.userVertexProperties; + for (const propName in properties){ + const prop = properties[propName]; + delete this.immediateBufferStrides[propName]; + prop.delete(); } - this._userUserAttributes = false; + this._useUserVertexProperties = false; this.tessyVertexSize = 12; - this.immediateMode.geometry.userAttributes = {}; + this.immediateMode.geometry.userVertexProperties = {}; this.immediateMode.buffers.user = []; }; @@ -485,11 +485,11 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertexNormals[i].y, this.immediateMode.geometry.vertexNormals[i].z ); - for (const attrName in this.immediateMode.geometry.userAttributes){ - const attr = this.immediateMode.geometry.userAttributes[attrName]; - const start = i * attr.getDataSize(); - const end = start + attr.getDataSize(); - const vals = attr.getSrcArray().slice(start, end); + for (const propName in this.immediateMode.geometry.userVertexProperties){ + const prop = this.immediateMode.geometry.userVertexProperties[propName]; + const start = i * prop.getDataSize(); + const end = start + prop.getDataSize(); + const vals = prop.getSrcArray().slice(start, end); contours[contours.length-1].push(...vals); } } @@ -498,9 +498,9 @@ p5.RendererGL.prototype._tesselateShape = function() { this.immediateMode.geometry.vertices = []; this.immediateMode.geometry.vertexNormals = []; this.immediateMode.geometry.uvs = []; - for (const attrName in this.immediateMode.geometry.userAttributes){ - const attr = this.immediateMode.geometry.userAttributes[attrName]; - attr.resetSrcArray(); + for (const propName in this.immediateMode.geometry.userVertexProperties){ + const prop = this.immediateMode.geometry.userVertexProperties[propName]; + prop.resetSrcArray(); } const colors = []; for ( @@ -512,12 +512,12 @@ p5.RendererGL.prototype._tesselateShape = function() { this.normal(...polyTriangles.slice(j + 9, j + 12)); { let offset = 12; - for (const attrName in this.immediateMode.geometry.userAttributes){ - const attr = this.immediateMode.geometry.userAttributes[attrName]; - const size = attr.getDataSize(); + for (const propName in this.immediateMode.geometry.userVertexProperties){ + const prop = this.immediateMode.geometry.userVertexProperties[propName]; + const size = prop.getDataSize(); const start = j + offset; const end = start + size; - attr.setCurrentData(polyTriangles.slice(start, end)); + prop.setCurrentData(polyTriangles.slice(start, end)); offset += size; } } diff --git a/src/webgl/p5.RendererGL.Retained.js b/src/webgl/p5.RendererGL.Retained.js index 6bcb3e8a2a..57ce2a9d15 100644 --- a/src/webgl/p5.RendererGL.Retained.js +++ b/src/webgl/p5.RendererGL.Retained.js @@ -116,10 +116,10 @@ p5.RendererGL.prototype.createBuffers = function(gId, model) { ? model.lineVertices.length / 3 : 0; - for (const attrName in model.userAttributes){ - const attr = model.userAttributes[attrName]; + for (const propName in model.userVertexProperties){ + const prop = model.userVertexProperties[propName]; this.retainedMode.buffers.user.push( - new p5.RenderBuffer(attr.getDataSize(), attr.getSrcName(), attr.getDstName(), attr.getName(), this) + new p5.RenderBuffer(prop.getDataSize(), prop.getSrcName(), prop.getDstName(), prop.getName(), this) ); } return buffers; @@ -147,12 +147,12 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { buff._prepareBuffer(geometry, fillShader); } for (const buff of this.retainedMode.buffers.user){ - const attr = geometry.model.userAttributes[buff.attr]; - const adjustedLength = attr.getSrcArray().length / attr.getDataSize(); + const prop = geometry.model.userVertexProperties[buff.attr]; + const adjustedLength = prop.getSrcArray().length / prop.getDataSize(); if(adjustedLength > geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute '${attr.getName()}' with more values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom vertex property '${prop.getName()}' with more values than vertices. This is probably caused by directly using the Geometry.vertexProperty() method.`, 'vertexProperty()'); } else if(adjustedLength < geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute '${attr.getName()}' with fewer values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom vertex property '${prop.getName()}' with fewer values than vertices. This is probably caused by directly using the Geometry.vertexProperty() method.`, 'vertexProperty()'); } buff._prepareBuffer(geometry, fillShader); } @@ -177,12 +177,12 @@ p5.RendererGL.prototype.drawBuffers = function(gId) { buff._prepareBuffer(geometry, strokeShader); } for (const buff of this.retainedMode.buffers.user){ - const attr = geometry.model.userAttributes[buff.attr]; - const adjustedLength = attr.getSrcArray().length / attr.getDataSize(); + const prop = geometry.model.userVertexProperties[buff.attr]; + const adjustedLength = prop.getSrcArray().length / prop.getDataSize(); if(adjustedLength > geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with more values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom vertex property ${prop.name} with more values than vertices. This is probably caused by directly using the Geometry.vertexProperty() method.`, 'vertexProperty()'); } else if(adjustedLength < geometry.model.vertices.length){ - p5._friendlyError(`One of the geometries has a custom attribute ${attr.name} with fewer values than vertices. This is probably caused by directly using the Geometry.setAttribute() method.`, 'setAttribute()'); + p5._friendlyError(`One of the geometries has a custom vertex property ${prop.name} with fewer values than vertices. This is probably caused by directly using the Geometry.vertexProperty() method.`, 'vertexProperty()'); } buff._prepareBuffer(geometry, strokeShader); } diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index def61f978e..105a49047f 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -131,7 +131,7 @@ visualSuite('WebGL', function() { ); }); - visualSuite('setAttribute', function(){ + visualSuite('vertexProperty', function(){ const vertSrc = `#version 300 es precision mediump float; uniform mat4 uProjectionMatrix; @@ -161,7 +161,7 @@ visualSuite('WebGL', function() { for (let i = 0; i < 20; i++){ let x = 20 * p5.sin(i/20*p5.TWO_PI); let y = 20 * p5.cos(i/20*p5.TWO_PI); - p5.setAttribute('aCol', [x/20, -y/20, 0]); + p5.vertexProperty('aCol', [x/20, -y/20, 0]); p5.vertex(x, y); } p5.endShape(); @@ -183,13 +183,13 @@ visualSuite('WebGL', function() { let x2 = x1 + 10; let y1 = j * 10; let y2 = y1 + 10; - p5.setAttribute('aCol', [1, 0, 0]); + p5.vertexProperty('aCol', [1, 0, 0]); p5.vertex(x1, y1); - p5.setAttribute('aCol', [0, 0, 1]); + p5.vertexProperty('aCol', [0, 0, 1]); p5.vertex(x2, y1); - p5.setAttribute('aCol', [0, 1, 1]); + p5.vertexProperty('aCol', [0, 1, 1]); p5.vertex(x2, y2); - p5.setAttribute('aCol', [1, 1, 1]); + p5.vertexProperty('aCol', [1, 1, 1]); p5.vertex(x1, y2); } } @@ -209,11 +209,11 @@ visualSuite('WebGL', function() { p5.sphere(5); p5.pop(); p5.beginShape(p5.TRIANGLES); - p5.setAttribute('aCol', [1,0,0]) + p5.vertexProperty('aCol', [1,0,0]) p5.vertex(-5, 5, 0); - p5.setAttribute('aCol', [0,1,0]) + p5.vertexProperty('aCol', [0,1,0]) p5.vertex(5, 5, 0); - p5.setAttribute('aCol', [0,0,1]) + p5.vertexProperty('aCol', [0,0,1]) p5.vertex(0, -5, 0); p5.endShape(p5.CLOSE); p5.push(); diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 400a1f62b0..6bfae7eb93 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -1577,19 +1577,19 @@ suite('p5.RendererGL', function() { renderer.beginShape(myp5.TESS); renderer.fill(255, 255, 255); renderer.normal(-1, -1, 1); - renderer.setAttribute('aCustom', [1, 1, 1]) + renderer.vertexProperty('aCustom', [1, 1, 1]) renderer.vertex(-10, -10, 0, 0); renderer.fill(255, 0, 0); renderer.normal(1, -1, 1); - renderer.setAttribute('aCustom', [1, 0, 0]) + renderer.vertexProperty('aCustom', [1, 0, 0]) renderer.vertex(10, -10, 1, 0); renderer.fill(0, 255, 0); renderer.normal(1, 1, 1); - renderer.setAttribute('aCustom', [0, 1, 0]) + renderer.vertexProperty('aCustom', [0, 1, 0]) renderer.vertex(10, 10, 1, 1); renderer.fill(0, 0, 255); renderer.normal(-1, 1, 1); - renderer.setAttribute('aCustom', [0, 0, 1]) + renderer.vertexProperty('aCustom', [0, 0, 1]) renderer.vertex(-10, 10, 0, 1); renderer.endShape(myp5.CLOSE); @@ -2504,21 +2504,21 @@ suite('p5.RendererGL', function() { ); }); - suite('setAttribute()', function() { + suite('vertexProperty()', function() { test('Immediate mode data and buffers created in beginShape', function() { myp5.createCanvas(50, 50, myp5.WEBGL); myp5.beginShape(); - myp5.setAttribute('aCustom', 1); - myp5.setAttribute('aCustomVec3', [1, 2, 3]); + myp5.vertexProperty('aCustom', 1); + myp5.vertexProperty('aCustomVec3', [1, 2, 3]); myp5.vertex(0,0,0); - expect(myp5._renderer.immediateMode.geometry.userAttributes.aCustom).to.containSubset({ + expect(myp5._renderer.immediateMode.geometry.userVertexProperties.aCustom).to.containSubset({ name: 'aCustom', currentData: 1, dataSize: 1 }); - expect(myp5._renderer.immediateMode.geometry.userAttributes.aCustomVec3).to.containSubset({ + expect(myp5._renderer.immediateMode.geometry.userVertexProperties.aCustomVec3).to.containSubset({ name: 'aCustomVec3', currentData: [1, 2, 3], dataSize: 3 @@ -2547,15 +2547,15 @@ suite('p5.RendererGL', function() { myp5.createCanvas(50, 50, myp5.WEBGL); myp5.beginShape(); - myp5.setAttribute('aCustom', 1); - myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertexProperty('aCustom', 1); + myp5.vertexProperty('aCustomVec3', [1,2,3]); myp5.vertex(0,0,0); myp5.endShape(); myp5.beginShape(); assert.isUndefined(myp5._renderer.immediateMode.geometry.aCustomSrc); assert.isUndefined(myp5._renderer.immediateMode.geometry.aCustomVec3Src); - assert.deepEqual(myp5._renderer.immediateMode.geometry.userAttributes, {}); + assert.deepEqual(myp5._renderer.immediateMode.geometry.userVertexProperties, {}); assert.deepEqual(myp5._renderer.immediateMode.buffers.user, []); myp5.endShape(); } @@ -2565,8 +2565,8 @@ suite('p5.RendererGL', function() { myp5.createCanvas(50, 50, myp5.WEBGL); myp5.beginGeometry(); myp5.beginShape(); - myp5.setAttribute('aCustom', 1); - myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertexProperty('aCustom', 1); + myp5.vertexProperty('aCustomVec3', [1,2,3]); myp5.vertex(0,1,0); myp5.vertex(-1,0,0); myp5.vertex(1,0,0); @@ -2582,8 +2582,8 @@ suite('p5.RendererGL', function() { myp5.createCanvas(50, 50, myp5.WEBGL); myp5.beginGeometry(); myp5.beginShape(); - myp5.setAttribute('aCustom', 1); - myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertexProperty('aCustom', 1); + myp5.vertexProperty('aCustomVec3', [1,2,3]); myp5.vertex(0,0,0); myp5.vertex(1,0,0); myp5.endShape(); @@ -2610,8 +2610,8 @@ suite('p5.RendererGL', function() { myp5.createCanvas(50, 50, myp5.WEBGL); myp5.beginGeometry(); myp5.beginShape(); - myp5.setAttribute('aCustom', 1); - myp5.setAttribute('aCustomVec3', [1,2,3]); + myp5.vertexProperty('aCustom', 1); + myp5.vertexProperty('aCustomVec3', [1,2,3]); myp5.vertex(0,0,0); myp5.vertex(1,0,0); myp5.endShape(); @@ -2628,13 +2628,13 @@ suite('p5.RendererGL', function() { const oldLog = console.log; console.log = myLog; myp5.beginShape(); - myp5.setAttribute('aCustom', [1,2,3]); + myp5.vertexProperty('aCustom', [1,2,3]); myp5.vertex(0,0,0); - myp5.setAttribute('aCustom', [1,2]); + myp5.vertexProperty('aCustom', [1,2]); myp5.vertex(1,0,0); myp5.endShape(); console.log = oldLog; - expect(logs.join('\n')).to.match(/Custom attribute 'aCustom' has been set with various data sizes/); + expect(logs.join('\n')).to.match(/Custom vertex property 'aCustom' has been set with various data sizes/); } ); test('Friendly error too many values set', @@ -2646,11 +2646,11 @@ suite('p5.RendererGL', function() { console.log = myLog; let myGeo = new p5.Geometry(); myGeo.vertices.push(new p5.Vector(0,0,0)); - myGeo.setAttribute('aCustom', 1); - myGeo.setAttribute('aCustom', 2); + myGeo.vertexProperty('aCustom', 1); + myGeo.vertexProperty('aCustom', 2); myp5.model(myGeo); console.log = oldLog; - expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute 'aCustom' with more values than vertices./); + expect(logs.join('\n')).to.match(/One of the geometries has a custom vertex property 'aCustom' with more values than vertices./); } ); test('Friendly error if too few values set', @@ -2663,10 +2663,10 @@ suite('p5.RendererGL', function() { let myGeo = new p5.Geometry(); myGeo.vertices.push(new p5.Vector(0,0,0)); myGeo.vertices.push(new p5.Vector(0,0,0)); - myGeo.setAttribute('aCustom', 1); + myGeo.vertexProperty('aCustom', 1); myp5.model(myGeo); console.log = oldLog; - expect(logs.join('\n')).to.match(/One of the geometries has a custom attribute 'aCustom' with fewer values than vertices./); + expect(logs.join('\n')).to.match(/One of the geometries has a custom vertex property 'aCustom' with fewer values than vertices./); } ); }) From 22cad2b3ba6fa07bf3850a9e8ab267b33309157c Mon Sep 17 00:00:00 2001 From: 23036879 Date: Tue, 1 Oct 2024 10:53:59 +0100 Subject: [PATCH 94/94] remove my temporary gitignore for dev folder --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9af31927d2..42f1e447dc 100644 --- a/.gitignore +++ b/.gitignore @@ -22,5 +22,4 @@ yarn.lock docs/data.json analyzer/ preview/ -__screenshots__/ -attributes-example/ \ No newline at end of file +__screenshots__/ \ No newline at end of file