Skip to content

Commit

Permalink
Merge branch 'processing:main' into model-view
Browse files Browse the repository at this point in the history
  • Loading branch information
deveshidwivedi authored Feb 2, 2024
2 parents 3fd2f4c + 304ee90 commit 88a91b0
Show file tree
Hide file tree
Showing 16 changed files with 167 additions and 12 deletions.
26 changes: 26 additions & 0 deletions contributor_docs/unit_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,29 @@ If you need to add a new test file, add it to that folder, then add the filename
When you add a new test, running `npm test` will generate new screenshots for any visual tests that do not yet have them. Those screenshots will then be used as a reference the next time tests run to make sure the sketch looks the same. If a test intentionally needs to look different, you can delete the folder matching the test name in the `test/unit/visual/screenshots` folder, and then re-run `npm test` to generate a new one.
To manually inspect all visual tests, run `grunt yui:dev` to launch a local server, then go to http://127.0.0.1:9001/test/visual.html to see a list of all test cases.
In a continuous integration (CI) environment, optimizing test speed is essential. It is advantageous to keep the code concise, avoid unnecessary frames, minimize canvas size, and load assets only when essential for the specific functionality under test.
To address scenarios involving operations like asynchronous 3D model rendering, consider returning a promise that resolves upon completing all the necessary tests, ensuring efficiency in your visual testing approach. Here's an example of how you can asynchronous 3D model rendering in your visual tests:
```js
visualSuite('3D Model rendering', function() {
visualTest('OBJ model is displayed correctly', function(p5, screenshot) {
// Return a Promise to ensure the test runner waits for the asynchronous operation to complete
return new Promise(resolve => {
p5.createCanvas(50, 50, p5.WEBGL);
// Load the model asynchronously
p5.loadModel('unit/assets/teapot.obj', model => {
p5.background(200);
p5.rotateX(10 * 0.01);
p5.rotateY(10 * 0.01);
p5.model(model);
// Take a screenshot for visual comparison
screenshot();
// Resolve the Promise to indicate completion
resolve();
});
});
});
});
```
9 changes: 6 additions & 3 deletions src/webgl/material.js
Original file line number Diff line number Diff line change
Expand Up @@ -1279,14 +1279,17 @@ p5.prototype.metalness = function (metallic) {
* @private blends colors according to color components.
* If alpha value is less than 1, or non-standard blendMode
* we need to enable blending on our gl context.
* @param {Number[]} color [description]
* @return {Number[]} Normalized numbers array
* @param {Number[]} color The currently set color, with values in 0-1 range
* @param {Boolean} [hasTransparency] Whether the shape being drawn has other
* transparency internally, e.g. via vertex colors
* @return {Number[]]} Normalized numbers array
*/
p5.RendererGL.prototype._applyColorBlend = function (colors) {
p5.RendererGL.prototype._applyColorBlend = function(colors, hasTransparency) {
const gl = this.GL;

const isTexture = this.drawMode === constants.TEXTURE;
const doBlend =
hasTransparency ||
this.userFillShader ||
this.userStrokeShader ||
this.userPointShader ||
Expand Down
5 changes: 2 additions & 3 deletions src/webgl/p5.Camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ p5.prototype.perspective = function (...args) {
* maximum z values.
*
* If no parameters are given, the following default is used:
* ortho(-width/2, width/2, -height/2, height/2, 0, max(width, height)).
* ortho(-width/2, width/2, -height/2, height/2, 0, max(width, height) + 800).
* @method ortho
* @for p5
* @param {Number} [left] camera frustum left plane
Expand All @@ -222,8 +222,7 @@ p5.prototype.perspective = function (...args) {
* //there's no vanishing point
* function setup() {
* createCanvas(100, 100, WEBGL);
* camera(0, 0, 50*sqrt(3), 0, 0, 0, 0, 1, 0);
* ortho(-width / 2, width / 2, height / 2, -height / 2, 0, 500);
* ortho();
* describe(
* 'two 3D boxes move back and forth along same plane, rotating as mouse is dragged.'
* );
Expand Down
32 changes: 32 additions & 0 deletions src/webgl/p5.Geometry.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,19 @@ p5.Geometry = class Geometry {
this.detailY = detailY !== undefined ? detailY : 1;
this.dirtyFlags = {};

this._hasFillTransparency = undefined;
this._hasStrokeTransparency = undefined;

if (callback instanceof Function) {
callback.call(this);
}
return this; // TODO: is this a constructor?
}

reset() {
this._hasFillTransparency = undefined;
this._hasStrokeTransparency = undefined;

this.lineVertices.clear();
this.lineTangentsIn.clear();
this.lineTangentsOut.clear();
Expand All @@ -88,6 +94,32 @@ p5.Geometry = class Geometry {

this.dirtyFlags = {};
}

hasFillTransparency() {
if (this._hasFillTransparency === undefined) {
this._hasFillTransparency = false;
for (let i = 0; i < this.vertexColors.length; i += 4) {
if (this.vertexColors[i + 3] < 1) {
this._hasFillTransparency = true;
break;
}
}
}
return this._hasFillTransparency;
}
hasStrokeTransparency() {
if (this._hasStrokeTransparency === undefined) {
this._hasStrokeTransparency = false;
for (let i = 0; i < this.lineVertexColors.length; i += 4) {
if (this.lineVertexColors[i + 3] < 1) {
this._hasStrokeTransparency = true;
break;
}
}
}
return this._hasStrokeTransparency;
}

/**
* Removes the internal colors of p5.Geometry.
* Using `clearColors()`, you can use `fill()` to supply new colors before drawing each shape.
Expand Down
10 changes: 8 additions & 2 deletions src/webgl/p5.RendererGL.Immediate.js
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,10 @@ p5.RendererGL.prototype._drawImmediateFill = function(count = 1) {
}
shader.disableRemainingAttributes();

this._applyColorBlend(this.curFillColor);
this._applyColorBlend(
this.curFillColor,
this.immediateMode.geometry.hasFillTransparency()
);

if (count === 1) {
gl.drawArrays(
Expand Down Expand Up @@ -561,7 +564,10 @@ p5.RendererGL.prototype._drawImmediateStroke = function() {
buff._prepareBuffer(this.immediateMode.geometry, shader);
}
shader.disableRemainingAttributes();
this._applyColorBlend(this.curStrokeColor);
this._applyColorBlend(
this.curStrokeColor,
this.immediateMode.geometry.hasFillTransparency()
);

gl.drawArrays(
gl.TRIANGLES,
Expand Down
10 changes: 8 additions & 2 deletions src/webgl/p5.RendererGL.Retained.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@ p5.RendererGL.prototype.drawBuffers = function(gId) {
//vertex index buffer
this._bindBuffer(geometry.indexBuffer, gl.ELEMENT_ARRAY_BUFFER);
}
this._applyColorBlend(this.curFillColor);
this._applyColorBlend(
this.curFillColor,
geometry.model.hasFillTransparency()
);
this._drawElements(gl.TRIANGLES, gId);
fillShader.unbindShader();
}
Expand All @@ -156,7 +159,10 @@ p5.RendererGL.prototype.drawBuffers = function(gId) {
buff._prepareBuffer(geometry, strokeShader);
}
strokeShader.disableRemainingAttributes();
this._applyColorBlend(this.curStrokeColor);
this._applyColorBlend(
this.curStrokeColor,
geometry.model.hasStrokeTransparency()
);
this._drawArrays(gl.TRIANGLES, gId);
strokeShader.unbindShader();
}
Expand Down
4 changes: 2 additions & 2 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1128,8 +1128,8 @@ p5.RendererGL = class RendererGL extends p5.Renderer {
target.filterCamera._resize();
this._pInst.setCamera(target.filterCamera);
this._pInst.resetMatrix();
this._pInst.image(fbo, -this.width / 2, -this.height / 2,
this.width, this.height);
this._pInst.image(fbo, -target.width / 2, -target.height / 2,
target.width, target.height);
this._pInst.pop();
this._pInst.pop();
}
Expand Down
46 changes: 46 additions & 0 deletions test/unit/visual/cases/webgl.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,52 @@ visualSuite('WebGL', function() {
});
});

visualSuite('filter', function() {
visualTest('On the main canvas', function(p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
p5.noStroke();
p5.fill('red');
p5.circle(0, 0, 20);
p5.filter(p5.GRAY);
screenshot();
});

visualTest('On a framebuffer', function(p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const fbo = p5.createFramebuffer({ antialias: true });
fbo.begin();
p5.noStroke();
p5.fill('red');
p5.circle(0, 0, 20);
p5.filter(p5.GRAY);
fbo.end();
p5.imageMode(p5.CENTER);
p5.image(fbo, 0, 0);
screenshot();
});

visualTest(
'On a framebuffer sized differently from the main canvas',
function(p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
const fbo = p5.createFramebuffer({
width: 26,
height: 26,
antialias: true
});
fbo.begin();
p5.noStroke();
p5.fill('red');
p5.circle(0, 0, 20);
p5.filter(p5.GRAY);
fbo.end();
p5.imageMode(p5.CENTER);
p5.image(fbo, 0, 0);
screenshot();
}
);
});

visualSuite('Lights', function() {
visualTest('Fill color and default ambient material', function(p5, screenshot) {
p5.createCanvas(50, 50, p5.WEBGL);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"numScreenshots": 1
}
4 changes: 4 additions & 0 deletions test/unit/visual/visualTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ window.visualTest = function(
actual.push(myp5.get());
});


if (actual.length === 0) {
throw new Error('No screenshots were generated. Check if your test generates screenshots correctly. If the test includes asynchronous operations, ensure they complete before the test ends.');
}
if (expectedScreenshots && actual.length !== expectedScreenshots) {
throw new Error(
`Expected ${expectedScreenshots} screenshot(s) but generated ${actual.length}`
Expand Down
24 changes: 24 additions & 0 deletions test/unit/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,30 @@ suite('p5.RendererGL', function() {
assert.deepEqual(myp5.get(16, 16), [255, 0, 255, 255]);
done();
});

test('transparency works the same with per-vertex colors', function() {
myp5.createCanvas(20, 20, myp5.WEBGL);
myp5.noStroke();

function drawShapes() {
myp5.fill(255, 0, 0, 100);
myp5.rect(-10, -10, 15, 15);
myp5.fill(0, 0, 255, 100);
myp5.rect(-5, -5, 15, 15);
}

drawShapes();
myp5.loadPixels();
const eachShapeResult = [...myp5.pixels];

myp5.clear();
const shapes = myp5.buildGeometry(drawShapes);
myp5.model(shapes);
myp5.loadPixels();
const singleShapeResult = [...myp5.pixels];

assert.deepEqual(eachShapeResult, singleShapeResult);
});
});

suite('BufferDef', function() {
Expand Down

0 comments on commit 88a91b0

Please sign in to comment.