Skip to content

implementing beginContour and endContour in WEBGL mode #6042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions src/core/shape/vertex.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,12 @@ let isFirstContour = true;
* white rect and smaller grey rect with red outlines in center of canvas.
*/
p5.prototype.beginContour = function() {
contourVertices = [];
isContour = true;
if (this._renderer.isP3D) {
this._renderer.beginContour();
} else {
contourVertices = [];
isContour = true;
}
return this;
};

Expand Down Expand Up @@ -563,19 +567,23 @@ p5.prototype.curveVertex = function(...args) {
* white rect and smaller grey rect with red outlines in center of canvas.
*/
p5.prototype.endContour = function() {
const vert = contourVertices[0].slice(); // copy all data
vert.isVert = contourVertices[0].isVert;
vert.moveTo = false;
contourVertices.push(vert);
if (this._renderer.isP3D) {
this._renderer.endContour();
} else {
const vert = contourVertices[0].slice(); // copy all data
vert.isVert = contourVertices[0].isVert;
vert.moveTo = false;
contourVertices.push(vert);

// prevent stray lines with multiple contours
if (isFirstContour) {
vertices.push(vertices[0]);
isFirstContour = false;
}
// prevent stray lines with multiple contours
if (isFirstContour) {
vertices.push(vertices[0]);
isFirstContour = false;
}

for (let i = 0; i < contourVertices.length; i++) {
vertices.push(contourVertices[i]);
for (let i = 0; i < contourVertices.length; i++) {
vertices.push(contourVertices[i]);
}
}
return this;
};
Expand Down
146 changes: 85 additions & 61 deletions src/webgl/p5.RendererGL.Immediate.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ const immediateBufferStrides = {
uvs: 2
};

p5.RendererGL.prototype.beginContour = function() {
this.immediateMode.isContour = true;
this.immediateMode.isFirstContour = true;
this.immediateMode.geometry.contourVertices = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can store this on this.immediateMode instead of this.immediateMode.geometry? We use the same geometry class for 3D models loaded with loadModel(), which won't have this notion of contours, so this should probably just be internal to p5.RendererGL.

return this;
};

p5.RendererGL.prototype.endContour = function() {
return this.immediateMode.geometry.contourVertices;
};

/**
* adds a vertex to be drawn in a custom Shape.
* @private
Expand Down Expand Up @@ -101,7 +112,11 @@ p5.RendererGL.prototype.vertex = function(x, y) {
v = arguments[4];
}
const vert = new p5.Vector(x, y, z);
this.immediateMode.geometry.vertices.push(vert);
if (this.immediateMode.isContour) {
this.immediateMode.geometry.contourVertices.push(vert);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, I think this is generally the right approach! The one difference is that we might have multiple beginContour/endContour calls within one shape. So we probably need contourVertices to be an array of arrays in WebGL mode, instead of just a single array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually, we'll need to pass all the contours into this array in _tesselateShape here:

const contours = [

This probably means we'll need to store vertexNormals, vertexColors, vertexStrokeColors, and uvs for each contour as well as just the position.

} else {
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(
Expand Down Expand Up @@ -187,6 +202,7 @@ p5.RendererGL.prototype.endShape = function(
if (this.immediateMode.shapeMode === constants.POINTS) {
this._drawPoints(
this.immediateMode.geometry.vertices,
this.immediateMode.geometry.contourVertices,
this.immediateMode.buffers.point
);
return this;
Expand Down Expand Up @@ -224,14 +240,16 @@ p5.RendererGL.prototype.endShape = function(
* TRIANGLE_STRIP, TRIANGLE_FAN and TESS(WEBGL only)
*/
p5.RendererGL.prototype._processVertices = function(mode) {
if (this.immediateMode.geometry.vertices.length === 0) return;
if (this.immediateMode.geometry.vertices.length === 0 ||
this.immediateMode.geometry.contourVertices === 0) return;

const calculateStroke = this._doStroke;
const shouldClose = mode === constants.CLOSE;
if (calculateStroke) {
this.immediateMode.geometry.edges = this._calculateEdges(
this.immediateMode.shapeMode,
this.immediateMode.geometry.vertices,
[this.immediateMode.geometry.vertices,
this.immediateMode.geometry.contourVertices],
shouldClose
);
this.immediateMode.geometry._edgesToVertices();
Expand Down Expand Up @@ -262,64 +280,70 @@ p5.RendererGL.prototype._calculateEdges = function(
) {
const res = [];
let i = 0;
switch (shapeMode) {
case constants.TRIANGLE_STRIP:
for (i = 0; i < verts.length - 2; i++) {
res.push([i, i + 1]);
res.push([i, i + 2]);
}
res.push([i, i + 1]);
break;
case constants.TRIANGLE_FAN:
for (i = 1; i < verts.length - 1; i++) {
res.push([0, i]);
res.push([i, i + 1]);
}
res.push([0, verts.length - 1]);
break;
case constants.TRIANGLES:
for (i = 0; i < verts.length - 2; i = i + 3) {
res.push([i, i + 1]);
res.push([i + 1, i + 2]);
res.push([i + 2, i]);
}
break;
case constants.LINES:
for (i = 0; i < verts.length - 1; i = i + 2) {
res.push([i, i + 1]);
}
break;
case constants.QUADS:
// Quads have been broken up into two triangles by `vertex()`:
// 0 3--5
// | \ \ |
// 1--2 4
for (i = 0; i < verts.length - 5; i += 6) {
res.push([i, i + 1]);
res.push([i + 1, i + 2]);
res.push([i + 3, i + 5]);
res.push([i + 4, i + 5]);
}
break;
case constants.QUAD_STRIP:
// 0---2---4
// | | |
// 1---3---5
for (i = 0; i < verts.length - 2; i += 2) {
res.push([i, i + 1]);
res.push([i, i + 2]);
res.push([i + 1, i + 3]);
}
res.push([i, i + 1]);
break;
default:
for (i = 0; i < verts.length - 1; i++) {
res.push([i, i + 1]);
}
break;
}
if (shouldClose) {
res.push([verts.length - 1, 0]);
let j = 0;
while (j < 2) {
let part = [];
switch (shapeMode) {
case constants.TRIANGLE_STRIP:
for (i = 0; i < verts[j].length - 2; i++) {
part.push([i, i + 1]);
part.push([i, i + 2]);
}
part.push([i, i + 1]);
break;
case constants.TRIANGLE_FAN:
for (i = 1; i < verts[j].length - 1; i++) {
part.push([0, i]);
part.push([i, i + 1]);
}
part.push([0, verts[j].length - 1]);
break;
case constants.TRIANGLES:
for (i = 0; i < verts[j].length - 2; i = i + 3) {
part.push([i, i + 1]);
part.push([i + 1, i + 2]);
part.push([i + 2, i]);
}
break;
case constants.LINES:
for (i = 0; i < verts[j].length - 1; i = i + 2) {
part.push([i, i + 1]);
}
break;
case constants.QUADS:
// Quads have been broken up into two triangles by `vertex()`:
// 0 3--5
// | \ \ |
// 1--2 4
for (i = 0; i < verts[j].length - 5; i += 6) {
part.push([i, i + 1]);
part.push([i + 1, i + 2]);
part.push([i + 3, i + 5]);
part.push([i + 4, i + 5]);
}
break;
case constants.QUAD_STRIP:
// 0---2---4
// | | |
// 1---3---5
for (i = 0; i < verts[j].length - 2; i += 2) {
part.push([i, i + 1]);
part.push([i, i + 2]);
part.push([i + 1, i + 3]);
}
part.push([i, i + 1]);
break;
default:
for (i = 0; i < verts[j].length - 1; i++) {
part.push([i, i + 1]);
}
break;
}
if (shouldClose) {
part.push([verts[j].length - 1, 0]);
}
res.push(part);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the main branch, the expected output of this function is an array of [index1, index2] tuples, basically saying "connect the vertex at geometry.vertices[index1] with the vertex at geometry.vertices[index2] with a line." This starts to get a little tricky now that not all the vertices are in the same geometry.vertices array.

With your change, this now returns an array of arrays of those tuples. That could be helpful, since now we know that the first array element is the pairs for the main shape vertices, the second array element is the pairs for the first contour, the third array element is the pairs for the second contour, etc. It just means we'll have to update the _edgesToVertices function, which takes this output and makes all the lines, to handle this new format.

e.g. over here, it directly accesses this.vertices[index]. We'll have to update this to loop over each array of edges, and determine which set of vertices/vertex stroke colors it should be reading from:

const begin = this.vertices[currEdge[0]];

j++;
}
return res;
};
Expand Down