simulationjsv2 0.2.8 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/TODO.md ADDED
@@ -0,0 +1,4 @@
1
+ # TODO
2
+
3
+ - [ ] Memoize render scene buffer
4
+ - Should only be reset to resize to a new larger object then reused on all frames
@@ -1,4 +1,6 @@
1
- export declare const BUF_LEN = 10;
2
- export declare const vertexSize = 40;
1
+ export declare const vertexSize = 44;
2
+ export declare const positionOffset = 0;
3
3
  export declare const colorOffset = 16;
4
4
  export declare const uvOffset = 32;
5
+ export declare const drawingInstancesOffset = 40;
6
+ export declare const BUF_LEN: number;
package/dist/constants.js CHANGED
@@ -1,4 +1,6 @@
1
- export const BUF_LEN = 10;
2
- export const vertexSize = 40; // 4 * 10
1
+ export const vertexSize = 44; // 4 * 10
2
+ export const positionOffset = 0;
3
3
  export const colorOffset = 16; // 4 * 4
4
4
  export const uvOffset = 32; // 4 * 8
5
+ export const drawingInstancesOffset = 40;
6
+ export const BUF_LEN = vertexSize / 4;
@@ -24,7 +24,6 @@ export declare class PlaneGeometry extends Geometry {
24
24
  protected triangleOrder: number[];
25
25
  private rawVertices;
26
26
  constructor(vertices: Vertex[]);
27
- private updateWireframeOrder;
28
27
  recompute(): void;
29
28
  updateVertices(vertices: Vertex[]): void;
30
29
  getTriangleBuffer(color: Color): number[];
@@ -75,6 +74,9 @@ export declare class SplineGeometry extends Geometry {
75
74
  constructor(points: SplinePoint2d[], color: Color, thickness: number, detail: number);
76
75
  updateInterpolationStart(start: number): void;
77
76
  updateInterpolationLimit(limit: number): void;
77
+ private getVertexCount;
78
+ getWireframeVertexCount(): number;
79
+ getTriangleVertexCount(): number;
78
80
  private computeCurves;
79
81
  private updateWireframeOrder;
80
82
  recompute(): void;
package/dist/geometry.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { mat4, vec2, vec3 } from 'wgpu-matrix';
2
- import { cloneBuf, interpolateColors, lossyTriangulate, matrix4, triangulateWireFrameOrder, vector2, vector2FromVector3, vector3, vector3FromVector2, vertex, vertexBuffer } from './utils.js';
2
+ import { cloneBuf, interpolateColors, lossyTriangulate, matrix4, triangulateWireFrameOrder, vector2, vector2FromVector3, vector3, vector3FromVector2, vertex, bufferGenerator } from './utils.js';
3
3
  import { CubicBezierCurve2d } from './graphics.js';
4
+ import { BUF_LEN } from './constants.js';
4
5
  export class Geometry {
5
6
  vertices;
6
7
  matrix;
@@ -27,7 +28,7 @@ export class Geometry {
27
28
  .map((vertexIndex) => {
28
29
  const pos = cloneBuf(this.vertices[vertexIndex]);
29
30
  vec3.transformMat4(pos, this.matrix, pos);
30
- return vertexBuffer(pos[0], pos[1], pos[2], color);
31
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], color);
31
32
  })
32
33
  .flat();
33
34
  }
@@ -50,22 +51,22 @@ export class PlaneGeometry extends Geometry {
50
51
  this.rawVertices = vertices;
51
52
  this.updateVertices(vertices);
52
53
  }
53
- updateWireframeOrder() {
54
- this.wireframeOrder = triangulateWireFrameOrder(this.vertices.length);
55
- }
56
54
  recompute() { }
57
55
  updateVertices(vertices) {
58
56
  this.rawVertices = vertices;
59
57
  this.vertices = vertices.map((vertex) => vertex.getPos());
60
- this.updateWireframeOrder();
58
+ this.wireframeOrder = triangulateWireFrameOrder(this.vertices.length);
59
+ this.triangleOrder = lossyTriangulate(Array(this.rawVertices.length)
60
+ .fill(0)
61
+ .map((_, index) => index)).flat();
61
62
  }
62
63
  getTriangleBuffer(color) {
63
- return lossyTriangulate(this.rawVertices)
64
- .flat()
65
- .map((vertex) => {
64
+ return this.triangleOrder
65
+ .map((index) => {
66
+ const vertex = this.rawVertices[index];
66
67
  const pos = cloneBuf(vertex.getPos());
67
68
  vec3.transformMat4(pos, this.matrix, pos);
68
- return vertexBuffer(pos[0], pos[1], pos[2], vertex.getColor() || color);
69
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], vertex.getColor() || color);
69
70
  })
70
71
  .flat();
71
72
  }
@@ -156,7 +157,7 @@ export class SquareGeometry extends Geometry {
156
157
  .map((vertexIndex) => {
157
158
  const pos = cloneBuf(this.vertices[vertexIndex]);
158
159
  vec3.transformMat4(pos, this.matrix, pos);
159
- return vertexBuffer(pos[0], pos[1], pos[2], this.params.colorMap[vertexIndex] || color);
160
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], this.params.colorMap[vertexIndex] || color);
160
161
  })
161
162
  .flat();
162
163
  }
@@ -212,7 +213,7 @@ export class SplineGeometry extends Geometry {
212
213
  triangleOrder;
213
214
  params;
214
215
  constructor(points, color, thickness, detail) {
215
- super([], 'list');
216
+ super();
216
217
  this.wireframeOrder = [];
217
218
  this.triangleOrder = [];
218
219
  this.params = {
@@ -235,6 +236,15 @@ export class SplineGeometry extends Geometry {
235
236
  updateInterpolationLimit(limit) {
236
237
  this.params.interpolateLimit = Math.min(1, Math.max(0, limit));
237
238
  }
239
+ getVertexCount() {
240
+ return this.triangleOrder.length * BUF_LEN;
241
+ }
242
+ getWireframeVertexCount() {
243
+ return this.getVertexCount();
244
+ }
245
+ getTriangleVertexCount() {
246
+ return this.getVertexCount();
247
+ }
238
248
  computeCurves() {
239
249
  for (let i = 0; i < this.params.points.length; i++) {
240
250
  let prevControl = null;
@@ -319,7 +329,7 @@ export class SplineGeometry extends Geometry {
319
329
  .map((vertexIndex) => {
320
330
  const vertex = cloneBuf(this.vertices[vertexIndex]);
321
331
  vec3.transformMat4(vertex, this.matrix, vertex);
322
- return vertexBuffer(vertex[0], vertex[1], vertex[2], color);
332
+ return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], color);
323
333
  })
324
334
  .flat();
325
335
  }
@@ -328,7 +338,7 @@ export class SplineGeometry extends Geometry {
328
338
  .map((vertexIndex) => {
329
339
  const vertex = cloneBuf(this.vertices[vertexIndex]);
330
340
  vec3.transformMat4(vertex, this.matrix, vertex);
331
- return vertexBuffer(vertex[0], vertex[1], vertex[2], this.params.vertexColors[vertexIndex]);
341
+ return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], this.params.vertexColors[vertexIndex]);
332
342
  })
333
343
  .flat();
334
344
  }
@@ -406,7 +416,7 @@ export class PolygonGeometry extends Geometry {
406
416
  .map((vertexIndex) => {
407
417
  const vertex = cloneBuf(this.vertices[vertexIndex]);
408
418
  vec3.transformMat4(vertex, this.matrix, vertex);
409
- return vertexBuffer(vertex[0], vertex[1], 0, this.params.points[vertexIndex].getColor() || color);
419
+ return bufferGenerator.generate(vertex[0], vertex[1], 0, this.params.points[vertexIndex].getColor() || color);
410
420
  })
411
421
  .flat();
412
422
  }
@@ -1,7 +1,8 @@
1
+ /// <reference types="dist" />
1
2
  import { Camera } from './simulation.js';
2
- import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation } from './types.js';
3
+ import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation, Mat4 } from './types.js';
3
4
  import { Vertex, VertexCache, Color } from './utils.js';
4
- import { CircleGeometry, CubeGeometry, Geometry, Line2dGeometry, Line3dGeometry, PlaneGeometry, PolygonGeometry, SplineGeometry, SquareGeometry } from './geometry.js';
5
+ import { BlankGeometry, CircleGeometry, CubeGeometry, Geometry, Line2dGeometry, Line3dGeometry, PlaneGeometry, PolygonGeometry, SplineGeometry, SquareGeometry } from './geometry.js';
5
6
  export declare abstract class SimulationElement<T extends Vector2 | Vector3 = Vector3> {
6
7
  protected abstract pos: T;
7
8
  protected abstract geometry: Geometry;
@@ -9,16 +10,17 @@ export declare abstract class SimulationElement<T extends Vector2 | Vector3 = Ve
9
10
  protected wireframe: boolean;
10
11
  protected vertexCache: VertexCache;
11
12
  protected rotation: ElementRotation<T>;
12
- readonly is3d: boolean;
13
+ isInstanced: boolean;
13
14
  /**
14
15
  * @param pos - Expected to be adjusted to devicePixelRatio before reaching constructor
15
16
  */
16
- constructor(color: Color | undefined, rotation: ElementRotation<T>, is3d?: boolean);
17
+ constructor(color: Color | undefined, rotation: ElementRotation<T>);
17
18
  getGeometryType(): "list" | "strip";
18
19
  setWireframe(wireframe: boolean): void;
19
20
  isWireframe(): boolean;
20
21
  getColor(): Color;
21
22
  getPos(): T;
23
+ getRotation(): ElementRotation<T>;
22
24
  fill(newColor: Color, t?: number, f?: LerpFunc): Promise<void>;
23
25
  abstract move(amount: T, t?: number, f?: LerpFunc): Promise<void>;
24
26
  abstract moveTo(pos: T, t?: number, f?: LerpFunc): Promise<void>;
@@ -31,7 +33,8 @@ export declare abstract class SimulationElement<T extends Vector2 | Vector3 = Ve
31
33
  }
32
34
  export declare abstract class SimulationElement3d extends SimulationElement {
33
35
  protected pos: Vector3;
34
- rotation: Vector3;
36
+ protected rotation: Vector3;
37
+ is3d: boolean;
35
38
  constructor(pos: Vector3, rotation?: Vector3, color?: Color);
36
39
  rotate(amount: Vector3, t?: number, f?: LerpFunc): Promise<void>;
37
40
  rotateTo(rot: Vector3, t?: number, f?: LerpFunc): Promise<void>;
@@ -164,3 +167,21 @@ export declare class Spline2d extends SimulationElement2d {
164
167
  interpolate(t: number): Vector2;
165
168
  protected updateMatrix(camera: Camera): void;
166
169
  }
170
+ export declare class Instance<T extends SimulationElement2d | SimulationElement3d> extends SimulationElement3d {
171
+ protected geometry: BlankGeometry;
172
+ private obj;
173
+ private instanceMatrix;
174
+ private matrixBuffer;
175
+ private device;
176
+ readonly isInstance = true;
177
+ constructor(obj: T, numInstances: number);
178
+ private setMatrixBuffer;
179
+ getInstances(): Mat4[];
180
+ getNumInstances(): number;
181
+ setDevice(device: GPUDevice): void;
182
+ getMatrixBuffer(): GPUBuffer | null;
183
+ getVertexCount(): number;
184
+ getGeometryType(): "list" | "strip";
185
+ protected updateMatrix(_: Camera): void;
186
+ getBuffer(camera: Camera): number[];
187
+ }
package/dist/graphics.js CHANGED
@@ -1,21 +1,21 @@
1
1
  import { vec3, mat4, vec2, vec4 } from 'wgpu-matrix';
2
- import { Vertex, VertexCache, cloneBuf, color, colorFromVector4, vector3ToPixelRatio, vector2, vector3, vertex, Color, transitionValues, logger, vector2FromVector3, matrix4, rotateMat4, vector3FromVector2, vector2ToPixelRatio } from './utils.js';
3
- import { CircleGeometry, CubeGeometry, Line2dGeometry, Line3dGeometry, PlaneGeometry, PolygonGeometry, SplineGeometry, SquareGeometry } from './geometry.js';
2
+ import { Vertex, VertexCache, cloneBuf, color, colorFromVector4, vector3ToPixelRatio, vector2, vector3, vertex, Color, transitionValues, logger, vector2FromVector3, matrix4, rotateMat4, vector3FromVector2, vector2ToPixelRatio, bufferGenerator } from './utils.js';
3
+ import { BlankGeometry, CircleGeometry, CubeGeometry, Line2dGeometry, Line3dGeometry, PlaneGeometry, PolygonGeometry, SplineGeometry, SquareGeometry } from './geometry.js';
4
4
  export class SimulationElement {
5
5
  color;
6
6
  wireframe;
7
7
  vertexCache;
8
8
  rotation;
9
- is3d;
9
+ isInstanced;
10
10
  /**
11
11
  * @param pos - Expected to be adjusted to devicePixelRatio before reaching constructor
12
12
  */
13
- constructor(color = new Color(), rotation, is3d = true) {
13
+ constructor(color = new Color(), rotation) {
14
14
  this.color = color;
15
15
  this.vertexCache = new VertexCache();
16
- this.is3d = is3d;
17
16
  this.wireframe = false;
18
17
  this.rotation = rotation;
18
+ this.isInstanced = false;
19
19
  }
20
20
  getGeometryType() {
21
21
  return this.geometry.getType();
@@ -32,6 +32,9 @@ export class SimulationElement {
32
32
  getPos() {
33
33
  return this.pos;
34
34
  }
35
+ getRotation() {
36
+ return this.rotation;
37
+ }
35
38
  fill(newColor, t = 0, f) {
36
39
  const diff = newColor.diff(this.color);
37
40
  const finalColor = newColor.clone();
@@ -47,6 +50,9 @@ export class SimulationElement {
47
50
  }, t, f);
48
51
  }
49
52
  getVertexCount() {
53
+ if (this.vertexCache.shouldUpdate()) {
54
+ this.geometry.recompute();
55
+ }
50
56
  if (this.isWireframe()) {
51
57
  return this.geometry.getWireframeVertexCount();
52
58
  }
@@ -70,6 +76,9 @@ export class SimulationElement {
70
76
  if (this.vertexCache.shouldUpdate() || camera.hasUpdated()) {
71
77
  this.updateMatrix(camera);
72
78
  this.geometry.recompute();
79
+ if (this.isInstanced) {
80
+ bufferGenerator.setInstancing(true);
81
+ }
73
82
  let resBuffer = [];
74
83
  if (this.isWireframe()) {
75
84
  resBuffer = this.geometry.getWireframeBuffer(this.color);
@@ -77,6 +86,7 @@ export class SimulationElement {
77
86
  else {
78
87
  resBuffer = this.geometry.getTriangleBuffer(this.color);
79
88
  }
89
+ bufferGenerator.setInstancing(false);
80
90
  this.vertexCache.setCache(resBuffer);
81
91
  return resBuffer;
82
92
  }
@@ -86,6 +96,7 @@ export class SimulationElement {
86
96
  export class SimulationElement3d extends SimulationElement {
87
97
  pos;
88
98
  rotation;
99
+ is3d = true;
89
100
  constructor(pos, rotation = vector3(), color) {
90
101
  super(color, rotation);
91
102
  this.pos = pos;
@@ -149,7 +160,7 @@ export class SimulationElement2d extends SimulationElement {
149
160
  pos;
150
161
  rotation;
151
162
  constructor(pos, rotation = 0, color) {
152
- super(color, rotation, false);
163
+ super(color, rotation);
153
164
  this.pos = pos;
154
165
  this.rotation = rotation;
155
166
  }
@@ -396,7 +407,6 @@ export class Circle extends SimulationElement2d {
396
407
  this.defaultUpdateMatrix(camera);
397
408
  }
398
409
  }
399
- // TODO: litterally this whole thing
400
410
  export class Polygon extends SimulationElement2d {
401
411
  geometry;
402
412
  vertices;
@@ -807,3 +817,69 @@ export class Spline2d extends SimulationElement2d {
807
817
  this.defaultUpdateMatrix(camera);
808
818
  }
809
819
  }
820
+ export class Instance extends SimulationElement3d {
821
+ geometry;
822
+ obj;
823
+ instanceMatrix;
824
+ matrixBuffer;
825
+ device;
826
+ isInstance = true;
827
+ constructor(obj, numInstances) {
828
+ super(vector3());
829
+ this.device = null;
830
+ this.matrixBuffer = null;
831
+ obj.isInstanced = true;
832
+ this.obj = obj;
833
+ this.instanceMatrix = [];
834
+ this.is3d = Boolean(obj.is3d);
835
+ this.geometry = new BlankGeometry();
836
+ const mat = matrix4();
837
+ if (typeof obj.getRotation() === 'number') {
838
+ mat4.rotateZ(mat, obj.getRotation(), mat);
839
+ }
840
+ else {
841
+ rotateMat4(mat, obj.getRotation());
842
+ }
843
+ for (let i = 0; i < numInstances; i++) {
844
+ const clone = cloneBuf(mat);
845
+ this.instanceMatrix.push(clone);
846
+ }
847
+ }
848
+ setMatrixBuffer() {
849
+ if (!this.device || this.instanceMatrix.length === 0)
850
+ return;
851
+ this.matrixBuffer = this.device.createBuffer({
852
+ size: this.instanceMatrix[0].length * 4 * this.instanceMatrix.length,
853
+ usage: GPUBufferUsage.STORAGE,
854
+ mappedAtCreation: true
855
+ });
856
+ const buf = this.instanceMatrix.map((mat) => [...mat]).flat();
857
+ new Float32Array(this.matrixBuffer.getMappedRange()).set(buf);
858
+ this.matrixBuffer.unmap();
859
+ }
860
+ getInstances() {
861
+ return this.instanceMatrix;
862
+ }
863
+ getNumInstances() {
864
+ return this.instanceMatrix.length;
865
+ }
866
+ setDevice(device) {
867
+ this.device = device;
868
+ if (this.matrixBuffer === null) {
869
+ this.setMatrixBuffer();
870
+ }
871
+ }
872
+ getMatrixBuffer() {
873
+ return this.matrixBuffer;
874
+ }
875
+ getVertexCount() {
876
+ return this.obj.getVertexCount();
877
+ }
878
+ getGeometryType() {
879
+ return this.obj.getGeometryType();
880
+ }
881
+ updateMatrix(_) { }
882
+ getBuffer(camera) {
883
+ return this.obj.getBuffer(camera);
884
+ }
885
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './simulation.js';
2
2
  export * from './graphics.js';
3
3
  export * from './types.js';
4
- export { Vertex, Color, cloneBuf, vector4, vector3, vector2, vector3FromVector2, colorFromVector4, randomInt, randomColor, vertex, color, colorf, transitionValues, lerp, smoothStep, linearStep, splinePoint2d, continuousSplinePoint2d, easeInOutQuad, easeInOutExpo, easeInOutQuart, waitFor } from './utils.js';
4
+ export { Vertex, Color, cloneBuf, vector4, vector3, vector2, vector3FromVector2, colorFromVector4, randomInt, randomColor, vertex, color, colorf, transitionValues, lerp, smoothStep, linearStep, splinePoint2d, continuousSplinePoint2d, easeInOutQuad, easeInOutExpo, easeInOutQuart, waitFor, matrix4 } from './utils.js';
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export * from './simulation.js';
2
2
  export * from './graphics.js';
3
3
  export * from './types.js';
4
- export { Vertex, Color, cloneBuf, vector4, vector3, vector2, vector3FromVector2, colorFromVector4, randomInt, randomColor, vertex, color, colorf, transitionValues, lerp, smoothStep, linearStep, splinePoint2d, continuousSplinePoint2d, easeInOutQuad, easeInOutExpo, easeInOutQuart, waitFor } from './utils.js';
4
+ export { Vertex, Color, cloneBuf, vector4, vector3, vector2, vector3FromVector2, colorFromVector4, randomInt, randomColor, vertex, color, colorf, transitionValues, lerp, smoothStep, linearStep, splinePoint2d, continuousSplinePoint2d, easeInOutQuad, easeInOutExpo, easeInOutQuart, waitFor, matrix4 } from './utils.js';
@@ -12,14 +12,15 @@ export declare class Simulation {
12
12
  private frameRateView;
13
13
  private camera;
14
14
  private pipelines;
15
+ private renderInfo;
15
16
  constructor(idOrCanvasRef: string | HTMLCanvasElement, camera?: Camera | null, showFrameRate?: boolean);
16
17
  add(el: SimulationElement<any>): void;
17
18
  setCanvasSize(width: number, height: number): void;
18
19
  start(): void;
19
20
  stop(): void;
20
21
  setBackground(color: Color): void;
22
+ private propagateDevice;
21
23
  render(device: GPUDevice, ctx: GPUCanvasContext): void;
22
- private getVertexCount;
23
24
  private renderScene;
24
25
  fitElement(): void;
25
26
  private assertHasCanvas;
@@ -28,6 +29,7 @@ export declare class SceneCollection extends SimulationElement3d {
28
29
  protected geometry: BlankGeometry;
29
30
  private name;
30
31
  private scene;
32
+ readonly isCollection = true;
31
33
  constructor(name: string);
32
34
  setWireframe(_: boolean): void;
33
35
  getName(): string;
@@ -1,16 +1,17 @@
1
1
  import { vec3 } from 'wgpu-matrix';
2
2
  import { SimulationElement3d } from './graphics.js';
3
3
  import { BUF_LEN } from './constants.js';
4
- import { Color, applyElementToScene, buildDepthTexture, buildMultisampleTexture, buildProjectionMatrix, createPipeline, getOrthoMatrix, getTransformationMatrix, logger, transitionValues, vector2, vector3 } from './utils.js';
4
+ import { Color, applyElementToScene, buildDepthTexture, buildMultisampleTexture, buildProjectionMatrix, createPipeline, getOrthoMatrix, getTotalVertices, getTransformationMatrix, logger, transitionValues, vector2, vector3 } from './utils.js';
5
5
  import { BlankGeometry } from './geometry.js';
6
6
  const shader = `
7
7
  struct Uniforms {
8
8
  modelViewProjectionMatrix : mat4x4<f32>,
9
- orthoProjectionMatrix : mat4x4<f32>,
10
- screenSize : vec2<f32>,
9
+ orthoProjectionMatrix : mat4x4<f32>
11
10
  }
12
11
 
13
- @binding(0) @group(0) var<uniform> uniforms : Uniforms;
12
+ @group(0) @binding(0) var<uniform> uniforms : Uniforms;
13
+
14
+ @group(0) @binding(1) var<storage, read> instanceMatrices : array<mat4x4f, 10>;
14
15
 
15
16
  struct VertexOutput {
16
17
  @builtin(position) Position : vec4<f32>,
@@ -21,9 +22,11 @@ struct VertexOutput {
21
22
 
22
23
  @vertex
23
24
  fn vertex_main_3d(
25
+ @builtin(instance_index) instanceIdx : u32,
24
26
  @location(0) position : vec4<f32>,
25
27
  @location(1) color : vec4<f32>,
26
28
  @location(2) uv : vec2<f32>,
29
+ @location(3) drawingInstance: f32
27
30
  ) -> VertexOutput {
28
31
  var output : VertexOutput;
29
32
 
@@ -36,13 +39,21 @@ fn vertex_main_3d(
36
39
 
37
40
  @vertex
38
41
  fn vertex_main_2d(
42
+ @builtin(instance_index) instanceIdx : u32,
39
43
  @location(0) position : vec4<f32>,
40
44
  @location(1) color : vec4<f32>,
41
45
  @location(2) uv : vec2<f32>,
46
+ @location(3) drawingInstance: f32
42
47
  ) -> VertexOutput {
43
- var output : VertexOutput;
48
+ var output: VertexOutput;
49
+
50
+ if (drawingInstance == 1) {
51
+ let transformedPos = instanceMatrices[instanceIdx] * position;
52
+ output.Position = uniforms.orthoProjectionMatrix * transformedPos;
53
+ } else {
54
+ output.Position = uniforms.orthoProjectionMatrix * position;
55
+ }
44
56
 
45
- output.Position = uniforms.orthoProjectionMatrix * position;
46
57
  output.fragUV = uv;
47
58
  output.fragPosition = position;
48
59
  output.fragColor = color;
@@ -56,7 +67,6 @@ fn fragment_main(
56
67
  @location(2) fragPosition: vec4<f32>
57
68
  ) -> @location(0) vec4<f32> {
58
69
  return fragColor;
59
- // return fragPosition;
60
70
  }
61
71
  `;
62
72
  const simjsFrameRateCss = `@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono&family=Roboto:wght@100&display=swap');
@@ -107,6 +117,7 @@ export class Simulation {
107
117
  frameRateView;
108
118
  camera;
109
119
  pipelines;
120
+ renderInfo;
110
121
  constructor(idOrCanvasRef, camera = null, showFrameRate = false) {
111
122
  if (typeof idOrCanvasRef === 'string') {
112
123
  const ref = document.getElementById(idOrCanvasRef);
@@ -135,6 +146,7 @@ export class Simulation {
135
146
  this.setCanvasSize(width, height);
136
147
  }
137
148
  });
149
+ this.renderInfo = null;
138
150
  this.pipelines = null;
139
151
  this.frameRateView = new FrameRateView(showFrameRate);
140
152
  this.frameRateView.updateFrameRate(1);
@@ -160,6 +172,7 @@ export class Simulation {
160
172
  if (!ctx)
161
173
  throw logger.error('Context is null');
162
174
  const device = await adapter.requestDevice();
175
+ this.propagateDevice(device);
163
176
  ctx.configure({
164
177
  device,
165
178
  format: 'bgra8unorm'
@@ -175,6 +188,13 @@ export class Simulation {
175
188
  setBackground(color) {
176
189
  this.bgColor = color;
177
190
  }
191
+ propagateDevice(device) {
192
+ for (let i = 0; i < this.scene.length; i++) {
193
+ if (this.scene[i].isInstance) {
194
+ this.scene[i].setDevice(device);
195
+ }
196
+ }
197
+ }
178
198
  render(device, ctx) {
179
199
  this.assertHasCanvas();
180
200
  const canvas = this.canvasRef;
@@ -187,27 +207,60 @@ export class Simulation {
187
207
  format: presentationFormat,
188
208
  alphaMode: 'premultiplied'
189
209
  });
190
- this.pipelines = {
191
- triangleList2d: createPipeline(device, shaderModule, presentationFormat, 'vertex_main_2d', 'triangle-list'),
192
- triangleStrip2d: createPipeline(device, shaderModule, presentationFormat, 'vertex_main_2d', 'triangle-strip'),
193
- lineStrip2d: createPipeline(device, shaderModule, presentationFormat, 'vertex_main_2d', 'line-strip'),
194
- triangleList3d: createPipeline(device, shaderModule, presentationFormat, 'vertex_main_3d', 'triangle-list'),
195
- triangleStrip3d: createPipeline(device, shaderModule, presentationFormat, 'vertex_main_3d', 'triangle-strip'),
196
- lineStrip3d: createPipeline(device, shaderModule, presentationFormat, 'vertex_main_3d', 'line-strip')
197
- };
198
210
  const uniformBufferSize = 4 * 16 + 4 * 16 + 4 * 2 + 8; // 4x4 matrix + 4x4 matrix + vec2<f32> + 8 bc 144 is cool
199
211
  const uniformBuffer = device.createBuffer({
200
212
  size: uniformBufferSize,
201
213
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
202
214
  });
215
+ const instanceBuffer = device.createBuffer({
216
+ size: 16 * 10 * 4,
217
+ usage: GPUBufferUsage.STORAGE
218
+ });
219
+ const bindGroupLayout = device.createBindGroupLayout({
220
+ entries: [
221
+ {
222
+ binding: 0,
223
+ visibility: GPUShaderStage.VERTEX,
224
+ buffer: {
225
+ type: 'uniform'
226
+ }
227
+ },
228
+ {
229
+ binding: 1,
230
+ visibility: GPUShaderStage.VERTEX,
231
+ buffer: {
232
+ type: 'read-only-storage'
233
+ }
234
+ }
235
+ ]
236
+ });
237
+ this.renderInfo = {
238
+ uniformBuffer,
239
+ bindGroupLayout,
240
+ instanceBuffer
241
+ };
242
+ this.pipelines = {
243
+ triangleList2d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_2d', 'triangle-list'),
244
+ triangleStrip2d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_2d', 'triangle-strip'),
245
+ lineStrip2d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_2d', 'line-strip'),
246
+ triangleList3d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_3d', 'triangle-list'),
247
+ triangleStrip3d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_3d', 'triangle-strip'),
248
+ lineStrip3d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_3d', 'line-strip')
249
+ };
203
250
  const uniformBindGroup = device.createBindGroup({
204
- layout: this.pipelines.triangleList3d.getBindGroupLayout(0),
251
+ layout: bindGroupLayout,
205
252
  entries: [
206
253
  {
207
254
  binding: 0,
208
255
  resource: {
209
256
  buffer: uniformBuffer
210
257
  }
258
+ },
259
+ {
260
+ binding: 1,
261
+ resource: {
262
+ buffer: instanceBuffer
263
+ }
211
264
  }
212
265
  ]
213
266
  });
@@ -281,8 +334,6 @@ export class Simulation {
281
334
  device.queue.writeBuffer(uniformBuffer, 0, modelViewProjectionMatrix.buffer, modelViewProjectionMatrix.byteOffset, modelViewProjectionMatrix.byteLength);
282
335
  device.queue.writeBuffer(uniformBuffer, 4 * 16, // 4x4 matrix
283
336
  orthoMatrix.buffer, orthoMatrix.byteOffset, orthoMatrix.byteLength);
284
- device.queue.writeBuffer(uniformBuffer, 4 * 16 + 4 * 16, // 4x4 matrix + 4x4 matrix
285
- screenSize.buffer, screenSize.byteOffset, screenSize.byteLength);
286
337
  const commandEncoder = device.createCommandEncoder();
287
338
  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
288
339
  passEncoder.setPipeline(this.pipelines.triangleList3d);
@@ -294,26 +345,17 @@ export class Simulation {
294
345
  };
295
346
  requestAnimationFrame(frame);
296
347
  }
297
- getVertexCount(scene) {
298
- let total = 0;
299
- for (let i = 0; i < scene.length; i++) {
300
- if (scene[i] instanceof SceneCollection)
301
- continue;
302
- total += scene[i].getVertexCount();
303
- }
304
- return total;
305
- }
306
348
  async renderScene(device, passEncoder, scene) {
307
349
  if (this.pipelines === null)
308
350
  return;
309
- let totalVertices = this.getVertexCount(scene);
351
+ let totalVertices = getTotalVertices(scene);
310
352
  const vertexBuffer = device.createBuffer({
311
- size: totalVertices * 40,
353
+ size: totalVertices * 4 * BUF_LEN,
312
354
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
313
355
  });
314
356
  let currentOffset = 0;
315
357
  for (let i = 0; i < scene.length; i++) {
316
- if (scene[i] instanceof SceneCollection) {
358
+ if (scene[i].isCollection) {
317
359
  this.renderScene(device, passEncoder, scene[i].getScene());
318
360
  continue;
319
361
  }
@@ -321,8 +363,9 @@ export class Simulation {
321
363
  const vertexCount = buffer.length / BUF_LEN;
322
364
  device.queue.writeBuffer(vertexBuffer, currentOffset, buffer);
323
365
  vertexBuffer.unmap();
366
+ const is3d = Boolean(scene[i].is3d);
324
367
  if (scene[i].isWireframe()) {
325
- if (scene[i].is3d) {
368
+ if (is3d) {
326
369
  passEncoder.setPipeline(this.pipelines.lineStrip3d);
327
370
  }
328
371
  else {
@@ -332,7 +375,7 @@ export class Simulation {
332
375
  else {
333
376
  const type = scene[i].getGeometryType();
334
377
  if (type === 'strip') {
335
- if (scene[i].is3d) {
378
+ if (is3d) {
336
379
  passEncoder.setPipeline(this.pipelines.triangleStrip3d);
337
380
  }
338
381
  else {
@@ -340,7 +383,7 @@ export class Simulation {
340
383
  }
341
384
  }
342
385
  else if (type === 'list') {
343
- if (scene[i].is3d) {
386
+ if (is3d) {
344
387
  passEncoder.setPipeline(this.pipelines.triangleList3d);
345
388
  }
346
389
  else {
@@ -348,8 +391,33 @@ export class Simulation {
348
391
  }
349
392
  }
350
393
  }
394
+ let instances = 1;
395
+ if (scene[i].isInstance) {
396
+ instances = scene[i].getNumInstances();
397
+ const buf = scene[i].getMatrixBuffer();
398
+ if (buf && this.renderInfo) {
399
+ const uniformBindGroup = device.createBindGroup({
400
+ layout: this.renderInfo.bindGroupLayout,
401
+ entries: [
402
+ {
403
+ binding: 0,
404
+ resource: {
405
+ buffer: this.renderInfo.uniformBuffer
406
+ }
407
+ },
408
+ {
409
+ binding: 1,
410
+ resource: {
411
+ buffer: buf
412
+ }
413
+ }
414
+ ]
415
+ });
416
+ passEncoder.setBindGroup(0, uniformBindGroup);
417
+ }
418
+ }
351
419
  passEncoder.setVertexBuffer(0, vertexBuffer, currentOffset, buffer.byteLength);
352
- passEncoder.draw(vertexCount);
420
+ passEncoder.draw(vertexCount, instances, 0, 0);
353
421
  currentOffset += buffer.byteLength;
354
422
  }
355
423
  }
@@ -373,6 +441,7 @@ export class SceneCollection extends SimulationElement3d {
373
441
  geometry;
374
442
  name;
375
443
  scene;
444
+ isCollection = true;
376
445
  constructor(name) {
377
446
  super(vector3());
378
447
  this.wireframe = false;
@@ -400,7 +469,6 @@ export class SceneCollection extends SimulationElement3d {
400
469
  return this.getSceneBuffer(camera);
401
470
  }
402
471
  getTriangles(camera) {
403
- console.log('here');
404
472
  return this.getSceneBuffer(camera);
405
473
  }
406
474
  updateMatrix(camera) {
package/dist/types.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /// <reference types="dist" />
2
2
  import { CubicBezierCurve2d, SplinePoint2d } from './graphics.js';
3
3
  import { Color, Vertex } from './utils.js';
4
- export type Vector4 = Float32Array & [number, number, number, number];
5
- export type Vector3 = Float32Array & [number, number, number];
6
- export type Vector2 = Float32Array & [number, number];
7
- export type Mat4 = Float32Array & [
4
+ export type FloatArray = Float32Array | Float64Array;
5
+ export type Vector4 = FloatArray & [number, number, number, number];
6
+ export type Vector3 = FloatArray & [number, number, number];
7
+ export type Vector2 = FloatArray & [number, number];
8
+ export type Mat4 = FloatArray & [
8
9
  number,
9
10
  number,
10
11
  number,
@@ -68,3 +69,8 @@ export type PipelineGroup = {
68
69
  triangleStrip3d: GPURenderPipeline;
69
70
  lineStrip3d: GPURenderPipeline;
70
71
  };
72
+ export type RenderInfo = {
73
+ uniformBuffer: GPUBuffer;
74
+ instanceBuffer: GPUBuffer;
75
+ bindGroupLayout: GPUBindGroupLayout;
76
+ };
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="dist" />
2
2
  import { SimulationElement, SplinePoint2d } from './graphics.js';
3
- import { Mat4, Vector2, Vector3, Vector4 } from './types.js';
3
+ import { FloatArray, Mat4, Vector2, Vector3, Vector4 } from './types.js';
4
4
  export declare class Color {
5
5
  r: number;
6
6
  g: number;
@@ -76,10 +76,16 @@ export declare function linearStep(t: number): number;
76
76
  export declare function easeInOutExpo(t: number): number;
77
77
  export declare function easeInOutQuart(t: number): number;
78
78
  export declare function easeInOutQuad(t: number): number;
79
- export declare function vertexBuffer(x: number, y: number, z: number, color: Color, uv?: Vector2): number[];
79
+ declare class BufferGenerator {
80
+ private instancing;
81
+ constructor();
82
+ setInstancing(state: boolean): void;
83
+ generate(x: number, y: number, z: number, color: Color, uv?: Vector2): number[];
84
+ }
85
+ export declare const bufferGenerator: BufferGenerator;
80
86
  export declare function vector3ToPixelRatio(vec: Vector3): void;
81
87
  export declare function vector2ToPixelRatio(vec: Vector2): void;
82
- export declare function cloneBuf<T extends Float32Array>(buf: T): T;
88
+ export declare function cloneBuf<T extends FloatArray>(buf: T): T;
83
89
  export declare function vector4(x?: number, y?: number, z?: number, w?: number): Vector4;
84
90
  export declare function vector3(x?: number, y?: number, z?: number): Vector3;
85
91
  export declare function vector2(x?: number, y?: number): Vector2;
@@ -101,6 +107,7 @@ export declare function interpolateColors(colors: Color[], t: number): Color;
101
107
  export declare function waitFor(t: number): Promise<unknown>;
102
108
  export declare function matrixFromRotation(rotation: Vector3): Mat4;
103
109
  export declare function rotateMat4(mat: Mat4, rotation: Vector3): void;
104
- export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology): GPURenderPipeline;
110
+ export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, bindGroupLayout: GPUBindGroupLayout, presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology): GPURenderPipeline;
105
111
  export declare function triangulateWireFrameOrder(len: number): number[];
112
+ export declare function getTotalVertices(scene: SimulationElement[]): number;
106
113
  export {};
package/dist/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { mat4, vec2, vec3, vec4 } from 'wgpu-matrix';
2
2
  import { SimulationElement, SplinePoint2d } from './graphics.js';
3
- import { BUF_LEN, colorOffset, uvOffset, vertexSize } from './constants.js';
3
+ import { BUF_LEN, colorOffset, drawingInstancesOffset, uvOffset, vertexSize } from './constants.js';
4
4
  export class Color {
5
5
  r; // 0 - 255
6
6
  g; // 0 - 255
@@ -100,9 +100,9 @@ export class Vertex {
100
100
  }
101
101
  toBuffer(defaultColor) {
102
102
  if (this.is3d)
103
- return vertexBuffer(this.pos[0], this.pos[1], this.pos[2], this.color || defaultColor, this.uv);
103
+ return bufferGenerator.generate(this.pos[0], this.pos[1], this.pos[2], this.color || defaultColor, this.uv);
104
104
  else
105
- return vertexBuffer(this.pos[0], this.pos[1], 0, this.color || defaultColor, this.uv);
105
+ return bufferGenerator.generate(this.pos[0], this.pos[1], 0, this.color || defaultColor, this.uv);
106
106
  }
107
107
  }
108
108
  export const buildProjectionMatrix = (aspectRatio, zNear = 1, zFar = 500) => {
@@ -260,9 +260,17 @@ export function easeInOutQuart(t) {
260
260
  export function easeInOutQuad(t) {
261
261
  return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2;
262
262
  }
263
- export function vertexBuffer(x, y, z, color, uv = vector2()) {
264
- return [x, y, z, 1, ...color.toBuffer(), ...uv];
263
+ class BufferGenerator {
264
+ instancing = false;
265
+ constructor() { }
266
+ setInstancing(state) {
267
+ this.instancing = state;
268
+ }
269
+ generate(x, y, z, color, uv = vector2()) {
270
+ return [x, y, z, 1, ...color.toBuffer(), ...uv, this.instancing ? 1 : 0];
271
+ }
265
272
  }
273
+ export const bufferGenerator = new BufferGenerator();
266
274
  export function vector3ToPixelRatio(vec) {
267
275
  vec[0] *= devicePixelRatio;
268
276
  vec[1] *= devicePixelRatio;
@@ -371,9 +379,11 @@ export function rotateMat4(mat, rotation) {
371
379
  mat4.rotateY(mat, rotation[1], mat);
372
380
  mat4.rotateX(mat, rotation[0], mat);
373
381
  }
374
- export function createPipeline(device, module, presentationFormat, entryPoint, topology) {
382
+ export function createPipeline(device, module, bindGroupLayout, presentationFormat, entryPoint, topology) {
375
383
  return device.createRenderPipeline({
376
- layout: 'auto',
384
+ layout: device.createPipelineLayout({
385
+ bindGroupLayouts: [bindGroupLayout]
386
+ }),
377
387
  vertex: {
378
388
  module,
379
389
  entryPoint,
@@ -398,6 +408,12 @@ export function createPipeline(device, module, presentationFormat, entryPoint, t
398
408
  shaderLocation: 2,
399
409
  offset: uvOffset,
400
410
  format: 'float32x2'
411
+ },
412
+ {
413
+ // drawing instances
414
+ shaderLocation: 3,
415
+ offset: drawingInstancesOffset,
416
+ format: 'float32'
401
417
  }
402
418
  ]
403
419
  }
@@ -438,3 +454,12 @@ export function triangulateWireFrameOrder(len) {
438
454
  }
439
455
  return order;
440
456
  }
457
+ export function getTotalVertices(scene) {
458
+ let total = 0;
459
+ for (let i = 0; i < scene.length; i++) {
460
+ if (scene[i].isCollection)
461
+ continue;
462
+ total += scene[i].getVertexCount();
463
+ }
464
+ return total;
465
+ }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "types": "./dist/index.d.ts",
6
6
  "author": "Jackson Otto",
7
7
  "description": "A simple graphics library using WebGPU",
8
- "version": "0.2.8",
8
+ "version": "0.2.9",
9
9
  "exports": {
10
10
  ".": {
11
11
  "import": "./dist/index.js",