From d9c6c1d549c2685f128af38a48627459b04d9619 Mon Sep 17 00:00:00 2001 From: Perminder Singh Date: Sat, 23 Nov 2024 07:01:35 +0530 Subject: [PATCH 1/3] webgl-lines --- src/core/constants.js | 14 +++- src/webgl/3d_primitives.js | 141 +++++++++++++++++++++++++++++------ src/webgl/GeometryBuilder.js | 2 +- src/webgl/ShapeBuilder.js | 2 +- src/webgl/p5.Geometry.js | 8 +- src/webgl/p5.RendererGL.js | 4 + src/webgl/shaders/line.vert | 6 +- 7 files changed, 150 insertions(+), 27 deletions(-) diff --git a/src/core/constants.js b/src/core/constants.js index bb120fa712..a78b690a34 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -60,6 +60,18 @@ export const WEBGL2 = Symbol('webgl2'); * @final */ export const ARROW = 'default'; + +/** + * @property {String} SIMPLE + * @final + */ +export const SIMPLE = 'simple'; +/** + * @property {String} FULLY + * @final + */ +export const FULLY = 'fully'; + /** * @typedef {'crosshair'} CROSS * @property {CROSS} CROSS @@ -1318,4 +1330,4 @@ export const HALF_FLOAT = 'half-float'; * @property {RGBA} RGBA * @final */ -export const RGBA = 'rgba'; +export const RGBA = 'rgba'; \ No newline at end of file diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 869b7ab240..f7511cbe18 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -520,6 +520,94 @@ function primitives3D(p5, fn){ return this._renderer.endGeometry(); }; + +/** + * Sets the stroke rendering mode to balance performance and visual features when drawing lines. + * + * `strokeMode()` offers two modes: + * + * - `SIMPLE`: Optimizes for speed by disabling caps, joins, and stroke color features. + * Use this mode for faster line rendering when these visual details are unnecessary. + * - `FULLY`: Enables caps, joins, and stroke color for lines. + * This mode provides enhanced visuals but may reduce performance due to additional processing. + * + * Choose the mode that best suits your application's needs to either improve rendering speed or enhance visual quality. + * + * @method strokeMode + * @param {string} mode - The stroke mode to set. Possible values are: + * - `'SIMPLE'`: Fast rendering without caps, joins, or stroke color. + * - `'FULLY'`: Detailed rendering with caps, joins, and stroke color. + * + * @example + *
+ * + * function setup() { + * createCanvas(100, 100, WEBGL); + * strokeWeight(10); + * stroke(0); + * + * describe('A red, horizontal, rectangular shape with rounded edges (resembling a pill or capsule) in a grey background'); + * } + * + * function draw() { + * strokeMode(FULLY); // Enables detailed rendering with caps, joins, and stroke color. + * stroke('red'); + * + * background(128); + * + * let centerX = 0; + * let centerY = 0; + * + * // Length of the small centered line + * let lineLength = 50; + * + * beginShape(LINES); + * vertex(centerX - lineLength / 2, centerY, 0); + * vertex(centerX + lineLength / 2, centerY, 0); + * endShape(); + * } + * + *
+ * + *
+ * + * function setup() { + * createCanvas(100, 100, WEBGL); + * strokeWeight(10); + * stroke(0); + * + * describe('A black, horizontal, rectangular shape without rounded edges in a grey background'); + * } + * + * function draw() { + * strokeMode(SIMPLE); // Enables detailed rendering with caps, joins, and stroke color. + * stroke(`red`); + * background(128); + * + * let centerX = 0; + * let centerY = 0; + * + * // Length of the small centered line + * let lineLength = 50; + * + * beginShape(LINES); + * vertex(centerX - lineLength / 2, centerY, 0); + * vertex(centerX + lineLength / 2, centerY, 0); + * endShape(); + * } + * + *
+ */ + +fn.strokeMode = function(mode){ + if(mode === constants.SIMPLE){ + this._renderer._simpleLines = true; + } else if (mode === constants.FULLY) { + this._renderer._simpleLines = false; + } else { + throw Error('no such parameter'); + } + } /** * Creates a custom p5.Geometry object from * simpler 3D shapes. @@ -2109,7 +2197,7 @@ function primitives3D(p5, fn){ this.faces = [[0, 1, 2]]; this.uvs = [0, 0, 1, 0, 1, 1]; }; - const triGeom = new Geometry(1, 1, _triangle); + const triGeom = new Geometry(1, 1, _triangle, this); triGeom._edgesToVertices(); triGeom.computeNormals(); triGeom.gid = gid; @@ -2245,7 +2333,7 @@ function primitives3D(p5, fn){ } }; - const arcGeom = new Geometry(detail, 1, _arc); + const arcGeom = new Geometry(detail, 1, _arc, this); arcGeom.computeNormals(); if (detail <= 50) { @@ -2308,7 +2396,7 @@ function primitives3D(p5, fn){ ]; } }; - const rectGeom = new Geometry(detailX, detailY, _rect); + const rectGeom = new Geometry(detailX, detailY, _rect, this); rectGeom .computeFaces() .computeNormals() @@ -2440,7 +2528,7 @@ function primitives3D(p5, fn){ this.uvs.push([pctx, pcty]); } } - }); + }, this); quadGeom.faces = []; for(let y = 0; y < detailY-1; y++){ @@ -3362,7 +3450,7 @@ function primitives3D(p5, fn){ } } }; - const planeGeom = new Geometry(detailX, detailY, _plane); + const planeGeom = new Geometry(detailX, detailY, _plane, this); planeGeom.computeFaces().computeNormals(); if (detailX <= 1 && detailY <= 1) { planeGeom._makeTriangleEdges()._edgesToVertices(); @@ -3442,7 +3530,7 @@ function primitives3D(p5, fn){ this.faces.push([v + 2, v + 1, v + 3]); }); }; - const boxGeom = new Geometry(detailX, detailY, _box); + const boxGeom = new Geometry(detailX, detailY, _box, this); boxGeom.computeNormals(); if (detailX <= 4 && detailY <= 4) { boxGeom._edgesToVertices(); @@ -3498,7 +3586,7 @@ function primitives3D(p5, fn){ } } }; - const ellipsoidGeom = new Geometry(detailX, detailY, _ellipsoid); + const ellipsoidGeom = new Geometry(detailX, detailY, _ellipsoid, this); ellipsoidGeom.computeFaces(); if (detailX <= 24 && detailY <= 24) { ellipsoidGeom._makeTriangleEdges()._edgesToVertices(); @@ -3525,17 +3613,18 @@ function primitives3D(p5, fn){ ) { const gid = `cylinder|${detailX}|${detailY}|${bottomCap}|${topCap}`; if (!this.geometryInHash(gid)) { - const cylinderGeom = new p5.Geometry(detailX, detailY); - _truncatedCone.call( - cylinderGeom, - 1, - 1, - 1, - detailX, - detailY, - bottomCap, - topCap - ); + const cylinderGeom = new p5.Geometry(detailX, detailY, function() { + _truncatedCone.call( + this, + 1, + 1, + 1, + detailX, + detailY, + bottomCap, + topCap + ); + }, this); // normals are computed in call to _truncatedCone if (detailX <= 24 && detailY <= 16) { cylinderGeom._makeTriangleEdges()._edgesToVertices(); @@ -3561,8 +3650,18 @@ function primitives3D(p5, fn){ ) { const gid = `cone|${detailX}|${detailY}|${cap}`; if (!this.geometryInHash(gid)) { - const coneGeom = new Geometry(detailX, detailY); - _truncatedCone.call(coneGeom, 1, 0, 1, detailX, detailY, cap, false); + const coneGeom = new Geometry(detailX, detailY, function() { + _truncatedCone.call( + this, + 1, + 0, + 1, + detailX, + detailY, + cap, + false + ); + }, this); if (detailX <= 24 && detailY <= 16) { coneGeom._makeTriangleEdges()._edgesToVertices(); } else if (this.states.doStroke) { @@ -3624,7 +3723,7 @@ function primitives3D(p5, fn){ } } }; - const torusGeom = new Geometry(detailX, detailY, _torus); + const torusGeom = new Geometry(detailX, detailY, _torus, this); torusGeom.computeFaces(); if (detailX <= 24 && detailY <= 16) { torusGeom._makeTriangleEdges()._edgesToVertices(); diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index a5691db31e..6d269282c2 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -13,7 +13,7 @@ class GeometryBuilder { renderer._pInst.push(); this.identityMatrix = new Matrix(); renderer.states.uModelMatrix = new Matrix(); - this.geometry = new Geometry(); + this.geometry = new Geometry(undefined, undefined, undefined, this.renderer); this.geometry.gid = `_p5_GeometryBuilder_${GeometryBuilder.nextGeometryId}`; GeometryBuilder.nextGeometryId++; this.hasTransform = false; diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js index 258f8c07e8..12d8798d63 100644 --- a/src/webgl/ShapeBuilder.js +++ b/src/webgl/ShapeBuilder.js @@ -21,7 +21,7 @@ export class ShapeBuilder { constructor(renderer) { this.renderer = renderer; this.shapeMode = constants.TESS; - this.geometry = new Geometry(); + this.geometry = new Geometry(undefined, undefined, undefined, this.renderer); this.geometry.gid = '__IMMEDIATE_MODE_GEOMETRY__'; this.contourIndices = []; diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index a5d5297b21..b1e45c0766 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -13,7 +13,8 @@ import { DataArray } from './p5.DataArray'; import { Vector } from '../math/p5.Vector'; class Geometry { - constructor(detailX, detailY, callback) { + constructor(detailX, detailY, callback, renderer) { + this.renderer = renderer; this.vertices = []; this.boundingBoxCache = null; @@ -1398,7 +1399,7 @@ class Geometry { if (dirOK) { this._addSegment(begin, end, fromColor, toColor, dir); } - + if(!this.renderer._simpleLines){ if (i > 0 && prevEdge[1] === currEdge[0]) { if (!connected.has(currEdge[0])) { connected.add(currEdge[0]); @@ -1481,6 +1482,7 @@ class Geometry { lastValidDir = dir; } } + } for (const { point, dir, color } of potentialCaps.values()) { this._addCap(point, dir, color); } @@ -1520,6 +1522,7 @@ class Geometry { } } this.lineVertices.push(...a, ...b, ...a, ...b, ...b, ...a); + if(!this.renderer._simpleLines){ this.lineVertexColors.push( ...fromColor, ...toColor, @@ -1528,6 +1531,7 @@ class Geometry { ...toColor, ...fromColor ); + } return this; } diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 93f9f14f53..b695e0d967 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -242,6 +242,9 @@ class RendererGL extends Renderer { // erasing this._isErasing = false; + // simple lines + this._simpleLines = false; + // clipping this._clipDepths = []; this._isClipApplied = false; @@ -2150,6 +2153,7 @@ class RendererGL extends Renderer { _setStrokeUniforms(strokeShader) { // set the uniform values + strokeShader.setUniform('uSimpleLines', this._simpleLines); strokeShader.setUniform('uUseLineColor', this._useLineColor); strokeShader.setUniform('uMaterialColor', this.states.curStrokeColor); strokeShader.setUniform('uStrokeWeight', this.curStrokeWeight); diff --git a/src/webgl/shaders/line.vert b/src/webgl/shaders/line.vert index ed8329f8d1..deb5294164 100644 --- a/src/webgl/shaders/line.vert +++ b/src/webgl/shaders/line.vert @@ -26,6 +26,7 @@ uniform mat4 uProjectionMatrix; uniform float uStrokeWeight; uniform bool uUseLineColor; +uniform bool uSimpleLines; uniform vec4 uMaterialColor; uniform vec4 uViewport; @@ -67,6 +68,8 @@ vec2 lineIntersection(vec2 aPoint, vec2 aDir, vec2 bPoint, vec2 bDir) { void main() { HOOK_beforeVertex(); + + if(!uSimpleLines){ // Caps have one of either the in or out tangent set to 0 vCap = (aTangentIn == vec3(0.)) != (aTangentOut == (vec3(0.))) ? 1. : 0.; @@ -77,6 +80,7 @@ void main() { aTangentOut != vec3(0.) && aTangentIn != aTangentOut ) ? 1. : 0.; + } vec4 localPosition = vec4(HOOK_getLocalPosition(aPosition.xyz), 1.); vec4 posp = vec4(HOOK_getWorldPosition((uModelViewMatrix * localPosition).xyz), 1.); @@ -171,7 +175,7 @@ void main() { } vec2 offset; - if (vJoin == 1.) { + if (vJoin == 1. && !uSimpleLines) { vTangent = normalize(tangentIn + tangentOut); vec2 normalIn = vec2(-tangentIn.y, tangentIn.x); vec2 normalOut = vec2(-tangentOut.y, tangentOut.x); From bfe216930e6c1f77dec5d8ab8841db74a615cbc1 Mon Sep 17 00:00:00 2001 From: Perminder Singh Date: Sat, 23 Nov 2024 22:09:09 +0530 Subject: [PATCH 2/3] fixes --- src/webgl/loading.js | 2 +- src/webgl/text.js | 2 +- test/unit/webgl/p5.Geometry.js | 2 +- test/unit/webgl/p5.RendererGL.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/webgl/loading.js b/src/webgl/loading.js index 66270dbb0a..28c6099432 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -377,7 +377,7 @@ function loading(p5, fn){ fileType = '.obj'; } - const model = new Geometry(); + const model = new Geometry(undefined, undefined, undefined, this._renderer); model.gid = `${path}|${normalize}`; async function getMaterials(lines) { diff --git a/src/webgl/text.js b/src/webgl/text.js index b8355f0c9a..ffef73167b 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -702,7 +702,7 @@ function text(p5, fn){ this.uvs.push(j, i); } } - })); + }, this) ); geom.computeFaces().computeNormals(); g = this.geometryBufferCache.ensureCached(geom); } diff --git a/test/unit/webgl/p5.Geometry.js b/test/unit/webgl/p5.Geometry.js index 636dd29387..cb42cb32a2 100644 --- a/test/unit/webgl/p5.Geometry.js +++ b/test/unit/webgl/p5.Geometry.js @@ -19,7 +19,7 @@ suite('p5.Geometry', function() { let geom; beforeEach(function() { - geom = new p5.Geometry(); + geom = new p5.Geometry(undefined, undefined, undefined, myp5._renderer); vi.spyOn(geom, '_addCap'); vi.spyOn(geom, '_addJoin'); vi.spyOn(geom, '_addSegment'); diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 931aa0ef24..8fd8b0fa06 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -2058,7 +2058,7 @@ suite('p5.RendererGL', function() { 0, 1, 0, 1 ); this._edgesToVertices(); - }); + }, myp5._renderer); myp5.background(255); myp5.fill(255); myp5.strokeWeight(4); @@ -2239,7 +2239,7 @@ suite('p5.RendererGL', function() { this.faces.push([0, 1, 2]); this.faces.push([0, 2, 3]); this.computeNormals(); - }); + }, myp5._renderer); myp5.fill(255); myp5.noStroke(); From 9b34110ddaf08f98548fade63a89240cbd6dca5e Mon Sep 17 00:00:00 2001 From: Perminder Singh Date: Tue, 26 Nov 2024 22:59:05 +0530 Subject: [PATCH 3/3] fixing-minor-suggestions --- src/core/constants.js | 4 +- src/webgl/3d_primitives.js | 75 ++++---- src/webgl/p5.Geometry.js | 368 ++++++++++++++++++------------------ src/webgl/shaders/line.vert | 21 +- 4 files changed, 233 insertions(+), 235 deletions(-) diff --git a/src/core/constants.js b/src/core/constants.js index a78b690a34..5ffefa0b46 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -67,10 +67,10 @@ export const ARROW = 'default'; */ export const SIMPLE = 'simple'; /** - * @property {String} FULLY + * @property {String} FULL * @final */ -export const FULLY = 'fully'; +export const FULL = 'full'; /** * @typedef {'crosshair'} CROSS diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index f7511cbe18..96b0593a1b 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -528,7 +528,7 @@ function primitives3D(p5, fn){ * * - `SIMPLE`: Optimizes for speed by disabling caps, joins, and stroke color features. * Use this mode for faster line rendering when these visual details are unnecessary. - * - `FULLY`: Enables caps, joins, and stroke color for lines. + * - `FULL`: Enables caps, joins, and stroke color for lines. * This mode provides enhanced visuals but may reduce performance due to additional processing. * * Choose the mode that best suits your application's needs to either improve rendering speed or enhance visual quality. @@ -536,34 +536,32 @@ function primitives3D(p5, fn){ * @method strokeMode * @param {string} mode - The stroke mode to set. Possible values are: * - `'SIMPLE'`: Fast rendering without caps, joins, or stroke color. - * - `'FULLY'`: Detailed rendering with caps, joins, and stroke color. + * - `'FULL'`: Detailed rendering with caps, joins, and stroke color. * * @example *
* * function setup() { - * createCanvas(100, 100, WEBGL); - * strokeWeight(10); - * stroke(0); + * createCanvas(300, 300, WEBGL); * - * describe('A red, horizontal, rectangular shape with rounded edges (resembling a pill or capsule) in a grey background'); + * describe('A sphere with red stroke and a red, wavy line on a gray background.'); * } * * function draw() { - * strokeMode(FULLY); // Enables detailed rendering with caps, joins, and stroke color. - * stroke('red'); - * * background(128); - * - * let centerX = 0; - * let centerY = 0; - * - * // Length of the small centered line - * let lineLength = 50; + * strokeMode(FULL); // Enables detailed rendering with caps, joins, and stroke color. + * push(); + * strokeWeight(1); + * translate(0, -50, 0); + * sphere(50); + * pop(); * - * beginShape(LINES); - * vertex(centerX - lineLength / 2, centerY, 0); - * vertex(centerX + lineLength / 2, centerY, 0); + * noFill(); + * strokeWeight(15); + * beginShape(); + * vertex(-150, 100); + * stroke('red'); + * bezierVertex(-50, -100, 30, 300, 130, 50); * endShape(); * } * @@ -571,38 +569,39 @@ function primitives3D(p5, fn){ * *
* - * function setup() { - * createCanvas(100, 100, WEBGL); - * strokeWeight(10); - * stroke(0); + * function setup() { + * createCanvas(300, 300, WEBGL); * - * describe('A black, horizontal, rectangular shape without rounded edges in a grey background'); + * describe('A sphere with red stroke and a wavy line without full curve decorations without caps and color on a gray background.'); * } * * function draw() { - * strokeMode(SIMPLE); // Enables detailed rendering with caps, joins, and stroke color. - * stroke(`red`); * background(128); - * - * let centerX = 0; - * let centerY = 0; - * - * // Length of the small centered line - * let lineLength = 50; + * strokeMode(SIMPLE); // Enables simple rendering without caps, joins, and stroke color. + * push(); + * strokeWeight(1); + * translate(0, -50, 0); + * sphere(50); + * pop(); * - * beginShape(LINES); - * vertex(centerX - lineLength / 2, centerY, 0); - * vertex(centerX + lineLength / 2, centerY, 0); + * noFill(); + * strokeWeight(15); + * beginShape(); + * vertex(-150, 100); + * stroke('red'); + * bezierVertex(-50, -100, 30, 300, 130, 50); * endShape(); * } * *
*/ -fn.strokeMode = function(mode){ - if(mode === constants.SIMPLE){ - this._renderer._simpleLines = true; - } else if (mode === constants.FULLY) { + fn.strokeMode = function (mode) { + if (mode === undefined) { + return this._renderer._simpleLines ? constants.SIMPLE : constants.FULL; + } else if (mode === constants.SIMPLE) { + this._renderer._simpleLines = true; + } else if (mode === constants.FULL) { this._renderer._simpleLines = false; } else { throw Error('no such parameter'); diff --git a/src/webgl/p5.Geometry.js b/src/webgl/p5.Geometry.js index b1e45c0766..afaf4c3b7c 100644 --- a/src/webgl/p5.Geometry.js +++ b/src/webgl/p5.Geometry.js @@ -490,7 +490,7 @@ class Geometry { if (binary) { let offset = 80; const bufferLength = - this.faces.length * 2 + this.faces.length * 3 * 4 * 4 + 80 + 4; + this.faces.length * 2 + this.faces.length * 3 * 4 * 4 + 80 + 4; const arrayBuffer = new ArrayBuffer(bufferLength); modelOutput = new DataView(arrayBuffer); modelOutput.setUint32(offset, this.faces.length, true); @@ -1399,90 +1399,90 @@ class Geometry { if (dirOK) { this._addSegment(begin, end, fromColor, toColor, dir); } - if(!this.renderer._simpleLines){ - if (i > 0 && prevEdge[1] === currEdge[0]) { - if (!connected.has(currEdge[0])) { - connected.add(currEdge[0]); - potentialCaps.delete(currEdge[0]); - // Add a join if this segment shares a vertex with the previous. Skip - // actually adding join vertices if either the previous segment or this - // one has a length of 0. - // - // Don't add a join if the tangents point in the same direction, which - // would mean the edges line up exactly, and there is no need for a join. - if (lastValidDir && dirOK && dir.dot(lastValidDir) < 1 - 1e-8) { - this._addJoin(begin, lastValidDir, dir, fromColor); - } - } - } else { - // Start a new line - if (dirOK && !connected.has(currEdge[0])) { - const existingCap = potentialCaps.get(currEdge[0]); - if (existingCap) { - this._addJoin( - begin, - existingCap.dir, - dir, - fromColor - ); - potentialCaps.delete(currEdge[0]); + if (!this.renderer?._simpleLines) { + if (i > 0 && prevEdge[1] === currEdge[0]) { + if (!connected.has(currEdge[0])) { connected.add(currEdge[0]); - } else { - potentialCaps.set(currEdge[0], { - point: begin, - dir: dir.copy().mult(-1), - color: fromColor - }); + potentialCaps.delete(currEdge[0]); + // Add a join if this segment shares a vertex with the previous. Skip + // actually adding join vertices if either the previous segment or this + // one has a length of 0. + // + // Don't add a join if the tangents point in the same direction, which + // would mean the edges line up exactly, and there is no need for a join. + if (lastValidDir && dirOK && dir.dot(lastValidDir) < 1 - 1e-8) { + this._addJoin(begin, lastValidDir, dir, fromColor); + } + } + } else { + // Start a new line + if (dirOK && !connected.has(currEdge[0])) { + const existingCap = potentialCaps.get(currEdge[0]); + if (existingCap) { + this._addJoin( + begin, + existingCap.dir, + dir, + fromColor + ); + potentialCaps.delete(currEdge[0]); + connected.add(currEdge[0]); + } else { + potentialCaps.set(currEdge[0], { + point: begin, + dir: dir.copy().mult(-1), + color: fromColor + }); + } + } + if (lastValidDir && !connected.has(prevEdge[1])) { + const existingCap = potentialCaps.get(prevEdge[1]); + if (existingCap) { + this._addJoin( + this.vertices[prevEdge[1]], + lastValidDir, + existingCap.dir.copy().mult(-1), + fromColor + ); + potentialCaps.delete(prevEdge[1]); + connected.add(prevEdge[1]); + } else { + // Close off the last segment with a cap + potentialCaps.set(prevEdge[1], { + point: this.vertices[prevEdge[1]], + dir: lastValidDir, + color: fromColor + }); + } + lastValidDir = undefined; } } - if (lastValidDir && !connected.has(prevEdge[1])) { - const existingCap = potentialCaps.get(prevEdge[1]); + + if (i === this.edges.length - 1 && !connected.has(currEdge[1])) { + const existingCap = potentialCaps.get(currEdge[1]); if (existingCap) { this._addJoin( - this.vertices[prevEdge[1]], - lastValidDir, + end, + dir, existingCap.dir.copy().mult(-1), - fromColor + toColor ); - potentialCaps.delete(prevEdge[1]); - connected.add(prevEdge[1]); + potentialCaps.delete(currEdge[1]); + connected.add(currEdge[1]); } else { - // Close off the last segment with a cap - potentialCaps.set(prevEdge[1], { - point: this.vertices[prevEdge[1]], - dir: lastValidDir, - color: fromColor + potentialCaps.set(currEdge[1], { + point: end, + dir, + color: toColor }); } - lastValidDir = undefined; } - } - if (i === this.edges.length - 1 && !connected.has(currEdge[1])) { - const existingCap = potentialCaps.get(currEdge[1]); - if (existingCap) { - this._addJoin( - end, - dir, - existingCap.dir.copy().mult(-1), - toColor - ); - potentialCaps.delete(currEdge[1]); - connected.add(currEdge[1]); - } else { - potentialCaps.set(currEdge[1], { - point: end, - dir, - color: toColor - }); + if (dirOK) { + lastValidDir = dir; } } - - if (dirOK) { - lastValidDir = dir; - } } - } for (const { point, dir, color } of potentialCaps.values()) { this._addCap(point, dir, color); } @@ -1522,16 +1522,16 @@ class Geometry { } } this.lineVertices.push(...a, ...b, ...a, ...b, ...b, ...a); - if(!this.renderer._simpleLines){ - this.lineVertexColors.push( - ...fromColor, - ...toColor, - ...fromColor, - ...toColor, - ...toColor, - ...fromColor - ); - } + if (!this.renderer?._simpleLines) { + this.lineVertexColors.push( + ...fromColor, + ...toColor, + ...fromColor, + ...toColor, + ...toColor, + ...fromColor + ); + } return this; } @@ -1688,110 +1688,110 @@ class Geometry { return this; } -/** Sets the shader's vertex property or attribute variables. - * - * 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, `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 shader variable. This value - * will be pushed directly onto the Geometry object. There should be the same number - * 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 vertexProperty() function. - * - * @example - *
- * - * let geo; - * - * function cartesianToSpherical(x, y, 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 }; - * } - * - * function setup() { - * createCanvas(100, 100, WEBGL); - * - * // Modify the material shader to display roughness. - * const 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; - * }` - * }); - * - * // 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); - * - * // Set the custom roughness vertex property. - * let roughness = noise(spherical.theta*5, spherical.phi*5); - * geo.vertexProperty('aRoughness', roughness); - * } - * - * // Use the custom shader. - * shader(myShader); - * - * describe('A rough pink sphere rotating on a blue background.'); - * } - * - * function draw() { - * // Set some styles and lighting - * background('lightblue'); - * noStroke(); - * - * specularMaterial(255,125,100); - * shininess(2); - * - * directionalLight('white', -1, 1, -1); - * ambientLight(320); - * - * rotateY(millis()*0.001); - * - * // Draw the geometry - * model(geo); - * } - * - *
- * - * @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. - */ + /** Sets the shader's vertex property or attribute variables. + * + * 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, `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 shader variable. This value + * will be pushed directly onto the Geometry object. There should be the same number + * 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 vertexProperty() function. + * + * @example + *
+ * + * let geo; + * + * function cartesianToSpherical(x, y, 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 }; + * } + * + * function setup() { + * createCanvas(100, 100, WEBGL); + * + * // Modify the material shader to display roughness. + * const 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; + * }` + * }); + * + * // 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); + * + * // Set the custom roughness vertex property. + * let roughness = noise(spherical.theta*5, spherical.phi*5); + * geo.vertexProperty('aRoughness', roughness); + * } + * + * // Use the custom shader. + * shader(myShader); + * + * describe('A rough pink sphere rotating on a blue background.'); + * } + * + * function draw() { + * // Set some styles and lighting + * background('lightblue'); + * noStroke(); + * + * specularMaterial(255,125,100); + * shininess(2); + * + * directionalLight('white', -1, 1, -1); + * ambientLight(320); + * + * rotateY(millis()*0.001); + * + * // Draw the geometry + * model(geo); + * } + * + *
+ * + * @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. + */ vertexProperty(propertyName, data, size){ let prop; if (!this.userVertexProperties[propertyName]){ diff --git a/src/webgl/shaders/line.vert b/src/webgl/shaders/line.vert index deb5294164..9609fc38d4 100644 --- a/src/webgl/shaders/line.vert +++ b/src/webgl/shaders/line.vert @@ -69,17 +69,16 @@ vec2 lineIntersection(vec2 aPoint, vec2 aDir, vec2 bPoint, vec2 bDir) { void main() { HOOK_beforeVertex(); - if(!uSimpleLines){ - // Caps have one of either the in or out tangent set to 0 - vCap = (aTangentIn == vec3(0.)) != (aTangentOut == (vec3(0.))) - ? 1. : 0.; - - // Joins have two unique, defined tangents - vJoin = ( - aTangentIn != vec3(0.) && - aTangentOut != vec3(0.) && - aTangentIn != aTangentOut - ) ? 1. : 0.; + if (!uSimpleLines) { + // Caps have one of either the in or out tangent set to 0 + vCap = (aTangentIn == vec3(0.)) != (aTangentOut == vec3(0.)) ? 1. : 0.; + + // Joins have two unique, defined tangents + vJoin = ( + aTangentIn != vec3(0.) && + aTangentOut != vec3(0.) && + aTangentIn != aTangentOut + ) ? 1. : 0.; } vec4 localPosition = vec4(HOOK_getLocalPosition(aPosition.xyz), 1.);