simulationjsv2 0.4.8 → 0.4.10

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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # TODO
2
2
 
3
- - [ ] Memoize render scene buffer
3
+ - [x] Memoize render scene buffer
4
4
  - Should only be reset to resize to a new larger object then reused on all frames
@@ -1,10 +1,10 @@
1
- import { CircleGeometryParams, CubeGeometryParams, Line2dGeometryParams, Line3dGeometryParams, Mat4, PolygonGeometryParams, Spline2dGeometryParams, SquareGeometryParams, Vector2, Vector3, VertexColorMap } from './types.js';
1
+ import { BufferExtenderInfo, CircleGeometryParams, CubeGeometryParams, EmptyParams, Line2dGeometryParams, Line3dGeometryParams, Mat4, PolygonGeometryParams, Spline2dGeometryParams, SquareGeometryParams, Vector2, Vector3, VertexColorMap } from './types.js';
2
2
  import { Color, Vertex } from './utils.js';
3
3
  import { CubicBezierCurve2d, SplinePoint2d } from './graphics.js';
4
- export declare abstract class Geometry {
4
+ export declare abstract class Geometry<T extends EmptyParams> {
5
5
  protected abstract wireframeOrder: number[];
6
6
  protected abstract triangleOrder: number[];
7
- protected abstract params: Record<string, any>;
7
+ protected abstract params: T;
8
8
  protected vertices: Vector3[];
9
9
  protected matrix: Mat4;
10
10
  protected geometryType: 'list' | 'strip';
@@ -14,11 +14,11 @@ export declare abstract class Geometry {
14
14
  abstract recompute(): void;
15
15
  getTriangleVertexCount(): number;
16
16
  getWireframeVertexCount(): number;
17
- protected bufferFromOrder(order: number[], color: Color): number[];
18
- getWireframeBuffer(color: Color): number[];
19
- getTriangleBuffer(color: Color): number[];
17
+ protected bufferFromOrder(order: number[], color: Color, bufferExtender?: BufferExtenderInfo): number[];
18
+ getWireframeBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
19
+ getTriangleBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
20
20
  }
21
- export declare class PlaneGeometry extends Geometry {
21
+ export declare class PlaneGeometry extends Geometry<EmptyParams> {
22
22
  protected params: {};
23
23
  protected wireframeOrder: number[];
24
24
  protected triangleOrder: number[];
@@ -26,9 +26,9 @@ export declare class PlaneGeometry extends Geometry {
26
26
  constructor(vertices: Vertex[]);
27
27
  recompute(): void;
28
28
  updateVertices(vertices: Vertex[]): void;
29
- getTriangleBuffer(color: Color): number[];
29
+ getTriangleBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
30
30
  }
31
- export declare class CubeGeometry extends Geometry {
31
+ export declare class CubeGeometry extends Geometry<CubeGeometryParams> {
32
32
  protected params: CubeGeometryParams;
33
33
  protected wireframeOrder: number[];
34
34
  protected triangleOrder: number[];
@@ -39,7 +39,7 @@ export declare class CubeGeometry extends Geometry {
39
39
  recompute(): void;
40
40
  updateSize(width: number, height: number, depth: number): void;
41
41
  }
42
- export declare class SquareGeometry extends Geometry {
42
+ export declare class SquareGeometry extends Geometry<SquareGeometryParams> {
43
43
  protected wireframeOrder: number[];
44
44
  protected triangleOrder: number[];
45
45
  protected params: SquareGeometryParams;
@@ -48,16 +48,16 @@ export declare class SquareGeometry extends Geometry {
48
48
  setWidth(width: number): void;
49
49
  setHeight(height: number): void;
50
50
  recompute(): void;
51
- getTriangleBuffer(color: Color): number[];
51
+ getTriangleBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
52
52
  }
53
- export declare class BlankGeometry extends Geometry {
53
+ export declare class BlankGeometry extends Geometry<EmptyParams> {
54
54
  protected wireframeOrder: never[];
55
55
  protected triangleOrder: never[];
56
56
  protected params: {};
57
57
  constructor();
58
58
  recompute(): void;
59
59
  }
60
- export declare class CircleGeometry extends Geometry {
60
+ export declare class CircleGeometry extends Geometry<CircleGeometryParams> {
61
61
  protected wireframeOrder: number[];
62
62
  protected triangleOrder: number[];
63
63
  protected params: CircleGeometryParams;
@@ -67,7 +67,7 @@ export declare class CircleGeometry extends Geometry {
67
67
  private updateTriangleOrder;
68
68
  recompute(): void;
69
69
  }
70
- export declare class Spline2dGeometry extends Geometry {
70
+ export declare class Spline2dGeometry extends Geometry<Spline2dGeometryParams> {
71
71
  protected wireframeOrder: number[];
72
72
  protected triangleOrder: number[];
73
73
  protected params: Spline2dGeometryParams;
@@ -83,24 +83,24 @@ export declare class Spline2dGeometry extends Geometry {
83
83
  private computeCurves;
84
84
  private updateWireframeOrder;
85
85
  recompute(): void;
86
- getWireframeBuffer(color: Color): number[];
87
- getTriangleBuffer(_: Color): number[];
86
+ getWireframeBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
87
+ getTriangleBuffer(_: Color, bufferExtender?: BufferExtenderInfo): number[];
88
88
  }
89
- export declare class Line2dGeometry extends Geometry {
89
+ export declare class Line2dGeometry extends Geometry<Line2dGeometryParams> {
90
90
  protected wireframeOrder: number[];
91
91
  protected triangleOrder: number[];
92
92
  protected params: Line2dGeometryParams;
93
93
  constructor(pos: Vector2, to: Vector2, thickness: number);
94
94
  recompute(): void;
95
95
  }
96
- export declare class Line3dGeometry extends Geometry {
96
+ export declare class Line3dGeometry extends Geometry<Line3dGeometryParams> {
97
97
  protected wireframeOrder: number[];
98
98
  protected triangleOrder: number[];
99
99
  protected params: Line3dGeometryParams;
100
100
  constructor(pos: Vector3, to: Vector3, thickness: number);
101
101
  recompute(): void;
102
102
  }
103
- export declare class PolygonGeometry extends Geometry {
103
+ export declare class PolygonGeometry extends Geometry<PolygonGeometryParams> {
104
104
  protected wireframeOrder: number[];
105
105
  protected triangleOrder: number[];
106
106
  protected params: PolygonGeometryParams;
package/dist/geometry.js CHANGED
@@ -24,20 +24,20 @@ export class Geometry {
24
24
  getWireframeVertexCount() {
25
25
  return this.wireframeOrder.length;
26
26
  }
27
- bufferFromOrder(order, color) {
27
+ bufferFromOrder(order, color, bufferExtender) {
28
28
  return order
29
29
  .map((vertexIndex) => {
30
30
  const pos = cloneBuf(this.vertices[vertexIndex]);
31
31
  vec3.transformMat4(pos, this.matrix, pos);
32
- return bufferGenerator.generate(pos[0], pos[1], pos[2], color);
32
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], color, vector2(), bufferExtender);
33
33
  })
34
34
  .flat();
35
35
  }
36
- getWireframeBuffer(color) {
37
- return this.bufferFromOrder(this.wireframeOrder, color);
36
+ getWireframeBuffer(color, bufferExtender) {
37
+ return this.bufferFromOrder(this.wireframeOrder, color, bufferExtender);
38
38
  }
39
- getTriangleBuffer(color) {
40
- return this.bufferFromOrder(this.triangleOrder, color);
39
+ getTriangleBuffer(color, bufferExtender) {
40
+ return this.bufferFromOrder(this.triangleOrder, color, bufferExtender);
41
41
  }
42
42
  }
43
43
  export class PlaneGeometry extends Geometry {
@@ -61,13 +61,13 @@ export class PlaneGeometry extends Geometry {
61
61
  .fill(0)
62
62
  .map((_, index) => index)).flat();
63
63
  }
64
- getTriangleBuffer(color) {
64
+ getTriangleBuffer(color, bufferExtender) {
65
65
  return this.triangleOrder
66
66
  .map((index) => {
67
67
  const vertex = this.rawVertices[index];
68
68
  const pos = cloneBuf(vertex.getPos());
69
69
  vec3.transformMat4(pos, this.matrix, pos);
70
- return bufferGenerator.generate(pos[0], pos[1], pos[2], vertex.getColor() || color);
70
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], vertex.getColor() || color, vector2(), bufferExtender);
71
71
  })
72
72
  .flat();
73
73
  }
@@ -155,12 +155,12 @@ export class SquareGeometry extends Geometry {
155
155
  vector3(-this.params.width * centerOffset[0], -this.params.height * (1 - centerOffset[1]))
156
156
  ];
157
157
  }
158
- getTriangleBuffer(color) {
158
+ getTriangleBuffer(color, bufferExtender) {
159
159
  return this.triangleOrder
160
160
  .map((vertexIndex) => {
161
161
  const pos = cloneBuf(this.vertices[vertexIndex]);
162
162
  vec3.transformMat4(pos, this.matrix, pos);
163
- return bufferGenerator.generate(pos[0], pos[1], pos[2], this.params.colorMap[vertexIndex] || color);
163
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], this.params.colorMap[vertexIndex] || color, vector2(), bufferExtender);
164
164
  })
165
165
  .flat();
166
166
  }
@@ -349,21 +349,21 @@ export class Spline2dGeometry extends Geometry {
349
349
  .map((_, index) => index)).flat();
350
350
  this.updateWireframeOrder();
351
351
  }
352
- getWireframeBuffer(color) {
352
+ getWireframeBuffer(color, bufferExtender) {
353
353
  return this.wireframeOrder
354
354
  .map((vertexIndex) => {
355
355
  const vertex = cloneBuf(this.vertices[vertexIndex]);
356
356
  vec3.transformMat4(vertex, this.matrix, vertex);
357
- return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], color);
357
+ return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], color, vector2(), bufferExtender);
358
358
  })
359
359
  .flat();
360
360
  }
361
- getTriangleBuffer(_) {
361
+ getTriangleBuffer(_, bufferExtender) {
362
362
  return this.triangleOrder
363
363
  .map((vertexIndex) => {
364
364
  const vertex = cloneBuf(this.vertices[vertexIndex]);
365
365
  vec3.transformMat4(vertex, this.matrix, vertex);
366
- return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], this.params.vertexColors[vertexIndex]);
366
+ return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], this.params.vertexColors[vertexIndex], vector2(), bufferExtender);
367
367
  })
368
368
  .flat();
369
369
  }
@@ -1,12 +1,12 @@
1
1
  /// <reference types="@webgpu/types" />
2
2
  import { Camera } from './simulation.js';
3
- import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation, Mat4 } from './types.js';
3
+ import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation, Mat4, AnySimulationElement, BufferExtenderInfo } from './types.js';
4
4
  import { Vertex, Color } from './utils.js';
5
5
  import { BlankGeometry, CircleGeometry, CubeGeometry, Geometry, Line2dGeometry, Line3dGeometry, PlaneGeometry, PolygonGeometry, Spline2dGeometry, SquareGeometry } from './geometry.js';
6
6
  import { VertexCache } from './internalUtils.js';
7
7
  export declare abstract class SimulationElement<T extends Vector2 | Vector3 = Vector3> {
8
8
  protected abstract pos: T;
9
- protected abstract geometry: Geometry;
9
+ protected abstract geometry: Geometry<any>;
10
10
  protected color: Color;
11
11
  protected wireframe: boolean;
12
12
  protected vertexCache: VertexCache;
@@ -30,7 +30,7 @@ export declare abstract class SimulationElement<T extends Vector2 | Vector3 = Ve
30
30
  protected abstract updateMatrix(camera: Camera): void;
31
31
  getVertexCount(): number;
32
32
  protected defaultUpdateMatrix(camera: Camera): void;
33
- getBuffer(camera: Camera): number[];
33
+ getBuffer(camera: Camera, bufferExtender?: BufferExtenderInfo): number[];
34
34
  }
35
35
  export declare abstract class SimulationElement3d extends SimulationElement {
36
36
  protected pos: Vector3;
@@ -177,7 +177,7 @@ export declare class Spline2d extends SimulationElement2d {
177
177
  interpolate(t: number): Vector2;
178
178
  protected updateMatrix(camera: Camera): void;
179
179
  }
180
- export declare class Instance<T extends SimulationElement2d | SimulationElement3d> extends SimulationElement3d {
180
+ export declare class Instance<T extends AnySimulationElement> extends SimulationElement3d {
181
181
  protected geometry: BlankGeometry;
182
182
  private obj;
183
183
  private instanceMatrix;
package/dist/graphics.js CHANGED
@@ -73,19 +73,21 @@ export class SimulationElement {
73
73
  }
74
74
  this.geometry.updateMatrix(matrix);
75
75
  }
76
- getBuffer(camera) {
77
- if (this.vertexCache.shouldUpdate() || camera.hasUpdated()) {
76
+ getBuffer(camera, bufferExtender) {
77
+ const shouldEvalExtender = bufferExtender?.shouldEvaluate?.();
78
+ const reEvalExtender = shouldEvalExtender === undefined ? true : shouldEvalExtender;
79
+ if (this.vertexCache.shouldUpdate() || camera.hasUpdated() || reEvalExtender) {
78
80
  this.updateMatrix(camera);
79
81
  this.geometry.recompute();
80
82
  if (this.isInstanced) {
81
83
  bufferGenerator.setInstancing(true);
82
84
  }
83
- let resBuffer = [];
85
+ let resBuffer;
84
86
  if (this.isWireframe()) {
85
- resBuffer = this.geometry.getWireframeBuffer(this.color);
87
+ resBuffer = this.geometry.getWireframeBuffer(this.color, bufferExtender);
86
88
  }
87
89
  else {
88
- resBuffer = this.geometry.getTriangleBuffer(this.color);
90
+ resBuffer = this.geometry.getTriangleBuffer(this.color, bufferExtender);
89
91
  }
90
92
  bufferGenerator.setInstancing(false);
91
93
  this.vertexCache.setCache(resBuffer);
@@ -650,9 +652,9 @@ export class BezierCurve2d {
650
652
  interpolateSlope(t) {
651
653
  t = Math.max(0, Math.min(1, t));
652
654
  let vectors = this.points;
653
- let slopeVector = vector2(1);
655
+ const slopeVector = vector2(1);
654
656
  while (vectors.length > 2) {
655
- let newVectors = [];
657
+ const newVectors = [];
656
658
  for (let i = 1; i < vectors.length - 1; i++) {
657
659
  const from = vector2();
658
660
  const to = vector2();
@@ -670,7 +672,7 @@ export class BezierCurve2d {
670
672
  vectors = newVectors;
671
673
  }
672
674
  vec2.sub(vectors[1], vectors[0], slopeVector);
673
- let resVector = vector2();
675
+ const resVector = vector2();
674
676
  vec2.scale(slopeVector, t, resVector);
675
677
  vec2.add(resVector, vectors[0], resVector);
676
678
  return [resVector, slopeVector];
@@ -987,6 +989,7 @@ export class Instance extends SimulationElement3d {
987
989
  getGeometryType() {
988
990
  return this.obj.getGeometryType();
989
991
  }
992
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
990
993
  updateMatrix(_) { }
991
994
  getBuffer(camera) {
992
995
  if (this.needsRemap)
@@ -1,7 +1,6 @@
1
1
  /// <reference types="@webgpu/types" />
2
- import { Mat4, Vector2, Vector3 } from './types.js';
2
+ import { AnySimulationElement, BufferExtenderInfo, Mat4, Vector2, Vector3, VertexParamInfo } from './types.js';
3
3
  import { Color } from './utils.js';
4
- import { SimulationElement } from './graphics.js';
5
4
  export declare class VertexCache {
6
5
  private vertices;
7
6
  private hasUpdated;
@@ -17,15 +16,15 @@ export declare const getTransformationMatrix: (pos: Vector3, rotation: Vector3,
17
16
  export declare const getOrthoMatrix: (screenSize: [number, number]) => Float32Array;
18
17
  export declare const buildDepthTexture: (device: GPUDevice, width: number, height: number) => GPUTexture;
19
18
  export declare const buildMultisampleTexture: (device: GPUDevice, ctx: GPUCanvasContext, width: number, height: number) => GPUTexture;
20
- export declare const addObject: (scene: SimSceneObjInfo[], el: SimulationElement<any>, device: GPUDevice | null, id?: string) => void;
21
- export declare const removeObject: (scene: SimSceneObjInfo[], el: SimulationElement<any>) => void;
19
+ export declare const addObject: (scene: SimSceneObjInfo[], el: AnySimulationElement, device: GPUDevice | null, id?: string) => void;
20
+ export declare const removeObject: (scene: SimSceneObjInfo[], el: AnySimulationElement) => void;
22
21
  export declare const removeObjectId: (scene: SimSceneObjInfo[], id: string) => void;
23
22
  export declare class SimSceneObjInfo {
24
23
  private obj;
25
24
  private id;
26
25
  private lifetime;
27
26
  private currentLife;
28
- constructor(obj: SimulationElement<any>, id?: string);
27
+ constructor(obj: AnySimulationElement, id?: string);
29
28
  /**
30
29
  * @param lifetime - ms
31
30
  */
@@ -36,7 +35,7 @@ export declare class SimSceneObjInfo {
36
35
  * @param amount - ms
37
36
  */
38
37
  traverseLife(amount: number): void;
39
- getObj(): SimulationElement<Vector3 | Vector2>;
38
+ getObj(): AnySimulationElement;
40
39
  getId(): string | null;
41
40
  }
42
41
  declare class Logger {
@@ -53,14 +52,14 @@ declare class BufferGenerator {
53
52
  private instancing;
54
53
  constructor();
55
54
  setInstancing(state: boolean): void;
56
- generate(x: number, y: number, z: number, color: Color, uv?: Vector2): number[];
55
+ generate(x: number, y: number, z: number, color: Color, uv?: Vector2, bufferExtender?: BufferExtenderInfo): number[];
57
56
  }
58
57
  export declare const bufferGenerator: BufferGenerator;
59
58
  export declare function vector3ToPixelRatio(vec: Vector3): void;
60
59
  export declare function vector2ToPixelRatio(vec: Vector2): void;
61
60
  export declare function matrixFromRotation(rotation: Vector3): Mat4;
62
61
  export declare function rotateMat4(mat: Mat4, rotation: Vector3): void;
63
- export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, bindGroupLayout: GPUBindGroupLayout, presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology): GPURenderPipeline;
62
+ export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, bindGroupLayout: GPUBindGroupLayout, presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology, vertexParams?: VertexParamInfo[]): GPURenderPipeline;
64
63
  export declare function triangulateWireFrameOrder(len: number): number[];
65
64
  export declare function getTotalVertices(scene: SimSceneObjInfo[]): number;
66
65
  export {};
@@ -182,7 +182,15 @@ class BufferGenerator {
182
182
  setInstancing(state) {
183
183
  this.instancing = state;
184
184
  }
185
- generate(x, y, z, color, uv = vector2()) {
185
+ generate(x, y, z, color, uv = vector2(), bufferExtender) {
186
+ if (bufferExtender) {
187
+ const buf = bufferExtender.extender(x, y, z, color);
188
+ if (buf.length !== bufferExtender.size) {
189
+ logger.log_error(`Vertex size for shader group does not match buffer extension size (${buf.length} to expected ${bufferExtender.size})`);
190
+ return [];
191
+ }
192
+ return buf;
193
+ }
186
194
  return [x, y, z, 1, ...color.toBuffer(), ...uv, this.instancing ? 1 : 0];
187
195
  }
188
196
  }
@@ -197,7 +205,7 @@ export function vector2ToPixelRatio(vec) {
197
205
  vec[1] *= devicePixelRatio;
198
206
  }
199
207
  export function matrixFromRotation(rotation) {
200
- let rotMatrix = mat4.identity();
208
+ const rotMatrix = mat4.identity();
201
209
  mat4.rotateZ(rotMatrix, rotation[2], rotMatrix);
202
210
  mat4.rotateY(rotMatrix, rotation[1], rotMatrix);
203
211
  mat4.rotateX(rotMatrix, rotation[0], rotMatrix);
@@ -208,7 +216,47 @@ export function rotateMat4(mat, rotation) {
208
216
  mat4.rotateY(mat, rotation[1], mat);
209
217
  mat4.rotateX(mat, rotation[0], mat);
210
218
  }
211
- export function createPipeline(device, module, bindGroupLayout, presentationFormat, entryPoint, topology) {
219
+ export function createPipeline(device, module, bindGroupLayout, presentationFormat, entryPoint, topology, vertexParams) {
220
+ let params = [
221
+ {
222
+ // position
223
+ shaderLocation: 0,
224
+ offset: 0,
225
+ format: 'float32x4'
226
+ },
227
+ {
228
+ // color
229
+ shaderLocation: 1,
230
+ offset: colorOffset,
231
+ format: 'float32x4'
232
+ },
233
+ {
234
+ // size
235
+ shaderLocation: 2,
236
+ offset: uvOffset,
237
+ format: 'float32x2'
238
+ },
239
+ {
240
+ // drawing instances
241
+ shaderLocation: 3,
242
+ offset: drawingInstancesOffset,
243
+ format: 'float32'
244
+ }
245
+ ];
246
+ let stride = vertexSize;
247
+ if (vertexParams) {
248
+ params = [];
249
+ let offset = 0;
250
+ for (let i = 0; i < vertexParams.length; i++) {
251
+ params.push({
252
+ shaderLocation: i,
253
+ offset,
254
+ format: vertexParams[i].format
255
+ });
256
+ offset += vertexParams[i].size;
257
+ }
258
+ stride = offset;
259
+ }
212
260
  return device.createRenderPipeline({
213
261
  layout: device.createPipelineLayout({
214
262
  bindGroupLayouts: [bindGroupLayout]
@@ -218,33 +266,8 @@ export function createPipeline(device, module, bindGroupLayout, presentationForm
218
266
  entryPoint,
219
267
  buffers: [
220
268
  {
221
- arrayStride: vertexSize,
222
- attributes: [
223
- {
224
- // position
225
- shaderLocation: 0,
226
- offset: 0,
227
- format: 'float32x4'
228
- },
229
- {
230
- // color
231
- shaderLocation: 1,
232
- offset: colorOffset,
233
- format: 'float32x4'
234
- },
235
- {
236
- // size
237
- shaderLocation: 2,
238
- offset: uvOffset,
239
- format: 'float32x2'
240
- },
241
- {
242
- // drawing instances
243
- shaderLocation: 3,
244
- offset: drawingInstancesOffset,
245
- format: 'float32'
246
- }
247
- ]
269
+ arrayStride: stride,
270
+ attributes: params
248
271
  }
249
272
  ]
250
273
  },
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@webgpu/types" />
2
- import { SimulationElement, SimulationElement3d } from './graphics.js';
3
- import type { Vector2, Vector3, LerpFunc } from './types.js';
2
+ import { SimulationElement3d } from './graphics.js';
3
+ import type { Vector2, Vector3, LerpFunc, AnySimulationElement, BufferExtenderInfo, VertexParamInfo } from './types.js';
4
4
  import { Color } from './utils.js';
5
5
  import { BlankGeometry } from './geometry.js';
6
6
  import { SimSceneObjInfo } from './internalUtils.js';
@@ -17,20 +17,20 @@ export declare class Simulation {
17
17
  private pipelines;
18
18
  private renderInfo;
19
19
  constructor(idOrCanvasRef: string | HTMLCanvasElement, camera?: Camera | null, showFrameRate?: boolean);
20
- add(el: SimulationElement<any>, id?: string): void;
21
- remove(el: SimulationElement<any>): void;
20
+ add(el: AnySimulationElement, id?: string): void;
21
+ remove(el: AnySimulationElement): void;
22
22
  removeId(id: string): void;
23
23
  /**
24
24
  * @param lifetime - ms
25
25
  */
26
- setLifetime(el: SimulationElement<any>, lifetime: number): void;
26
+ setLifetime(el: AnySimulationElement, lifetime: number): void;
27
27
  setCanvasSize(width: number, height: number): void;
28
28
  start(): void;
29
29
  private propagateDevice;
30
30
  stop(): void;
31
31
  setBackground(color: Color): void;
32
32
  getScene(): SimSceneObjInfo[];
33
- getSceneObjects(): SimulationElement<Vector3 | Vector2>[];
33
+ getSceneObjects(): AnySimulationElement[];
34
34
  private render;
35
35
  private renderScene;
36
36
  fitElement(): void;
@@ -39,24 +39,24 @@ export declare class SceneCollection extends SimulationElement3d {
39
39
  protected geometry: BlankGeometry;
40
40
  private name;
41
41
  private scene;
42
- private device;
43
- constructor(name: string);
42
+ protected device: GPUDevice | null;
43
+ constructor(name?: string);
44
44
  setWireframe(_: boolean): void;
45
- getName(): string;
45
+ getName(): string | null;
46
46
  getScene(): SimSceneObjInfo[];
47
47
  setDevice(device: GPUDevice): void;
48
- private propagateDevice;
48
+ protected propagateDevice(device: GPUDevice): void;
49
49
  getVertexCount(): number;
50
- getSceneObjects(): SimulationElement<Vector3 | Vector2>[];
51
- setSceneObjects(newScene: SimulationElement<any>[]): void;
50
+ getSceneObjects(): AnySimulationElement[];
51
+ setSceneObjects(newScene: AnySimulationElement[]): void;
52
52
  setScene(newScene: SimSceneObjInfo[]): void;
53
- add(el: SimulationElement<any>, id?: string): void;
54
- remove(el: SimulationElement<any>): void;
53
+ add(el: AnySimulationElement, id?: string): void;
54
+ remove(el: AnySimulationElement): void;
55
55
  removeId(id: string): void;
56
56
  /**
57
57
  * @param lifetime - ms
58
58
  */
59
- setLifetime(el: SimulationElement<any>, lifetime: number): void;
59
+ setLifetime(el: AnySimulationElement, lifetime: number): void;
60
60
  empty(): void;
61
61
  getSceneBuffer(camera: Camera): number[];
62
62
  getWireframe(camera: Camera): number[];
@@ -82,3 +82,17 @@ export declare class Camera {
82
82
  getPos(): Vector3;
83
83
  getAspectRatio(): number;
84
84
  }
85
+ export declare class ShaderGroup extends SceneCollection {
86
+ protected geometry: BlankGeometry;
87
+ private code;
88
+ private module;
89
+ private pipeline;
90
+ private topology;
91
+ private bufferExtender;
92
+ private vertexParams;
93
+ constructor(shaderCode: string, topology: GPUPrimitiveTopology | undefined, vertexParams: VertexParamInfo[], bufferExtender: BufferExtenderInfo);
94
+ protected propagateDevice(device: GPUDevice): void;
95
+ getPipeline(): GPURenderPipeline | null;
96
+ protected updateMatrix(camera: Camera): void;
97
+ getBufferExtender(): BufferExtenderInfo;
98
+ }
@@ -31,9 +31,15 @@ fn vertex_main_3d(
31
31
  ) -> VertexOutput {
32
32
  var output : VertexOutput;
33
33
 
34
- output.Position = uniforms.modelViewProjectionMatrix * position;
34
+ if (drawingInstance == 1) {
35
+ let transformedPos = instanceMatrices[instanceIdx] * position;
36
+ output.Position = uniforms.modelViewProjectionMatrix * transformedPos;
37
+ } else {
38
+ output.Position = uniforms.modelViewProjectionMatrix * position;
39
+ }
40
+
35
41
  output.fragUV = uv;
36
- output.fragPosition = position;
42
+ output.fragPosition = output.Position;
37
43
  output.fragColor = color;
38
44
  return output;
39
45
  }
@@ -56,7 +62,7 @@ fn vertex_main_2d(
56
62
  }
57
63
 
58
64
  output.fragUV = uv;
59
- output.fragPosition = position;
65
+ output.fragPosition = output.Position;
60
66
  output.fragColor = color;
61
67
  return output;
62
68
  }
@@ -70,9 +76,7 @@ fn fragment_main(
70
76
  return fragColor;
71
77
  }
72
78
  `;
73
- const simjsFrameRateCss = `@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono&family=Roboto:wght@100&display=swap');
74
-
75
- .simjs-frame-rate {
79
+ const simjsFrameRateCss = `.simjs-frame-rate {
76
80
  position: absolute;
77
81
  top: 0;
78
82
  left: 0;
@@ -80,13 +84,14 @@ const simjsFrameRateCss = `@import url('https://fonts.googleapis.com/css2?family
80
84
  color: white;
81
85
  padding: 8px 12px;
82
86
  z-index: 1000;
83
- font-family: Roboto Mono;
87
+ font-family: monospace;
84
88
  font-size: 16px;
85
89
  }`;
86
90
  class FrameRateView {
87
91
  el;
88
92
  fpsBuffer = [];
89
93
  maxFpsBufferLength = 8;
94
+ prevAvg = 0;
90
95
  constructor(show) {
91
96
  this.el = document.createElement('div');
92
97
  this.el.classList.add('simjs-frame-rate');
@@ -106,7 +111,10 @@ class FrameRateView {
106
111
  this.fpsBuffer.push(num);
107
112
  }
108
113
  const fps = Math.round(this.fpsBuffer.reduce((acc, curr) => acc + curr, 0) / this.fpsBuffer.length);
109
- this.el.innerHTML = `${fps} FPS`;
114
+ if (fps !== this.prevAvg) {
115
+ this.el.innerHTML = `${fps} FPS`;
116
+ this.prevAvg = fps;
117
+ }
110
118
  }
111
119
  }
112
120
  export class Simulation {
@@ -299,6 +307,7 @@ export class Simulation {
299
307
  ]
300
308
  });
301
309
  const colorAttachment = {
310
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
302
311
  // @ts-ignore
303
312
  view: undefined, // Assigned later
304
313
  clearValue: this.bgColor.toObject(),
@@ -361,8 +370,10 @@ export class Simulation {
361
370
  depthTexture = buildDepthTexture(device, screenSize[0], screenSize[1]);
362
371
  renderPassDescriptor.depthStencilAttachment.view = depthTexture.createView();
363
372
  }
373
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
364
374
  // @ts-ignore
365
375
  renderPassDescriptor.colorAttachments[0].view = multisampleTexture.createView();
376
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
366
377
  // @ts-ignore
367
378
  renderPassDescriptor.colorAttachments[0].resolveTarget = ctx.getCurrentTexture().createView();
368
379
  if (this.camera.hasUpdated()) {
@@ -391,11 +402,11 @@ export class Simulation {
391
402
  };
392
403
  requestAnimationFrame(frame);
393
404
  }
394
- renderScene(device, passEncoder, vertexBuffer, scene, startOffset, diff) {
405
+ renderScene(device, passEncoder, vertexBuffer, scene, startOffset, diff, shaderInfo) {
395
406
  if (this.pipelines === null)
396
407
  return 0;
397
408
  let currentOffset = startOffset;
398
- let toRemove = [];
409
+ const toRemove = [];
399
410
  for (let i = 0; i < scene.length; i++) {
400
411
  const lifetime = scene[i].getLifetime();
401
412
  if (lifetime !== null) {
@@ -408,15 +419,26 @@ export class Simulation {
408
419
  }
409
420
  const obj = scene[i].getObj();
410
421
  if (obj instanceof SceneCollection) {
411
- currentOffset += this.renderScene(device, passEncoder, vertexBuffer, obj.getScene(), currentOffset, diff);
422
+ let shaderInfo = undefined;
423
+ if (obj instanceof ShaderGroup) {
424
+ const pipeline = obj.getPipeline();
425
+ if (pipeline !== null) {
426
+ shaderInfo = { pipeline, bufferExtender: obj.getBufferExtender() };
427
+ }
428
+ }
429
+ currentOffset += this.renderScene(device, passEncoder, vertexBuffer, obj.getScene(), currentOffset, diff, shaderInfo || undefined);
412
430
  continue;
413
431
  }
414
- const buffer = new Float32Array(obj.getBuffer(this.camera));
415
- const vertexCount = buffer.length / BUF_LEN;
432
+ const buffer = new Float32Array(obj.getBuffer(this.camera, shaderInfo?.bufferExtender));
433
+ const bufLen = shaderInfo?.bufferExtender?.size || BUF_LEN;
434
+ const vertexCount = buffer.length / bufLen;
416
435
  device.queue.writeBuffer(vertexBuffer, currentOffset, buffer);
417
436
  vertexBuffer.unmap();
418
437
  const is3d = Boolean(obj.is3d);
419
- if (obj.isWireframe()) {
438
+ if (shaderInfo) {
439
+ passEncoder.setPipeline(shaderInfo.pipeline);
440
+ }
441
+ else if (obj.isWireframe()) {
420
442
  if (is3d) {
421
443
  passEncoder.setPipeline(this.pipelines.lineStrip3d);
422
444
  }
@@ -497,10 +519,11 @@ export class SceneCollection extends SimulationElement3d {
497
519
  constructor(name) {
498
520
  super(vector3());
499
521
  this.wireframe = false;
500
- this.name = name;
522
+ this.name = name || null;
501
523
  this.scene = [];
502
524
  this.geometry = new BlankGeometry();
503
525
  }
526
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
504
527
  setWireframe(_) { }
505
528
  getName() {
506
529
  return this.name;
@@ -560,9 +583,11 @@ export class SceneCollection extends SimulationElement3d {
560
583
  getSceneBuffer(camera) {
561
584
  return this.scene.map((item) => item.getObj().getBuffer(camera)).flat();
562
585
  }
586
+ // TODO - improve
563
587
  getWireframe(camera) {
564
588
  return this.getSceneBuffer(camera);
565
589
  }
590
+ // TODO - improve
566
591
  getTriangles(camera) {
567
592
  return this.getSceneBuffer(camera);
568
593
  }
@@ -657,3 +682,65 @@ export class Camera {
657
682
  return this.aspectRatio;
658
683
  }
659
684
  }
685
+ export class ShaderGroup extends SceneCollection {
686
+ geometry;
687
+ code;
688
+ module;
689
+ pipeline;
690
+ topology;
691
+ bufferExtender;
692
+ vertexParams;
693
+ constructor(shaderCode, topology = 'triangle-list', vertexParams, bufferExtender) {
694
+ super();
695
+ const defaultCode = `
696
+ struct Uniforms {
697
+ modelViewProjectionMatrix : mat4x4<f32>,
698
+ orthoProjectionMatrix : mat4x4<f32>
699
+ }
700
+
701
+ @group(0) @binding(0) var<uniform> uniforms : Uniforms;
702
+
703
+ @group(0) @binding(1) var<storage, read> instanceMatrices : array<mat4x4f>;
704
+ `;
705
+ this.geometry = new BlankGeometry();
706
+ this.code = defaultCode + shaderCode;
707
+ this.module = null;
708
+ this.pipeline = null;
709
+ this.topology = topology;
710
+ this.bufferExtender = bufferExtender;
711
+ this.vertexParams = vertexParams;
712
+ }
713
+ propagateDevice(device) {
714
+ super.propagateDevice(device);
715
+ this.module = device.createShaderModule({ code: this.code });
716
+ const bindGroupLayout = device.createBindGroupLayout({
717
+ entries: [
718
+ {
719
+ binding: 0,
720
+ visibility: GPUShaderStage.VERTEX,
721
+ buffer: {
722
+ type: 'uniform'
723
+ }
724
+ },
725
+ {
726
+ binding: 1,
727
+ visibility: GPUShaderStage.VERTEX,
728
+ buffer: {
729
+ type: 'read-only-storage'
730
+ }
731
+ }
732
+ ]
733
+ });
734
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
735
+ this.pipeline = createPipeline(device, this.module, bindGroupLayout, presentationFormat, 'vertex_main_2d', this.topology, this.vertexParams);
736
+ }
737
+ getPipeline() {
738
+ return this.pipeline;
739
+ }
740
+ updateMatrix(camera) {
741
+ this.defaultUpdateMatrix(camera);
742
+ }
743
+ getBufferExtender() {
744
+ return this.bufferExtender;
745
+ }
746
+ }
package/dist/types.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  /// <reference types="@webgpu/types" />
2
- import { CubicBezierCurve2d, SplinePoint2d } from './graphics.js';
2
+ import { CubicBezierCurve2d, SimulationElement2d, SimulationElement3d, SplinePoint2d } from './graphics.js';
3
3
  import { Color, Vertex } from './utils.js';
4
- export type Shift<T extends any[]> = ((...args: T) => any) extends (arg1: any, ...rest: infer R) => any ? R : never;
5
4
  export type FloatArray = Float32Array | Float64Array;
6
5
  export type Vector4 = FloatArray & [number, number, number, number];
7
6
  export type Vector3 = FloatArray & [number, number, number];
@@ -27,6 +26,8 @@ export type Mat4 = FloatArray & [
27
26
  export type LerpFunc = (n: number) => number;
28
27
  export type VertexColorMap = Record<number, Color>;
29
28
  export type ElementRotation<T extends Vector2 | Vector3> = T extends Vector2 ? number : T;
29
+ export type AnySimulationElement = SimulationElement2d | SimulationElement3d;
30
+ export type EmptyParams = object;
30
31
  export type CubeGeometryParams = {
31
32
  width: number;
32
33
  height: number;
@@ -77,3 +78,20 @@ export type RenderInfo = {
77
78
  bindGroupLayout: GPUBindGroupLayout;
78
79
  vertexBuffer: GPUBuffer | null;
79
80
  };
81
+ export type BindingInfo = {
82
+ visibility: GPUBindGroupLayoutEntry['visibility'];
83
+ buffer: GPUBindGroupLayoutEntry['buffer'];
84
+ };
85
+ export type BufferExtenderInfo = {
86
+ size: number;
87
+ extender: (x: number, y: number, z: number, color: Color) => number[];
88
+ shouldEvaluate?: () => boolean;
89
+ };
90
+ export type ShaderInfo = {
91
+ pipeline: GPURenderPipeline;
92
+ bufferExtender: BufferExtenderInfo;
93
+ };
94
+ export type VertexParamInfo = {
95
+ format: GPUVertexFormat;
96
+ size: number;
97
+ };
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { SimulationElement, SplinePoint2d } from './graphics.js';
2
- import { FloatArray, Mat4, Shift, Vector2, Vector3, Vector4 } from './types.js';
1
+ import { SplinePoint2d } from './graphics.js';
2
+ import { AnySimulationElement, FloatArray, Mat4, Vector2, Vector3, Vector4 } from './types.js';
3
3
  import { SimSceneObjInfo } from './internalUtils.js';
4
4
  export declare class Color {
5
5
  r: number;
@@ -44,7 +44,7 @@ export declare class Vertex {
44
44
  * @returns {Promise<void>}
45
45
  */
46
46
  export declare function transitionValues(callback1: (deltaT: number, t: number) => void, callback2: () => void, transitionLength: number, func?: (n: number) => number): Promise<void>;
47
- export declare function frameLoop<T extends (...args: any[]) => any>(cb: T): (...params: Shift<Parameters<T>>) => void;
47
+ export declare function frameLoop<T extends (dt: number, ...args: any[]) => any>(cb: T): (...params: Parameters<T>) => void;
48
48
  export declare function lerp(a: number, b: number, t: number): number;
49
49
  export declare function smoothStep(t: number): number;
50
50
  export declare function linearStep(t: number): number;
@@ -78,6 +78,6 @@ export declare function continuousSplinePoint2d(end: Vertex, control: Vector2, d
78
78
  export declare function waitFor(t: number): Promise<unknown>;
79
79
  export declare function distance2d(vector1: Vector2, vector2: Vector2): number;
80
80
  export declare function distance3d(vector1: Vector3, vector2: Vector3): number;
81
- export declare function toSceneObjInfo(el: SimulationElement<any>, id?: string): SimSceneObjInfo;
82
- export declare function toSceneObjInfoMany(el: SimulationElement<any>[], id?: (string | undefined)[]): SimSceneObjInfo[];
81
+ export declare function toSceneObjInfo(el: AnySimulationElement, id?: string): SimSceneObjInfo;
82
+ export declare function toSceneObjInfoMany(el: AnySimulationElement[], id?: (string | undefined)[]): SimSceneObjInfo[];
83
83
  export declare function interpolateColors(colors: Color[], t: number): Color;
package/dist/utils.js CHANGED
@@ -121,6 +121,7 @@ export function transitionValues(callback1, callback2, transitionLength, func) {
121
121
  }
122
122
  });
123
123
  }
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
124
125
  export function frameLoop(cb) {
125
126
  let prevFrame = 0;
126
127
  let prevTime = 0;
@@ -0,0 +1,6 @@
1
+ // @ts-check
2
+
3
+ import eslint from '@eslint/js';
4
+ import tseslint from 'typescript-eslint';
5
+
6
+ export default tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended);
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.4.8",
8
+ "version": "0.4.10",
9
9
  "exports": {
10
10
  ".": {
11
11
  "import": "./dist/index.js",