simulationjsv2 0.3.3 → 0.4.0

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/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # SimulationJS v2
2
2
 
3
- SimulationJS is a graphics library with simple easy to use APIs. Version 2 hopes to boost performance by using webgpu under the hood.
3
+ SimulationJS is a graphics library with simple easy to use APIs. Version 2 hopes to improve 3d graphics and boost performance by using webgpu under the hood.
4
4
 
5
- The APIs for v2 will be different however semantically, using them should be nearly identical.
5
+ The APIs for v2 will differ however semantically, using them should be nearly identical.
6
6
 
7
- **Documentation coming soon**
7
+ **Documentation eventually**
8
8
 
9
9
  By _Jackson Otto_
@@ -1,4 +1,4 @@
1
- /// <reference types="dist" />
1
+ /// <reference types="@webgpu/types" />
2
2
  import { Camera } from './simulation.js';
3
3
  import type { Vector2, Vector3, LerpFunc, VertexColorMap, ElementRotation, Mat4 } from './types.js';
4
4
  import { Vertex, Color } from './utils.js';
@@ -1,4 +1,4 @@
1
- /// <reference types="dist" />
1
+ /// <reference types="@webgpu/types" />
2
2
  import { Mat4, Vector2, Vector3 } from './types.js';
3
3
  import { Color } from './utils.js';
4
4
  import { SimulationElement } from './graphics.js';
@@ -17,7 +17,28 @@ export declare const getTransformationMatrix: (pos: Vector3, rotation: Vector3,
17
17
  export declare const getOrthoMatrix: (screenSize: [number, number]) => Float32Array;
18
18
  export declare const buildDepthTexture: (device: GPUDevice, width: number, height: number) => GPUTexture;
19
19
  export declare const buildMultisampleTexture: (device: GPUDevice, ctx: GPUCanvasContext, width: number, height: number) => GPUTexture;
20
- export declare const applyElementToScene: (scene: SimulationElement[], el: SimulationElement) => void;
20
+ export declare const addObject: (scene: SimSceneObjInfo[], el: SimulationElement<any>, id?: string) => void;
21
+ export declare const removeObject: (scene: SimSceneObjInfo[], el: SimulationElement<any>) => void;
22
+ export declare const removeObjectId: (scene: SimSceneObjInfo[], id: string) => void;
23
+ export declare class SimSceneObjInfo {
24
+ private obj;
25
+ private id;
26
+ private lifetime;
27
+ private currentLife;
28
+ constructor(obj: SimulationElement<any>, id?: string);
29
+ /**
30
+ * @param lifetime - ms
31
+ */
32
+ setLifetime(lifetime: number): void;
33
+ getLifetime(): number | null;
34
+ lifetimeComplete(): boolean;
35
+ /**
36
+ * @param amount - ms
37
+ */
38
+ traverseLife(amount: number): void;
39
+ getObj(): SimulationElement<Vector3 | Vector2>;
40
+ getId(): string | null;
41
+ }
21
42
  declare class Logger {
22
43
  constructor();
23
44
  private fmt;
@@ -42,5 +63,5 @@ export declare function matrixFromRotation(rotation: Vector3): Mat4;
42
63
  export declare function rotateMat4(mat: Mat4, rotation: Vector3): void;
43
64
  export declare function createPipeline(device: GPUDevice, module: GPUShaderModule, bindGroupLayout: GPUBindGroupLayout, presentationFormat: GPUTextureFormat, entryPoint: string, topology: GPUPrimitiveTopology): GPURenderPipeline;
44
65
  export declare function triangulateWireFrameOrder(len: number): number[];
45
- export declare function getTotalVertices(scene: SimulationElement[]): number;
66
+ export declare function getTotalVertices(scene: SimSceneObjInfo[]): number;
46
67
  export {};
@@ -59,14 +59,71 @@ export const buildMultisampleTexture = (device, ctx, width, height) => {
59
59
  sampleCount: 4
60
60
  });
61
61
  };
62
- export const applyElementToScene = (scene, el) => {
62
+ export const addObject = (scene, el, id) => {
63
63
  if (el instanceof SimulationElement) {
64
- scene.unshift(el);
64
+ const obj = new SimSceneObjInfo(el, id);
65
+ scene.unshift(obj);
65
66
  }
66
67
  else {
67
68
  throw logger.error('Cannot add invalid SimulationElement');
68
69
  }
69
70
  };
71
+ export const removeObject = (scene, el) => {
72
+ if (!(el instanceof SimulationElement))
73
+ return;
74
+ for (let i = 0; i < scene.length; i++) {
75
+ if (scene[i].getObj() === el) {
76
+ scene.splice(i, 1);
77
+ break;
78
+ }
79
+ }
80
+ };
81
+ export const removeObjectId = (scene, id) => {
82
+ for (let i = 0; i < scene.length; i++) {
83
+ if (scene[i].getId() === id) {
84
+ scene.splice(i, 1);
85
+ break;
86
+ }
87
+ }
88
+ };
89
+ export class SimSceneObjInfo {
90
+ obj;
91
+ id;
92
+ lifetime; // ms
93
+ currentLife;
94
+ constructor(obj, id) {
95
+ this.obj = obj;
96
+ this.id = id || null;
97
+ this.lifetime = null;
98
+ this.currentLife = 0;
99
+ }
100
+ /**
101
+ * @param lifetime - ms
102
+ */
103
+ setLifetime(lifetime) {
104
+ this.lifetime = lifetime;
105
+ }
106
+ getLifetime() {
107
+ return this.lifetime;
108
+ }
109
+ lifetimeComplete() {
110
+ if (this.lifetime === null)
111
+ return false;
112
+ return this.currentLife >= this.lifetime;
113
+ }
114
+ /**
115
+ * @param amount - ms
116
+ */
117
+ traverseLife(amount) {
118
+ this.currentLife += amount;
119
+ }
120
+ getObj() {
121
+ return this.obj;
122
+ }
123
+ getId() {
124
+ return this.id;
125
+ }
126
+ }
70
127
  class Logger {
71
128
  constructor() { }
72
129
  fmt(msg) {
@@ -250,9 +307,10 @@ export function triangulateWireFrameOrder(len) {
250
307
  export function getTotalVertices(scene) {
251
308
  let total = 0;
252
309
  for (let i = 0; i < scene.length; i++) {
253
- if (scene[i].isCollection)
310
+ const obj = scene[i].getObj();
311
+ if (obj.isCollection)
254
312
  continue;
255
- total += scene[i].getVertexCount();
313
+ total += obj.getVertexCount();
256
314
  }
257
315
  return total;
258
316
  }
@@ -1,20 +1,28 @@
1
- /// <reference types="dist" />
1
+ /// <reference types="@webgpu/types" />
2
2
  import { SimulationElement, SimulationElement3d } from './graphics.js';
3
3
  import type { Vector2, Vector3, LerpFunc } from './types.js';
4
4
  import { Color } from './utils.js';
5
5
  import { BlankGeometry } from './geometry.js';
6
+ import { SimSceneObjInfo } from './internalUtils.js';
6
7
  export declare class Simulation {
7
8
  canvasRef: HTMLCanvasElement | null;
8
9
  private bgColor;
9
10
  private scene;
10
11
  private fittingElement;
11
12
  private running;
13
+ private initialized;
12
14
  private frameRateView;
13
15
  private camera;
14
16
  private pipelines;
15
17
  private renderInfo;
16
18
  constructor(idOrCanvasRef: string | HTMLCanvasElement, camera?: Camera | null, showFrameRate?: boolean);
17
- add(el: SimulationElement<any>): void;
19
+ add(el: SimulationElement<any>, id?: string): void;
20
+ remove(el: SimulationElement<any>): void;
21
+ removeId(id: string): void;
22
+ /**
23
+ * @param lifetime - ms
24
+ */
25
+ setLifetime(el: SimulationElement<any>, lifetime: number): void;
18
26
  setCanvasSize(width: number, height: number): void;
19
27
  start(): void;
20
28
  stop(): void;
@@ -33,9 +41,15 @@ export declare class SceneCollection extends SimulationElement3d {
33
41
  constructor(name: string);
34
42
  setWireframe(_: boolean): void;
35
43
  getName(): string;
36
- getScene(): SimulationElement<Vector3>[];
37
- setScene(newScene: SimulationElement<any>[]): void;
44
+ getScene(): SimSceneObjInfo[];
45
+ setSceneObjects(newScene: SimulationElement<any>[]): void;
46
+ setScene(newScene: SimSceneObjInfo[]): void;
38
47
  add(el: SimulationElement<any>): void;
48
+ remove(el: SimulationElement<any>): void;
49
+ /**
50
+ * @param lifetime - ms
51
+ */
52
+ setLifetime(el: SimulationElement<any>, lifetime: number): void;
39
53
  empty(): void;
40
54
  getSceneBuffer(camera: Camera): number[];
41
55
  getWireframe(camera: Camera): number[];
@@ -3,7 +3,7 @@ import { SimulationElement3d } from './graphics.js';
3
3
  import { BUF_LEN } from './constants.js';
4
4
  import { Color, transitionValues, vector2, vector3 } from './utils.js';
5
5
  import { BlankGeometry } from './geometry.js';
6
- import { applyElementToScene, buildDepthTexture, buildMultisampleTexture, buildProjectionMatrix, createPipeline, getOrthoMatrix, getTotalVertices, getTransformationMatrix, logger } from './internalUtils.js';
6
+ import { SimSceneObjInfo, addObject, buildDepthTexture, buildMultisampleTexture, buildProjectionMatrix, createPipeline, getOrthoMatrix, getTotalVertices, getTransformationMatrix, logger, removeObject, removeObjectId } from './internalUtils.js';
7
7
  const shader = `
8
8
  struct Uniforms {
9
9
  modelViewProjectionMatrix : mat4x4<f32>,
@@ -115,6 +115,7 @@ export class Simulation {
115
115
  scene = [];
116
116
  fittingElement = false;
117
117
  running = true;
118
+ initialized = false;
118
119
  frameRateView;
119
120
  camera;
120
121
  pipelines;
@@ -152,8 +153,23 @@ export class Simulation {
152
153
  this.frameRateView = new FrameRateView(showFrameRate);
153
154
  this.frameRateView.updateFrameRate(1);
154
155
  }
155
- add(el) {
156
- applyElementToScene(this.scene, el);
156
+ add(el, id) {
157
+ addObject(this.scene, el, id);
158
+ }
159
+ remove(el) {
160
+ removeObject(this.scene, el);
161
+ }
162
+ removeId(id) {
163
+ removeObjectId(this.scene, id);
164
+ }
165
+ /**
166
+ * @param lifetime - ms
167
+ */
168
+ setLifetime(el, lifetime) {
169
+ for (let i = 0; i < this.scene.length; i++) {
170
+ if (this.scene[i].getObj() === el)
171
+ this.scene[i].setLifetime(lifetime);
172
+ }
157
173
  }
158
174
  setCanvasSize(width, height) {
159
175
  this.assertHasCanvas();
@@ -163,8 +179,13 @@ export class Simulation {
163
179
  this.canvasRef.style.height = height + 'px';
164
180
  }
165
181
  start() {
182
+ if (this.initialized) {
183
+ this.running = true;
184
+ return;
185
+ }
166
186
  (async () => {
167
187
  this.assertHasCanvas();
188
+ this.initialized = true;
168
189
  this.running = true;
169
190
  const adapter = await navigator.gpu.requestAdapter();
170
191
  if (!adapter)
@@ -191,8 +212,9 @@ export class Simulation {
191
212
  }
192
213
  propagateDevice(device) {
193
214
  for (let i = 0; i < this.scene.length; i++) {
194
- if (this.scene[i].isInstance) {
195
- this.scene[i].setDevice(device);
215
+ const obj = this.scene[i].getObj();
216
+ if (obj.isInstance) {
217
+ obj.setDevice(device);
196
218
  }
197
219
  }
198
220
  }
@@ -299,9 +321,11 @@ export class Simulation {
299
321
  let prev = Date.now() - 10;
300
322
  let prevFps = 0;
301
323
  const frame = async () => {
302
- if (!this.running || !canvas)
324
+ if (!canvas)
303
325
  return;
304
326
  requestAnimationFrame(frame);
327
+ if (!this.running)
328
+ return;
305
329
  const now = Date.now();
306
330
  const diff = Math.max(now - prev, 1);
307
331
  prev = now;
@@ -339,14 +363,14 @@ export class Simulation {
339
363
  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
340
364
  passEncoder.setPipeline(this.pipelines.triangleList3d);
341
365
  passEncoder.setBindGroup(0, uniformBindGroup);
342
- this.renderScene(device, passEncoder, this.scene);
366
+ this.renderScene(device, passEncoder, this.scene, diff);
343
367
  this.camera.updateConsumed();
344
368
  passEncoder.end();
345
369
  device.queue.submit([commandEncoder.finish()]);
346
370
  };
347
371
  requestAnimationFrame(frame);
348
372
  }
349
- async renderScene(device, passEncoder, scene) {
373
+ async renderScene(device, passEncoder, scene, diff) {
350
374
  if (this.pipelines === null)
351
375
  return;
352
376
  let totalVertices = getTotalVertices(scene);
@@ -355,17 +379,28 @@ export class Simulation {
355
379
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
356
380
  });
357
381
  let currentOffset = 0;
382
+ let toRemove = [];
358
383
  for (let i = 0; i < scene.length; i++) {
359
- if (scene[i].isCollection) {
360
- this.renderScene(device, passEncoder, scene[i].getScene());
384
+ const lifetime = scene[i].getLifetime();
385
+ if (lifetime !== null) {
386
+ const complete = scene[i].lifetimeComplete();
387
+ if (complete) {
388
+ toRemove.push(i);
389
+ continue;
390
+ }
391
+ scene[i].traverseLife(diff);
392
+ }
393
+ const obj = scene[i].getObj();
394
+ if (obj.isCollection) {
395
+ this.renderScene(device, passEncoder, obj.getScene(), diff);
361
396
  continue;
362
397
  }
363
- const buffer = new Float32Array(scene[i].getBuffer(this.camera));
398
+ const buffer = new Float32Array(obj.getBuffer(this.camera));
364
399
  const vertexCount = buffer.length / BUF_LEN;
365
400
  device.queue.writeBuffer(vertexBuffer, currentOffset, buffer);
366
401
  vertexBuffer.unmap();
367
- const is3d = Boolean(scene[i].is3d);
368
- if (scene[i].isWireframe()) {
402
+ const is3d = Boolean(obj.is3d);
403
+ if (obj.isWireframe()) {
369
404
  if (is3d) {
370
405
  passEncoder.setPipeline(this.pipelines.lineStrip3d);
371
406
  }
@@ -374,7 +409,7 @@ export class Simulation {
374
409
  }
375
410
  }
376
411
  else {
377
- const type = scene[i].getGeometryType();
412
+ const type = obj.getGeometryType();
378
413
  if (type === 'strip') {
379
414
  if (is3d) {
380
415
  passEncoder.setPipeline(this.pipelines.triangleStrip3d);
@@ -393,9 +428,9 @@ export class Simulation {
393
428
  }
394
429
  }
395
430
  let instances = 1;
396
- if (scene[i].isInstance) {
397
- instances = scene[i].getNumInstances();
398
- const buf = scene[i].getMatrixBuffer();
431
+ if (obj.isInstance) {
432
+ instances = obj.getNumInstances();
433
+ const buf = obj.getMatrixBuffer();
399
434
  if (buf && this.renderInfo) {
400
435
  const uniformBindGroup = device.createBindGroup({
401
436
  layout: this.renderInfo.bindGroupLayout,
@@ -421,6 +456,9 @@ export class Simulation {
421
456
  passEncoder.draw(vertexCount, instances, 0, 0);
422
457
  currentOffset += buffer.byteLength;
423
458
  }
459
+ for (let i = toRemove.length - 1; i >= 0; i--) {
460
+ removeObject(scene, scene[i].getObj());
461
+ }
424
462
  }
425
463
  fitElement() {
426
464
  this.assertHasCanvas();
@@ -457,17 +495,32 @@ export class SceneCollection extends SimulationElement3d {
457
495
  getScene() {
458
496
  return this.scene;
459
497
  }
498
+ setSceneObjects(newScene) {
499
+ this.scene = newScene.map((item) => new SimSceneObjInfo(item));
500
+ }
460
501
  setScene(newScene) {
461
502
  this.scene = newScene;
462
503
  }
463
504
  add(el) {
464
- applyElementToScene(this.scene, el);
505
+ addObject(this.scene, el);
506
+ }
507
+ remove(el) {
508
+ removeObject(this.scene, el);
509
+ }
510
+ /**
511
+ * @param lifetime - ms
512
+ */
513
+ setLifetime(el, lifetime) {
514
+ for (let i = 0; i < this.scene.length; i++) {
515
+ if (this.scene[i].getObj() === el)
516
+ this.scene[i].setLifetime(lifetime);
517
+ }
465
518
  }
466
519
  empty() {
467
520
  this.scene = [];
468
521
  }
469
522
  getSceneBuffer(camera) {
470
- return this.scene.map((item) => item.getBuffer(camera)).flat();
523
+ return this.scene.map((item) => item.getObj().getBuffer(camera)).flat();
471
524
  }
472
525
  getWireframe(camera) {
473
526
  return this.getSceneBuffer(camera);
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /// <reference types="dist" />
1
+ /// <reference types="@webgpu/types" />
2
2
  import { CubicBezierCurve2d, SplinePoint2d } from './graphics.js';
3
3
  import { Color, Vertex } from './utils.js';
4
4
  export type Shift<T extends any[]> = ((...args: T) => any) extends (arg1: any, ...rest: infer R) => any ? R : never;
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.3.3",
8
+ "version": "0.4.0",
9
9
  "exports": {
10
10
  ".": {
11
11
  "import": "./dist/index.js",