simulationjsv2 0.4.10 → 0.5.1

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,5 @@
1
1
  # TODO
2
2
 
3
- - [x] Memoize render scene buffer
4
- - Should only be reset to resize to a new larger object then reused on all frames
3
+ - [ ] Make input position vec3 not vec4
4
+ - [ ] Change position/rotation to be matrix transform on gpu
5
+ - [ ] Make getBuffer return cached Float32Array
@@ -1,4 +1,4 @@
1
- import { BufferExtenderInfo, CircleGeometryParams, CubeGeometryParams, EmptyParams, Line2dGeometryParams, Line3dGeometryParams, Mat4, PolygonGeometryParams, Spline2dGeometryParams, SquareGeometryParams, Vector2, Vector3, VertexColorMap } from './types.js';
1
+ import { VertexParamGeneratorInfo, 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
4
  export declare abstract class Geometry<T extends EmptyParams> {
@@ -14,9 +14,9 @@ export declare abstract class Geometry<T extends EmptyParams> {
14
14
  abstract recompute(): void;
15
15
  getTriangleVertexCount(): number;
16
16
  getWireframeVertexCount(): 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[];
17
+ protected bufferFromOrder(order: number[], color: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
18
+ getWireframeBuffer(color: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
19
+ getTriangleBuffer(color: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
20
20
  }
21
21
  export declare class PlaneGeometry extends Geometry<EmptyParams> {
22
22
  protected params: {};
@@ -26,7 +26,7 @@ export declare class PlaneGeometry extends Geometry<EmptyParams> {
26
26
  constructor(vertices: Vertex[]);
27
27
  recompute(): void;
28
28
  updateVertices(vertices: Vertex[]): void;
29
- getTriangleBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
29
+ getTriangleBuffer(color: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
30
30
  }
31
31
  export declare class CubeGeometry extends Geometry<CubeGeometryParams> {
32
32
  protected params: CubeGeometryParams;
@@ -48,7 +48,7 @@ export declare class SquareGeometry extends Geometry<SquareGeometryParams> {
48
48
  setWidth(width: number): void;
49
49
  setHeight(height: number): void;
50
50
  recompute(): void;
51
- getTriangleBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
51
+ getTriangleBuffer(color: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
52
52
  }
53
53
  export declare class BlankGeometry extends Geometry<EmptyParams> {
54
54
  protected wireframeOrder: never[];
@@ -83,8 +83,8 @@ export declare class Spline2dGeometry extends Geometry<Spline2dGeometryParams> {
83
83
  private computeCurves;
84
84
  private updateWireframeOrder;
85
85
  recompute(): void;
86
- getWireframeBuffer(color: Color, bufferExtender?: BufferExtenderInfo): number[];
87
- getTriangleBuffer(_: Color, bufferExtender?: BufferExtenderInfo): number[];
86
+ getWireframeBuffer(color: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
87
+ getTriangleBuffer(_: Color, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
88
88
  }
89
89
  export declare class Line2dGeometry extends Geometry<Line2dGeometryParams> {
90
90
  protected wireframeOrder: number[];
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, bufferExtender) {
27
+ bufferFromOrder(order, color, vertexParamGenerator) {
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, vector2(), bufferExtender);
32
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], color, vector2(), vertexParamGenerator);
33
33
  })
34
34
  .flat();
35
35
  }
36
- getWireframeBuffer(color, bufferExtender) {
37
- return this.bufferFromOrder(this.wireframeOrder, color, bufferExtender);
36
+ getWireframeBuffer(color, vertexParamGenerator) {
37
+ return this.bufferFromOrder(this.wireframeOrder, color, vertexParamGenerator);
38
38
  }
39
- getTriangleBuffer(color, bufferExtender) {
40
- return this.bufferFromOrder(this.triangleOrder, color, bufferExtender);
39
+ getTriangleBuffer(color, vertexParamGenerator) {
40
+ return this.bufferFromOrder(this.triangleOrder, color, vertexParamGenerator);
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, bufferExtender) {
64
+ getTriangleBuffer(color, vertexParamGenerator) {
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, vector2(), bufferExtender);
70
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], vertex.getColor() || color, vector2(), vertexParamGenerator);
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, bufferExtender) {
158
+ getTriangleBuffer(color, vertexParamGenerator) {
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, vector2(), bufferExtender);
163
+ return bufferGenerator.generate(pos[0], pos[1], pos[2], this.params.colorMap[vertexIndex] || color, vector2(), vertexParamGenerator);
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, bufferExtender) {
352
+ getWireframeBuffer(color, vertexParamGenerator) {
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, vector2(), bufferExtender);
357
+ return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], color, vector2(), vertexParamGenerator);
358
358
  })
359
359
  .flat();
360
360
  }
361
- getTriangleBuffer(_, bufferExtender) {
361
+ getTriangleBuffer(_, vertexParamGenerator) {
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], vector2(), bufferExtender);
366
+ return bufferGenerator.generate(vertex[0], vertex[1], vertex[2], this.params.vertexColors[vertexIndex], vector2(), vertexParamGenerator);
367
367
  })
368
368
  .flat();
369
369
  }
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@webgpu/types" />
2
2
  import { Camera } from './simulation.js';
3
- import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation, Mat4, AnySimulationElement, BufferExtenderInfo } from './types.js';
3
+ import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation, Mat4, AnySimulationElement, VertexParamGeneratorInfo } 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';
@@ -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, bufferExtender?: BufferExtenderInfo): number[];
33
+ getBuffer(camera: Camera, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
34
34
  }
35
35
  export declare abstract class SimulationElement3d extends SimulationElement {
36
36
  protected pos: Vector3;
@@ -49,7 +49,7 @@ export declare abstract class SimulationElement2d extends SimulationElement<Vect
49
49
  rotate(rotation: number, t?: number, f?: LerpFunc): Promise<void>;
50
50
  rotateTo(newRotation: number, t?: number, f?: LerpFunc): Promise<void>;
51
51
  move(amount: Vector2, t?: number, f?: LerpFunc): Promise<void>;
52
- moveTo(pos: Vector2, t?: number, f?: LerpFunc): Promise<void>;
52
+ moveTo(newPos: Vector2, t?: number, f?: LerpFunc): Promise<void>;
53
53
  }
54
54
  export declare class Plane extends SimulationElement3d {
55
55
  protected geometry: PlaneGeometry;
package/dist/graphics.js CHANGED
@@ -73,8 +73,8 @@ export class SimulationElement {
73
73
  }
74
74
  this.geometry.updateMatrix(matrix);
75
75
  }
76
- getBuffer(camera, bufferExtender) {
77
- const shouldEvalExtender = bufferExtender?.shouldEvaluate?.();
76
+ getBuffer(camera, vertexParamGenerator) {
77
+ const shouldEvalExtender = vertexParamGenerator?.shouldEvaluate?.();
78
78
  const reEvalExtender = shouldEvalExtender === undefined ? true : shouldEvalExtender;
79
79
  if (this.vertexCache.shouldUpdate() || camera.hasUpdated() || reEvalExtender) {
80
80
  this.updateMatrix(camera);
@@ -84,10 +84,10 @@ export class SimulationElement {
84
84
  }
85
85
  let resBuffer;
86
86
  if (this.isWireframe()) {
87
- resBuffer = this.geometry.getWireframeBuffer(this.color, bufferExtender);
87
+ resBuffer = this.geometry.getWireframeBuffer(this.color, vertexParamGenerator);
88
88
  }
89
89
  else {
90
- resBuffer = this.geometry.getTriangleBuffer(this.color, bufferExtender);
90
+ resBuffer = this.geometry.getTriangleBuffer(this.color, vertexParamGenerator);
91
91
  }
92
92
  bufferGenerator.setInstancing(false);
93
93
  this.vertexCache.setCache(resBuffer);
@@ -188,18 +188,22 @@ export class SimulationElement2d extends SimulationElement {
188
188
  }, t, f);
189
189
  }
190
190
  move(amount, t = 0, f) {
191
+ const tempAmount = cloneBuf(amount);
192
+ vector2ToPixelRatio(tempAmount);
191
193
  const finalPos = vector2();
192
- vec3.add(amount, this.pos, finalPos);
194
+ vec3.add(tempAmount, this.pos, finalPos);
193
195
  return transitionValues((p) => {
194
- this.pos[0] += amount[0] * p;
195
- this.pos[1] += amount[1] * p;
196
+ this.pos[0] += tempAmount[0] * p;
197
+ this.pos[1] += tempAmount[1] * p;
196
198
  this.vertexCache.updated();
197
199
  }, () => {
198
200
  this.pos = finalPos;
199
201
  this.vertexCache.updated();
200
202
  }, t, f);
201
203
  }
202
- moveTo(pos, t = 0, f) {
204
+ moveTo(newPos, t = 0, f) {
205
+ const pos = cloneBuf(newPos);
206
+ vector2ToPixelRatio(pos);
203
207
  const diff = vector2();
204
208
  vec2.sub(pos, this.pos, diff);
205
209
  return transitionValues((p) => {
@@ -1,5 +1,5 @@
1
1
  /// <reference types="@webgpu/types" />
2
- import { AnySimulationElement, BufferExtenderInfo, Mat4, Vector2, Vector3, VertexParamInfo } from './types.js';
2
+ import { AnySimulationElement, VertexParamGeneratorInfo, Mat4, Vector2, Vector3, VertexParamInfo } from './types.js';
3
3
  import { Color } from './utils.js';
4
4
  export declare class VertexCache {
5
5
  private vertices;
@@ -52,14 +52,14 @@ declare class BufferGenerator {
52
52
  private instancing;
53
53
  constructor();
54
54
  setInstancing(state: boolean): void;
55
- generate(x: number, y: number, z: number, color: Color, uv?: Vector2, bufferExtender?: BufferExtenderInfo): number[];
55
+ generate(x: number, y: number, z: number, color: Color, uv?: Vector2, vertexParamGenerator?: VertexParamGeneratorInfo): number[];
56
56
  }
57
57
  export declare const bufferGenerator: BufferGenerator;
58
58
  export declare function vector3ToPixelRatio(vec: Vector3): void;
59
59
  export declare function vector2ToPixelRatio(vec: Vector2): void;
60
60
  export declare function matrixFromRotation(rotation: Vector3): Mat4;
61
61
  export declare function rotateMat4(mat: Mat4, rotation: Vector3): void;
62
- export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, bindGroupLayout: GPUBindGroupLayout, presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology, vertexParams?: VertexParamInfo[]): GPURenderPipeline;
62
+ export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, bindGroupLayouts: GPUBindGroupLayout[], presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology, vertexParams?: VertexParamInfo[]): GPURenderPipeline;
63
63
  export declare function triangulateWireFrameOrder(len: number): number[];
64
64
  export declare function getTotalVertices(scene: SimSceneObjInfo[]): number;
65
65
  export {};
@@ -182,11 +182,11 @@ class BufferGenerator {
182
182
  setInstancing(state) {
183
183
  this.instancing = state;
184
184
  }
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})`);
185
+ generate(x, y, z, color, uv = vector2(), vertexParamGenerator) {
186
+ if (vertexParamGenerator) {
187
+ const buf = vertexParamGenerator.createBuffer(x, y, z, color);
188
+ if (buf.length !== vertexParamGenerator.bufferSize) {
189
+ logger.log_error(`Vertex size for shader group does not match buffer extension size (${buf.length} to expected ${vertexParamGenerator.bufferSize})`);
190
190
  return [];
191
191
  }
192
192
  return buf;
@@ -216,7 +216,7 @@ export function rotateMat4(mat, rotation) {
216
216
  mat4.rotateY(mat, rotation[1], mat);
217
217
  mat4.rotateX(mat, rotation[0], mat);
218
218
  }
219
- export function createPipeline(device, module, bindGroupLayout, presentationFormat, entryPoint, topology, vertexParams) {
219
+ export function createPipeline(device, module, bindGroupLayouts, presentationFormat, entryPoint, topology, vertexParams) {
220
220
  let params = [
221
221
  {
222
222
  // position
@@ -259,7 +259,7 @@ export function createPipeline(device, module, bindGroupLayout, presentationForm
259
259
  }
260
260
  return device.createRenderPipeline({
261
261
  layout: device.createPipelineLayout({
262
- bindGroupLayouts: [bindGroupLayout]
262
+ bindGroupLayouts: bindGroupLayouts
263
263
  }),
264
264
  vertex: {
265
265
  module,
@@ -1,6 +1,6 @@
1
1
  /// <reference types="@webgpu/types" />
2
2
  import { SimulationElement3d } from './graphics.js';
3
- import type { Vector2, Vector3, LerpFunc, AnySimulationElement, BufferExtenderInfo, VertexParamInfo } from './types.js';
3
+ import type { Vector2, Vector3, LerpFunc, AnySimulationElement, VertexParamGeneratorInfo, VertexParamInfo, BindGroupInfo } from './types.js';
4
4
  import { Color } from './utils.js';
5
5
  import { BlankGeometry } from './geometry.js';
6
6
  import { SimSceneObjInfo } from './internalUtils.js';
@@ -16,7 +16,12 @@ export declare class Simulation {
16
16
  private device;
17
17
  private pipelines;
18
18
  private renderInfo;
19
+ private resizeEvents;
19
20
  constructor(idOrCanvasRef: string | HTMLCanvasElement, camera?: Camera | null, showFrameRate?: boolean);
21
+ private handleCanvasResize;
22
+ onResize(cb: (width: number, height: number) => void): void;
23
+ getWidth(): number;
24
+ getHeight(): number;
20
25
  add(el: AnySimulationElement, id?: string): void;
21
26
  remove(el: AnySimulationElement): void;
22
27
  removeId(id: string): void;
@@ -24,6 +29,7 @@ export declare class Simulation {
24
29
  * @param lifetime - ms
25
30
  */
26
31
  setLifetime(el: AnySimulationElement, lifetime: number): void;
32
+ private applyCanvasSize;
27
33
  setCanvasSize(width: number, height: number): void;
28
34
  start(): void;
29
35
  private propagateDevice;
@@ -87,12 +93,19 @@ export declare class ShaderGroup extends SceneCollection {
87
93
  private code;
88
94
  private module;
89
95
  private pipeline;
96
+ private bindGroupLayout;
90
97
  private topology;
91
- private bufferExtender;
98
+ private paramGenerator;
92
99
  private vertexParams;
93
- constructor(shaderCode: string, topology: GPUPrimitiveTopology | undefined, vertexParams: VertexParamInfo[], bufferExtender: BufferExtenderInfo);
100
+ private bindGroup;
101
+ private valueBuffers;
102
+ constructor(shaderCode: string, topology: GPUPrimitiveTopology | undefined, vertexParams: VertexParamInfo[], paramGenerator: VertexParamGeneratorInfo, bindGroup?: BindGroupInfo);
94
103
  protected propagateDevice(device: GPUDevice): void;
104
+ getBindGroupLayout(): GPUBindGroupLayout | null;
95
105
  getPipeline(): GPURenderPipeline | null;
106
+ getBindGroupBuffers(): GPUBuffer[] | null;
107
+ private createBuffer;
96
108
  protected updateMatrix(camera: Camera): void;
97
- getBufferExtender(): BufferExtenderInfo;
109
+ getVertexParamGenerator(): VertexParamGeneratorInfo;
110
+ hasBindGroup(): boolean;
98
111
  }
@@ -12,7 +12,7 @@ struct Uniforms {
12
12
 
13
13
  @group(0) @binding(0) var<uniform> uniforms : Uniforms;
14
14
 
15
- @group(0) @binding(1) var<storage, read> instanceMatrices : array<mat4x4f>;
15
+ @group(0) @binding(1) var<storage> instanceMatrices : array<mat4x4f>;
16
16
 
17
17
  struct VertexOutput {
18
18
  @builtin(position) Position : vec4<f32>,
@@ -129,6 +129,7 @@ export class Simulation {
129
129
  device = null;
130
130
  pipelines = null;
131
131
  renderInfo = null;
132
+ resizeEvents;
132
133
  constructor(idOrCanvasRef, camera = null, showFrameRate = false) {
133
134
  if (typeof idOrCanvasRef === 'string') {
134
135
  const ref = document.getElementById(idOrCanvasRef);
@@ -150,16 +151,29 @@ export class Simulation {
150
151
  this.camera = camera;
151
152
  if (parent === null)
152
153
  throw logger.error('Canvas parent is null');
154
+ this.resizeEvents = [];
153
155
  addEventListener('resize', () => {
154
- if (this.fittingElement) {
155
- const width = parent.clientWidth;
156
- const height = parent.clientHeight;
157
- this.setCanvasSize(width, height);
158
- }
156
+ this.handleCanvasResize(parent);
159
157
  });
160
158
  this.frameRateView = new FrameRateView(showFrameRate);
161
159
  this.frameRateView.updateFrameRate(1);
162
160
  }
161
+ handleCanvasResize(parent) {
162
+ if (this.fittingElement) {
163
+ const width = parent.clientWidth;
164
+ const height = parent.clientHeight;
165
+ this.setCanvasSize(width, height);
166
+ }
167
+ }
168
+ onResize(cb) {
169
+ this.resizeEvents.push(cb);
170
+ }
171
+ getWidth() {
172
+ return (this.canvasRef?.width || 0) / devicePixelRatio;
173
+ }
174
+ getHeight() {
175
+ return (this.canvasRef?.height || 0) / devicePixelRatio;
176
+ }
163
177
  add(el, id) {
164
178
  addObject(this.scene, el, this.device, id);
165
179
  }
@@ -178,7 +192,7 @@ export class Simulation {
178
192
  this.scene[i].setLifetime(lifetime);
179
193
  }
180
194
  }
181
- setCanvasSize(width, height) {
195
+ applyCanvasSize(width, height) {
182
196
  if (this.canvasRef === null)
183
197
  return;
184
198
  this.canvasRef.width = width * devicePixelRatio;
@@ -186,6 +200,12 @@ export class Simulation {
186
200
  this.canvasRef.style.width = width + 'px';
187
201
  this.canvasRef.style.height = height + 'px';
188
202
  }
203
+ setCanvasSize(width, height) {
204
+ this.applyCanvasSize(width, height);
205
+ for (let i = 0; i < this.resizeEvents.length; i++) {
206
+ this.resizeEvents[i](width, height);
207
+ }
208
+ }
189
209
  start() {
190
210
  if (this.initialized) {
191
211
  this.running = true;
@@ -282,12 +302,12 @@ export class Simulation {
282
302
  vertexBuffer: null
283
303
  };
284
304
  this.pipelines = {
285
- triangleList2d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_2d', 'triangle-list'),
286
- triangleStrip2d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_2d', 'triangle-strip'),
287
- lineStrip2d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_2d', 'line-strip'),
288
- triangleList3d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_3d', 'triangle-list'),
289
- triangleStrip3d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_3d', 'triangle-strip'),
290
- lineStrip3d: createPipeline(device, shaderModule, bindGroupLayout, presentationFormat, 'vertex_main_3d', 'line-strip')
305
+ triangleList2d: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'vertex_main_2d', 'triangle-list'),
306
+ triangleStrip2d: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'vertex_main_2d', 'triangle-strip'),
307
+ lineStrip2d: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'vertex_main_2d', 'line-strip'),
308
+ triangleList3d: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'vertex_main_3d', 'triangle-list'),
309
+ triangleStrip3d: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'vertex_main_3d', 'triangle-strip'),
310
+ lineStrip3d: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'vertex_main_3d', 'line-strip')
291
311
  };
292
312
  const uniformBindGroup = device.createBindGroup({
293
313
  layout: bindGroupLayout,
@@ -423,14 +443,23 @@ export class Simulation {
423
443
  if (obj instanceof ShaderGroup) {
424
444
  const pipeline = obj.getPipeline();
425
445
  if (pipeline !== null) {
426
- shaderInfo = { pipeline, bufferExtender: obj.getBufferExtender() };
446
+ shaderInfo = {
447
+ pipeline,
448
+ paramGenerator: obj.getVertexParamGenerator(),
449
+ bufferInfo: obj.hasBindGroup()
450
+ ? {
451
+ buffers: obj.getBindGroupBuffers(),
452
+ layout: obj.getBindGroupLayout()
453
+ }
454
+ : null
455
+ };
427
456
  }
428
457
  }
429
458
  currentOffset += this.renderScene(device, passEncoder, vertexBuffer, obj.getScene(), currentOffset, diff, shaderInfo || undefined);
430
459
  continue;
431
460
  }
432
- const buffer = new Float32Array(obj.getBuffer(this.camera, shaderInfo?.bufferExtender));
433
- const bufLen = shaderInfo?.bufferExtender?.size || BUF_LEN;
461
+ const buffer = new Float32Array(obj.getBuffer(this.camera, shaderInfo?.paramGenerator));
462
+ const bufLen = shaderInfo?.paramGenerator?.bufferSize || BUF_LEN;
434
463
  const vertexCount = buffer.length / bufLen;
435
464
  device.queue.writeBuffer(vertexBuffer, currentOffset, buffer);
436
465
  vertexBuffer.unmap();
@@ -490,6 +519,19 @@ export class Simulation {
490
519
  passEncoder.setBindGroup(0, uniformBindGroup);
491
520
  }
492
521
  }
522
+ if (shaderInfo && shaderInfo.bufferInfo) {
523
+ const bindGroupEntries = shaderInfo.bufferInfo.buffers.map((buffer, index) => ({
524
+ binding: index,
525
+ resource: {
526
+ buffer
527
+ }
528
+ }));
529
+ const bindGroup = device.createBindGroup({
530
+ layout: shaderInfo.bufferInfo.layout,
531
+ entries: bindGroupEntries
532
+ });
533
+ passEncoder.setBindGroup(1, bindGroup);
534
+ }
493
535
  passEncoder.setVertexBuffer(0, vertexBuffer, currentOffset, buffer.byteLength);
494
536
  passEncoder.draw(vertexCount, instances, 0, 0);
495
537
  currentOffset += buffer.byteLength;
@@ -682,17 +724,7 @@ export class Camera {
682
724
  return this.aspectRatio;
683
725
  }
684
726
  }
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 = `
727
+ const defaultShaderCode = `
696
728
  struct Uniforms {
697
729
  modelViewProjectionMatrix : mat4x4<f32>,
698
730
  orthoProjectionMatrix : mat4x4<f32>
@@ -702,17 +734,34 @@ struct Uniforms {
702
734
 
703
735
  @group(0) @binding(1) var<storage, read> instanceMatrices : array<mat4x4f>;
704
736
  `;
737
+ export class ShaderGroup extends SceneCollection {
738
+ geometry;
739
+ code;
740
+ module;
741
+ pipeline;
742
+ bindGroupLayout;
743
+ topology;
744
+ paramGenerator;
745
+ vertexParams;
746
+ bindGroup;
747
+ valueBuffers;
748
+ constructor(shaderCode, topology = 'triangle-list', vertexParams, paramGenerator, bindGroup) {
749
+ super();
705
750
  this.geometry = new BlankGeometry();
706
- this.code = defaultCode + shaderCode;
751
+ this.code = defaultShaderCode + shaderCode;
707
752
  this.module = null;
708
753
  this.pipeline = null;
754
+ this.bindGroupLayout = null;
755
+ this.bindGroup = bindGroup || null;
709
756
  this.topology = topology;
710
- this.bufferExtender = bufferExtender;
757
+ this.paramGenerator = paramGenerator;
711
758
  this.vertexParams = vertexParams;
759
+ this.valueBuffers = null;
712
760
  }
713
761
  propagateDevice(device) {
714
762
  super.propagateDevice(device);
715
763
  this.module = device.createShaderModule({ code: this.code });
764
+ const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
716
765
  const bindGroupLayout = device.createBindGroupLayout({
717
766
  entries: [
718
767
  {
@@ -731,16 +780,75 @@ struct Uniforms {
731
780
  }
732
781
  ]
733
782
  });
734
- const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
735
- this.pipeline = createPipeline(device, this.module, bindGroupLayout, presentationFormat, 'vertex_main_2d', this.topology, this.vertexParams);
783
+ const bindGroups = [bindGroupLayout];
784
+ if (this.bindGroup !== null) {
785
+ const entryValues = this.bindGroup.bindings.map((binding, index) => ({
786
+ binding: index,
787
+ visibility: binding.visibility,
788
+ buffer: binding.buffer
789
+ }));
790
+ this.bindGroupLayout = device.createBindGroupLayout({
791
+ entries: entryValues
792
+ });
793
+ bindGroups.push(this.bindGroupLayout);
794
+ }
795
+ this.pipeline = createPipeline(device, this.module, bindGroups, presentationFormat, 'vertex_main_2d', this.topology, this.vertexParams);
796
+ }
797
+ getBindGroupLayout() {
798
+ return this.bindGroupLayout;
736
799
  }
737
800
  getPipeline() {
738
801
  return this.pipeline;
739
802
  }
803
+ getBindGroupBuffers() {
804
+ if (this.bindGroup === null)
805
+ return null;
806
+ if (this.device === null)
807
+ return null;
808
+ const values = this.bindGroup.values();
809
+ if (this.valueBuffers === null) {
810
+ this.valueBuffers = [];
811
+ for (let i = 0; i < values.length; i++) {
812
+ const buffer = this.createBuffer(this.device, values[i]);
813
+ this.valueBuffers.push(buffer);
814
+ }
815
+ }
816
+ else {
817
+ for (let i = 0; i < values.length; i++) {
818
+ const arrayConstructor = values[i].array;
819
+ const array = new arrayConstructor(values[i].value);
820
+ if (array.byteLength > this.valueBuffers[i].size) {
821
+ const newBuffer = this.createBuffer(this.device, values[i]);
822
+ this.valueBuffers[i].destroy();
823
+ this.valueBuffers[i] = newBuffer;
824
+ }
825
+ else {
826
+ this.device.queue.writeBuffer(this.valueBuffers[i], 0, array.buffer, array.byteOffset, array.byteLength);
827
+ }
828
+ }
829
+ }
830
+ return this.valueBuffers;
831
+ }
832
+ createBuffer(device, value) {
833
+ const arrayConstructor = value.array;
834
+ const array = new arrayConstructor(value.value);
835
+ const buffer = device.createBuffer({
836
+ mappedAtCreation: true,
837
+ size: array.byteLength,
838
+ usage: value.usage
839
+ });
840
+ const bufferArr = new arrayConstructor(buffer.getMappedRange());
841
+ bufferArr.set(array);
842
+ buffer.unmap();
843
+ return buffer;
844
+ }
740
845
  updateMatrix(camera) {
741
846
  this.defaultUpdateMatrix(camera);
742
847
  }
743
- getBufferExtender() {
744
- return this.bufferExtender;
848
+ getVertexParamGenerator() {
849
+ return this.paramGenerator;
850
+ }
851
+ hasBindGroup() {
852
+ return !!this.bindGroup;
745
853
  }
746
854
  }
package/dist/types.d.ts CHANGED
@@ -78,20 +78,35 @@ export type RenderInfo = {
78
78
  bindGroupLayout: GPUBindGroupLayout;
79
79
  vertexBuffer: GPUBuffer | null;
80
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[];
81
+ export type VertexParamGeneratorInfo = {
82
+ bufferSize: number;
83
+ createBuffer: (x: number, y: number, z: number, color: Color) => number[];
88
84
  shouldEvaluate?: () => boolean;
89
85
  };
90
86
  export type ShaderInfo = {
91
87
  pipeline: GPURenderPipeline;
92
- bufferExtender: BufferExtenderInfo;
88
+ paramGenerator: VertexParamGeneratorInfo;
89
+ bufferInfo: {
90
+ buffers: GPUBuffer[];
91
+ layout: GPUBindGroupLayout;
92
+ } | null;
93
93
  };
94
94
  export type VertexParamInfo = {
95
95
  format: GPUVertexFormat;
96
96
  size: number;
97
97
  };
98
+ export type BindGroupEntry = {
99
+ visibility: GPUBindGroupLayoutEntry['visibility'];
100
+ buffer: GPUBindGroupLayoutEntry['buffer'];
101
+ };
102
+ export type ArrayConstructors = Float32ArrayConstructor | Float64ArrayConstructor | Int8ArrayConstructor | Int16ArrayConstructor | Int32ArrayConstructor;
103
+ export type ArrayTypes = Float32Array | Float64Array | Int8Array | Int16Array | Int32Array;
104
+ export type BindGroupValue = {
105
+ value: number[];
106
+ usage: GPUBufferDescriptor['usage'];
107
+ array: ArrayConstructors;
108
+ };
109
+ export type BindGroupInfo = {
110
+ bindings: BindGroupEntry[];
111
+ values: () => BindGroupValue[];
112
+ };
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.10",
8
+ "version": "0.5.1",
9
9
  "exports": {
10
10
  ".": {
11
11
  "import": "./dist/index.js",