Skip to content

Commit f134f1d

Browse files
committed
Support instancing for immediate-mode primitives
1 parent 2d446bf commit f134f1d

11 files changed

Lines changed: 143 additions & 108 deletions

File tree

src/core/p5.Renderer3D.js

Lines changed: 58 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ export class Renderer3D extends Renderer {
532532
}
533533

534534
endShape(mode, count) {
535-
this.drawShapeCount = count;
535+
this.drawShapeCount = count ?? this._instanceCount ?? 1;
536536
super.endShape(mode, count);
537537
}
538538

@@ -2038,61 +2038,55 @@ function renderer3D(p5, fn) {
20382038
* item is accessed by index, and its properties are available by name.
20392039
*
20402040
* ```js example
2041-
* let instanceData;
2042-
* let instancesShader;
2043-
* let instance;
2044-
* let count = 5;
2045-
*
2046-
* async function setup() {
2047-
* await createCanvas(200, 200, WEBGPU);
2048-
*
2049-
* let data = [];
2050-
* for (let i = 0; i < count; i++) {
2051-
* data.push({
2052-
* position: createVector(
2053-
* random(-1, 1) * width / 2,
2054-
* random(-1, 1) * height / 2,
2055-
* 0,
2056-
* ),
2057-
* color: color(
2058-
* random(255),
2059-
* random(255),
2060-
* random(255)
2061-
* )
2062-
* });
2063-
* }
2064-
* instanceData = createStorage(data);
2065-
* instance = buildGeometry(drawInstance);
2066-
* instancesShader = buildMaterialShader(drawInstances);
2067-
* describe('Five spheres at random positions, each a different random color.');
2068-
* }
2069-
*
2070-
* function drawInstance() {
2071-
* sphere(15);
2072-
* }
2073-
*
2074-
* function drawInstances() {
2075-
* let data = uniformStorage(instanceData);
2076-
* let itemColor = sharedVec4();
2077-
*
2078-
* worldInputs.begin();
2079-
* let item = data[instanceID()];
2080-
* itemColor = item.color;
2081-
* worldInputs.position += item.position;
2082-
* worldInputs.end();
2083-
*
2084-
* finalColor.begin();
2085-
* finalColor.set(itemColor);
2086-
* finalColor.end();
2087-
* }
2088-
*
2089-
* function draw() {
2090-
* background(220);
2091-
* lights();
2092-
* noStroke();
2093-
* shader(instancesShader);
2094-
* model(instance, count);
2095-
* }
2041+
* let instanceData;
2042+
* let instancesShader;
2043+
* let count = 5;
2044+
*
2045+
* async function setup() {
2046+
* await createCanvas(200, 200, WEBGPU);
2047+
*
2048+
* let data = [];
2049+
* for (let i = 0; i < count; i++) {
2050+
* data.push({
2051+
* position: createVector(
2052+
* random(-1, 1) * width / 2,
2053+
* random(-1, 1) * height / 2,
2054+
* 0,
2055+
* ),
2056+
* color: color(
2057+
* random(255),
2058+
* random(255),
2059+
* random(255)
2060+
* )
2061+
* });
2062+
* }
2063+
* instanceData = createStorage(data);
2064+
* instancesShader = buildMaterialShader(drawInstances);
2065+
* describe('Five spheres at random positions, each a different random color.');
2066+
* }
2067+
*
2068+
* function drawInstances() {
2069+
* let data = uniformStorage(instanceData);
2070+
* let itemColor = sharedVec4();
2071+
*
2072+
* worldInputs.begin();
2073+
* let item = data[instanceIndex];
2074+
* itemColor = item.color;
2075+
* worldInputs.position += item.position;
2076+
* worldInputs.end();
2077+
*
2078+
* finalColor.begin();
2079+
* finalColor.set(itemColor);
2080+
* finalColor.end();
2081+
* }
2082+
*
2083+
* function draw() {
2084+
* background(220);
2085+
* lights();
2086+
* noStroke();
2087+
* shader(instancesShader);
2088+
* instances(count).sphere(15);
2089+
* }
20962090
* ```
20972091
*
20982092
* You can also store a plain list of numbers by passing an array of numbers.
@@ -2246,23 +2240,21 @@ function renderer3D(p5, fn) {
22462240
* initial data. Connect your iteration function to the storage by passing the storage
22472241
* into <a href="#/p5/uniformStorage">`uniformStorage`</a>.
22482242
*
2249-
* Often, compute shaders are paired with <a href="#/p5/model">`model(myGeometry, count)`</a>
2243+
* Often, compute shaders are paired with <a href="#/p5/instances">`instances(count)`</a>
22502244
* to draw one instance per object in the storage, and a shader that uses
2251-
* <a href="#/p5/instanceID">`instanceID()`</a> to position each instance.
2245+
* <a href="#/p5/instanceIndex">`instanceIndex`</a> to position each instance.
22522246
*
22532247
* ```js example
22542248
* let particles;
22552249
* let computeShader;
22562250
* let displayShader;
2257-
* let instance;
22582251
* const numParticles = 100;
22592252
*
22602253
* async function setup() {
22612254
* await createCanvas(100, 100, WEBGPU);
22622255
* particles = createStorage(makeParticles(width / 2, height / 2));
22632256
* computeShader = buildComputeShader(simulate);
22642257
* displayShader = buildMaterialShader(display);
2265-
* instance = buildGeometry(drawParticle);
22662258
* describe('100 orange particles shooting outward.');
22672259
* }
22682260
*
@@ -2279,10 +2271,6 @@ function renderer3D(p5, fn) {
22792271
* return data;
22802272
* }
22812273
*
2282-
* function drawParticle() {
2283-
* sphere(2);
2284-
* }
2285-
*
22862274
* function simulate() {
22872275
* let data = uniformStorage(particles);
22882276
* let idx = index.x;
@@ -2292,7 +2280,7 @@ function renderer3D(p5, fn) {
22922280
* function display() {
22932281
* let data = uniformStorage(particles);
22942282
* worldInputs.begin();
2295-
* let pos = data[instanceID()].position;
2283+
* let pos = data[instanceIndex].position;
22962284
* worldInputs.position.xy += pos - [width / 2, height / 2];
22972285
* worldInputs.end();
22982286
* }
@@ -2306,15 +2294,14 @@ function renderer3D(p5, fn) {
23062294
* noStroke();
23072295
* fill(255, 200, 50);
23082296
* shader(displayShader);
2309-
* model(instance, numParticles);
2297+
* instances(numParticles).sphere(2);
23102298
* }
23112299
* ```
23122300
*
23132301
* ```js example
23142302
* let particles;
23152303
* let computeShader;
23162304
* let displayShader;
2317-
* let instance;
23182305
* const numParticles = 50;
23192306
*
23202307
* async function setup() {
@@ -2337,14 +2324,9 @@ function renderer3D(p5, fn) {
23372324
*
23382325
* computeShader = buildComputeShader(simulate);
23392326
* displayShader = buildMaterialShader(display);
2340-
* instance = buildGeometry(drawParticle);
23412327
* describe('50 white spheres bouncing around the canvas.');
23422328
* }
23432329
*
2344-
* function drawParticle() {
2345-
* sphere(3);
2346-
* }
2347-
*
23482330
* function simulate() {
23492331
* let r = 3;
23502332
* let data = uniformStorage(particles);
@@ -2367,7 +2349,7 @@ function renderer3D(p5, fn) {
23672349
* function display() {
23682350
* let data = uniformStorage(particles);
23692351
* worldInputs.begin();
2370-
* let pos = data[instanceID()].position;
2352+
* let pos = data[instanceIndex].position;
23712353
* worldInputs.position.xy += pos;
23722354
* worldInputs.end();
23732355
* }
@@ -2379,7 +2361,7 @@ function renderer3D(p5, fn) {
23792361
* fill(255);
23802362
* lights();
23812363
* shader(displayShader);
2382-
* model(instance, numParticles);
2364+
* instances(numParticles).sphere(3);
23832365
* }
23842366
* ```
23852367
*
@@ -2416,7 +2398,6 @@ function renderer3D(p5, fn) {
24162398
* let particles;
24172399
* let computeShader;
24182400
* let displayShader;
2419-
* let instance;
24202401
* const numParticles = 50;
24212402
*
24222403
* async function setup() {
@@ -2439,14 +2420,9 @@ function renderer3D(p5, fn) {
24392420
*
24402421
* computeShader = buildComputeShader(simulate);
24412422
* displayShader = buildMaterialShader(display);
2442-
* instance = buildGeometry(drawParticle);
24432423
* describe('50 white spheres bouncing around the canvas.');
24442424
* }
24452425
*
2446-
* function drawParticle() {
2447-
* sphere(3);
2448-
* }
2449-
*
24502426
* function simulate() {
24512427
* let r = 3;
24522428
* let data = uniformStorage(particles);
@@ -2469,7 +2445,7 @@ function renderer3D(p5, fn) {
24692445
* function display() {
24702446
* let data = uniformStorage(particles);
24712447
* worldInputs.begin();
2472-
* let pos = data[instanceID()].position;
2448+
* let pos = data[instanceIndex].position;
24732449
* worldInputs.position.xy += pos;
24742450
* worldInputs.end();
24752451
* }
@@ -2481,7 +2457,7 @@ function renderer3D(p5, fn) {
24812457
* fill(255);
24822458
* lights();
24832459
* shader(displayShader);
2484-
* model(instance, numParticles);
2460+
* instances(numParticles).sphere(3);
24852461
* }
24862462
* ```
24872463
*

src/shape/vertex.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,11 +833,20 @@ function vertex(p5, fn){
833833
* describe('A row of four squares. Their colors transition from purple on the left to red on the right');
834834
* }
835835
*/
836-
fn.endShape = function(mode, count = 1) {
836+
fn.endShape = function(mode, count) {
837837
// p5._validateParameters('endShape', arguments);
838-
if (count < 1) {
839-
console.log('🌸 p5.js says: You can not have less than one instance');
840-
count = 1;
838+
if (typeof mode === 'number') {
839+
count = mode;
840+
mode = undefined;
841+
}
842+
843+
if (count !== undefined) {
844+
if (count < 1) {
845+
console.log('🌸 p5.js says: You can not have less than one instance');
846+
count = 1;
847+
} else {
848+
count = Math.round(count);
849+
}
841850
}
842851

843852
this._renderer.endShape(mode, count);

src/webgl/3d_primitives.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,6 +2611,10 @@ function primitives3D(p5, fn){
26112611
* @property {function(x: Number, y: Number, w: Number, h: Number=, detail: Number=): undefined} ellipse
26122612
* @property {function(x: Number, y: Number, w: Number, h: Number, start: Number, stop: Number, mode: String=, detail: Number=): undefined} arc
26132613
* @property {function(model: p5.Geometry, count: Number=): undefined} model
2614+
* @property {function(x1: Number, y1: Number, z1: Number|Number, x2: Number=, y2: Number=, z2: Number=): undefined} line
2615+
* @property {function(x: Number, y: Number, z: Number=): undefined} point
2616+
* @property {function(x1: Number, y1: Number, z1: Number|Number, x2: Number=, y2: Number=, z2: Number=, x3: Number=, y3: Number=, z3: Number=, x4: Number=, y4: Number=, z4: Number=): undefined} bezier
2617+
* @property {function(x1: Number, y1: Number, z1: Number|Number, x2: Number=, y2: Number=, z2: Number=, x3: Number=, y3: Number=, z3: Number=, x4: Number=, y4: Number=, z4: Number=): undefined} spline
26142618
*/
26152619

26162620
/**
@@ -2627,7 +2631,7 @@ function primitives3D(p5, fn){
26272631
* integer.
26282632
* @returns {p5.InstancesWrapper} an object with methods `sphere`, `box`, `plane`,
26292633
* `ellipsoid`, `cylinder`, `cone`, `torus`, `triangle`, `rect`, `quad`,
2630-
* `ellipse`, `arc`, and `model`. Call one of
2634+
* `ellipse`, `arc`, `model`, `line`, `point`, `bezier`, and `spline`. Call one of
26312635
* these to draw `count` instances of that primitive.
26322636
*
26332637
* @example
@@ -2688,7 +2692,7 @@ function primitives3D(p5, fn){
26882692
}
26892693
};
26902694

2691-
return {
2695+
const result = {
26922696
sphere: wrap(r.sphere),
26932697
box: wrap(r.box),
26942698
plane: wrap(r.plane),
@@ -2701,8 +2705,18 @@ function primitives3D(p5, fn){
27012705
quad: wrap(this.quad, this),
27022706
ellipse: wrap(this.ellipse, this),
27032707
arc: wrap(this.arc, this),
2704-
model: wrap(r.model)
2708+
model: wrap(r.model),
2709+
line: wrap(this.line, this),
2710+
point: wrap(this.point, this),
2711+
bezier: wrap(this.bezier, this),
2712+
spline: wrap(this.spline, this)
27052713
};
2714+
2715+
if (typeof this.curve === 'function') {
2716+
result.curve = wrap(this.curve, this);
2717+
}
2718+
2719+
return result;
27062720
};
27072721
}
27082722

src/webgl/loading.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,23 +1077,17 @@ function loading(p5, fn){
10771077
* }
10781078
* ```
10791079
*
1080-
* Multiple instances can be drawn at once with `model(geometry, count)`. On its own,
1080+
* Multiple instances can be drawn at once with `instances(count)`. On its own,
10811081
* all the instances get drawn to the same spot, but you can use
1082-
* <a href="#/p5/instanceID">`instanceID()`</a> inside of a shader to handle each instance.
1082+
* <a href="#/p5/instanceIndex">`instanceIndex`</a> inside of a shader to handle each instance.
10831083
* At large counts, this often runs faster than using a `for` loop.
10841084
*
10851085
* ```js example
10861086
* let instancesShader;
1087-
* let instance;
10881087
* let count = 5;
10891088
*
1090-
* function drawInstance() {
1091-
* sphere(15);
1092-
* }
1093-
*
10941089
* function setup() {
10951090
* createCanvas(200, 200, WEBGL);
1096-
* instance = buildGeometry(drawInstance);
10971091
* instancesShader = buildMaterialShader(drawSpaced);
10981092
* }
10991093
*
@@ -1102,7 +1096,7 @@ function loading(p5, fn){
11021096
* // Spread spheres evenly across the canvas based on their index
11031097
* let spacing = width / count;
11041098
* worldInputs.position.x +=
1105-
* (instanceID() - (count - 1) / 2) * spacing;
1099+
* (instanceIndex - (count - 1) / 2) * spacing;
11061100
* worldInputs.end();
11071101
* }
11081102
*
@@ -1112,7 +1106,7 @@ function loading(p5, fn){
11121106
* noStroke();
11131107
* fill('red');
11141108
* shader(instancesShader);
1115-
* model(instance, count);
1109+
* instances(count).sphere(15);
11161110
* }
11171111
* ```
11181112
*

0 commit comments

Comments
 (0)