Description
How would this new feature help [increase access]
Currently creation of custom 3d objects either has poor performance (drawing multiple primitives, or using beginShape/endShape), or requires separate work in a 3d modeling tool such as Blender. For users who are new to 3d graphics and would like to create complex models procedurally without having to use a separate piece of software it would be beneficial to be able to create p5.Geometry
objects for complex shapes purely within p5.js and get the same performance that they would with a model created in Blender and imported with loadModel
.
Inspiration for this feature request: https://stackoverflow.com/questions/68898347/p5-js-3d-dot-diagram-rendering-extremely-slow
Most appropriate sub-area of p5.js?
- Accessibility (Web Accessibility)
- Build tools and processes
- Color
- Core/Environment/Rendering
- Data
- DOM
- Events
- Friendly error system
- Image
- IO (Input/Output)
- Localization
- Math
- Unit Testing
- Typography
- Utilities
- WebGL
- Other (specify if possible)
New feature details:
Consider the following code that draws an icosahedron:
const PHI = (1 + Math.sqrt(5)) / 2;
let vertices = [
[0, 1, PHI],
[PHI, 0, 1],
[0, -1, PHI],
[-PHI, 0, 1],
[-1, PHI, 0],
[1, PHI, 0],
[PHI, 0, -1],
[1, -PHI, 0],
[-1, -PHI, 0],
[-PHI, 0, -1],
[0, 1, -PHI],
[0, -1, -PHI],
];
// ...
draw() {
// ...
beginShape(TRIANGLES);
for (let i = 0; i < 5; i++) {
vertex(...vertices[i + 1]);
vertex(...vertices[i + 6]);
let n = (i + 1) % 5;
vertex(...vertices[n + 6]);
vertex(...vertices[i + 1]);
vertex(...vertices[n + 1]);
vertex(...vertices[0]);
}
for (let i = 0; i < 5; i++) {
vertex(...vertices[i + 6]);
vertex(...vertices[i + 1]);
let n = (i - 1);
if (n < 0) {
n = 4;
}
vertex(...vertices[n + 1]);
vertex(...vertices[i + 6]);
vertex(...vertices[n + 6]);
vertex(...vertices[11]);
}
endShape();
}
This code would get more complicated if we also wanted to specify vertex normals, and if we wanted to draw many of these it would be detrimental for performance. Converting this to p5.Geometry
is possible but neither well documented nor trivial:
const PHI = (1 + Math.sqrt(5)) / 2;
let vertices = [
[0, 1, PHI],
[PHI, 0, 1],
[0, -1, PHI],
[-PHI, 0, 1],
[-1, PHI, 0],
[1, PHI, 0],
[PHI, 0, -1],
[1, -PHI, 0],
[-1, -PHI, 0],
[-PHI, 0, -1],
[0, 1, -PHI],
[0, -1, -PHI],
];
// ...
let geom;
setup() {
// ...
geom = new p5.Geometry(1, 1, constructIcosahedron);
// This is used as the key for caching vertex buffers. Make sure it is unique.
geom.gid = 'icosahedron';
// Automatically calculate normals based on faces.
geom.computeNormals();
}
function constructIcosahedron() {
for (let v of vertices) {
this.vertices.push(createVector(...v));
}
for (let i = 0; i < 5; i++) {
let n = (i + 1) % 5;
this.faces.push([
i + 1,
i + 6,
n + 6
]);
this.faces.push([
i + 1,
n + 1,
0
]);
}
for (let i = 0; i < 5; i++) {
let n = (i - 1);
if (n < 0) {
n = 4;
}
this.faces.push([
i + 6,
i + 1,
n + 1
]);
this.faces.push([
i + 6,
n + 6,
11
]);
}
}
draw() {
// ...
model(geom);
}
What I propose is something like this:
const PHI = (1 + Math.sqrt(5)) / 2;
let vertices = [
[0, 1, PHI],
[PHI, 0, 1],
[0, -1, PHI],
[-PHI, 0, 1],
[-1, PHI, 0],
[1, PHI, 0],
[PHI, 0, -1],
[1, -PHI, 0],
[-1, -PHI, 0],
[-PHI, 0, -1],
[0, 1, -PHI],
[0, -1, -PHI],
];
// ...
let geom;
setup() {
// ...
geom = createGeometry();
geom.beginShape(TRIANGLES);
for (let i = 0; i < 5; i++) {
geom.vertex(...vertices[i + 1]);
geom.vertex(...vertices[i + 6]);
let n = (i + 1) % 5;
geom.vertex(...vertices[n + 6]);
geom.vertex(...vertices[i + 1]);
geom.vertex(...vertices[n + 1]);
geom.vertex(...vertices[0]);
}
for (let i = 0; i < 5; i++) {
geom.vertex(...vertices[i + 6]);
geom.vertex(...vertices[i + 1]);
let n = (i - 1);
if (n < 0) {
n = 4;
}
geom.vertex(...vertices[n + 1]);
geom.vertex(...vertices[i + 6]);
geom.vertex(...vertices[n + 6]);
geom.vertex(...vertices[11]);
}
geom.endShape();
geom.computeNormals();
}
draw() {
// ...
model(geom);
}
Functions that should be available on p5.Geometry
created in this way (could actually be a different type, like p5.MutableGeometry
):
- beginShape()
- endShape()
- vertex()
- normal()
- plane()
- box()
- sphere()
- cylinder()
- cone()
- ellipsoid()
- torus()
- applyMatrix()
- resetMatrix()
- rotate()
- rotateX()
- rotateY()
- rotateZ()
- scale()
- shearX()
- shearY()
- translate()
- push()
- pop()
- clear()
- computeNormals()
Each call that creates new faces should cause the geometry id to be regenerated so that the next time it is drawn using model()
the WebGL buffers are recreated and cached using the new id.
Metadata
Metadata
Assignees
Type
Projects
Status