From 072fac9949f176b702e6f7e8b625e28c7e730b66 Mon Sep 17 00:00:00 2001 From: Paul Wheeler Date: Sat, 30 Jan 2021 11:39:18 -1000 Subject: [PATCH] Added normal function for specifying explicit normals while drawing 3d shapes with vertex. --- src/core/shape/vertex.js | 52 +++++++++++++++++++++++++++ src/webgl/p5.RendererGL.Immediate.js | 36 ++++++++++++------- src/webgl/p5.RendererGL.js | 5 +++ test/unit/webgl/normal.js | 54 ++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 test/unit/webgl/normal.js diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js index bfc96e0fed..4c897b89e7 100644 --- a/src/core/shape/vertex.js +++ b/src/core/shape/vertex.js @@ -992,4 +992,56 @@ p5.prototype.vertex = function(x, y, moveTo, u, v) { return this; }; +/** + * Sets the 3d vertex normal to use for subsequent vertices drawn with + * vertex(). A normal is a vector that is generally + * nearly perpendicular to a shape's surface which controls how much light will + * be reflected from that part of the surface. + * + * @method normal + * @param {Vector} vector A p5.Vector representing the vertex normal. + * @chainable + * @example + *
+ * + * function setup() { + * createCanvas(100, 100, WEBGL); + * noStroke(); + * } + * + * function draw() { + * background(255); + * rotateY(frameCount / 100); + * normalMaterial(); + * beginShape(TRIANGLE_STRIP); + * normal(-0.4, 0.4, 0.8); + * vertex(-30, 30, 0); + * + * normal(0, 0, 1); + * vertex(-30, -30, 30); + * vertex(30, 30, 30); + * + * normal(0.4, -0.4, 0.8); + * vertex(30, -30, 0); + * endShape(); + * } + * + *
+ */ + +/** + * @method normal + * @param {Number} x The x component of the vertex normal. + * @param {Number} y The y component of the vertex normal. + * @param {Number} z The z component of the vertex normal. + * @chainable + */ +p5.prototype.normal = function(x, y, z) { + this._assert3d('normal'); + p5._validateParameters('normal', arguments); + this._renderer.normal(...arguments); + + return this; +}; + export default p5; diff --git a/src/webgl/p5.RendererGL.Immediate.js b/src/webgl/p5.RendererGL.Immediate.js index 16cb6fb988..410237643b 100644 --- a/src/webgl/p5.RendererGL.Immediate.js +++ b/src/webgl/p5.RendererGL.Immediate.js @@ -67,6 +67,7 @@ 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); const vertexColor = this.curFillColor || [0.5, 0.5, 0.5, 1.0]; this.immediateMode.geometry.vertexColors.push( vertexColor[0], @@ -103,6 +104,28 @@ p5.RendererGL.prototype.vertex = function(x, y) { return this; }; +/** + * Sets the normal to use for subsequent vertices. + * @method vertexNormal + * @param {Number} x + * @param {Number} y + * @param {Number} z + * @chainable + * + * @method vertexNormal + * @param {Vector} v + * @chainable + */ +p5.RendererGL.prototype.normal = function(xorv, y, z) { + if (xorv instanceof p5.Vector) { + this._currentNormal = xorv; + } else { + this._currentNormal = new p5.Vector(xorv, y, z); + } + + return this; +}; + /** * End shape drawing and render vertices to screen. * @chainable @@ -251,7 +274,6 @@ p5.RendererGL.prototype._drawImmediateFill = function() { const gl = this.GL; const shader = this._getImmediateFillShader(); - this._calculateNormals(this.immediateMode.geometry); this._setFillUniforms(shader); for (const buff of this.immediateMode.buffers.fill) { @@ -298,16 +320,4 @@ p5.RendererGL.prototype._drawImmediateStroke = function() { shader.unbindShader(); }; -/** - * Called from _drawImmediateFill(). Currently adds default normals which - * only work for flat shapes. - * @parem - * @private - */ -p5.RendererGL.prototype._calculateNormals = function(geometry) { - geometry.vertices.forEach(() => { - geometry.vertexNormals.push(new p5.Vector(0, 0, 1)); - }); -}; - export default p5.RendererGL; diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index c0fdc2b9ba..f2b4237ce2 100755 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -122,6 +122,9 @@ p5.RendererGL = function(elt, pInst, isMainCanvas, attr) { this.uPMatrix = new p5.Matrix(); this.uNMatrix = new p5.Matrix('mat3'); + // Current vertex normal + this._currentNormal = new p5.Vector(0, 0, 1); + // Camera this._curCamera = new p5.Camera(this); this._curCamera._computeCameraDefaultSettings(); @@ -1026,6 +1029,8 @@ p5.RendererGL.prototype.push = function() { properties._tex = this._tex; properties.drawMode = this.drawMode; + properties._currentNormal = this._currentNormal; + return style; }; diff --git a/test/unit/webgl/normal.js b/test/unit/webgl/normal.js new file mode 100644 index 0000000000..d8254ef7d3 --- /dev/null +++ b/test/unit/webgl/normal.js @@ -0,0 +1,54 @@ +suite('', function() { + var myp5; + + if (!window.Modernizr.webgl) { + return; + } + + setup(function() { + myp5 = new p5(function(p) { + p.setup = function() { + p.createCanvas(100, 100, p.WEBGL); + }; + }); + }); + + teardown(function() { + myp5.remove(); + }); + + suite('p5.prototype.normal', function() { + test('should be a function', function() { + assert.ok(myp5.normal); + assert.typeOf(myp5.normal, 'function'); + }); + test('missing param #1', function() { + assert.validationError(function() { + myp5.normal(10); + }); + }); + test('wrong param type at #0', function() { + assert.validationError(function() { + myp5.normal('a', 1); + }); + }); + test('accepts numeric arguments', function() { + assert.doesNotThrow( + function() { + myp5.normal(0, 1, 0); + }, + Error, + 'got unwanted exception' + ); + }); + test('accepts vector argument', function() { + assert.doesNotThrow( + function() { + myp5.normal(myp5.createVector(0, 1, 0)); + }, + Error, + 'got unwanted exception' + ); + }); + }); +});