simulationjsv2 0.5.2 → 0.7.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.
@@ -1,64 +1,42 @@
1
1
  import { vec3 } from 'wgpu-matrix';
2
- import { Instance, SimulationElement3d } from './graphics.js';
3
- import { BUF_LEN } from './constants.js';
4
- import { Color, toSceneObjInfoMany, transitionValues, vector2, vector3 } from './utils.js';
2
+ import { EmptyElement, SimulationElement3d } from './graphics.js';
3
+ import { BUF_LEN, worldProjMatOffset } from './constants.js';
4
+ import { Color, matrix4, transitionValues, vector2, vector3 } from './utils.js';
5
5
  import { BlankGeometry } from './geometry.js';
6
- import { addObject, buildDepthTexture, buildMultisampleTexture, buildProjectionMatrix, createPipeline, getOrthoMatrix, getTotalVertices, getTransformationMatrix, logger, removeObject, removeObjectId } from './internalUtils.js';
6
+ import { SimSceneObjInfo, buildDepthTexture, buildMultisampleTexture, updateProjectionMatrix, createPipeline, getTotalVertices, logger, removeObjectId, updateOrthoProjectionMatrix, updateWorldProjectionMatrix } from './internalUtils.js';
7
+ import { Settings } from './settings.js';
7
8
  const shader = `
8
9
  struct Uniforms {
9
- modelViewProjectionMatrix : mat4x4<f32>,
10
- orthoProjectionMatrix : mat4x4<f32>
10
+ worldProjectionMatrix: mat4x4<f32>,
11
+ modelProjectionMatrix: mat4x4<f32>,
11
12
  }
12
13
 
13
- @group(0) @binding(0) var<uniform> uniforms : Uniforms;
14
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
14
15
 
15
- @group(0) @binding(1) var<storage> instanceMatrices : array<mat4x4f>;
16
+ @group(0) @binding(1) var<storage> instanceMatrices: array<mat4x4f>;
16
17
 
17
18
  struct VertexOutput {
18
- @builtin(position) Position : vec4<f32>,
19
- @location(0) fragUV : vec2<f32>,
20
- @location(1) fragColor : vec4<f32>,
19
+ @builtin(position) Position: vec4<f32>,
20
+ @location(0) fragUV: vec2<f32>,
21
+ @location(1) fragColor: vec4<f32>,
21
22
  @location(2) fragPosition: vec4<f32>,
22
23
  }
23
24
 
24
25
  @vertex
25
- fn vertex_main_3d(
26
- @builtin(instance_index) instanceIdx : u32,
27
- @location(0) position : vec4<f32>,
28
- @location(1) color : vec4<f32>,
29
- @location(2) uv : vec2<f32>,
30
- @location(3) drawingInstance: f32
31
- ) -> VertexOutput {
32
- var output : VertexOutput;
33
-
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
-
41
- output.fragUV = uv;
42
- output.fragPosition = output.Position;
43
- output.fragColor = color;
44
- return output;
45
- }
46
-
47
- @vertex
48
- fn vertex_main_2d(
49
- @builtin(instance_index) instanceIdx : u32,
50
- @location(0) position : vec4<f32>,
51
- @location(1) color : vec4<f32>,
52
- @location(2) uv : vec2<f32>,
26
+ fn vertex_main(
27
+ @builtin(instance_index) instanceIdx: u32,
28
+ @location(0) position: vec3<f32>,
29
+ @location(1) color: vec4<f32>,
30
+ @location(2) uv: vec2<f32>,
53
31
  @location(3) drawingInstance: f32
54
32
  ) -> VertexOutput {
55
33
  var output: VertexOutput;
56
34
 
35
+
57
36
  if (drawingInstance == 1) {
58
- let transformedPos = instanceMatrices[instanceIdx] * position;
59
- output.Position = uniforms.orthoProjectionMatrix * transformedPos;
37
+ output.Position = uniforms.worldProjectionMatrix * uniforms.modelProjectionMatrix * instanceMatrices[instanceIdx] * vec4(position, 1.0);
60
38
  } else {
61
- output.Position = uniforms.orthoProjectionMatrix * position;
39
+ output.Position = uniforms.worldProjectionMatrix * uniforms.modelProjectionMatrix * vec4(position, 1.0);
62
40
  }
63
41
 
64
42
  output.fragUV = uv;
@@ -117,7 +95,117 @@ class FrameRateView {
117
95
  }
118
96
  }
119
97
  }
120
- export class Simulation {
98
+ const baseBindGroupLayout = {
99
+ entries: [
100
+ {
101
+ binding: 0,
102
+ visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
103
+ buffer: {
104
+ type: 'uniform'
105
+ }
106
+ },
107
+ {
108
+ binding: 1,
109
+ visibility: GPUShaderStage.VERTEX,
110
+ buffer: {
111
+ type: 'read-only-storage'
112
+ }
113
+ }
114
+ ]
115
+ };
116
+ let aspectRatio = 0;
117
+ const projMat = matrix4();
118
+ const worldProjMat = matrix4();
119
+ const orthoMatrix = matrix4();
120
+ export class Camera {
121
+ pos;
122
+ rotation;
123
+ aspectRatio = 1;
124
+ updated;
125
+ screenSize = vector2();
126
+ constructor(pos, rotation = vector3()) {
127
+ this.pos = pos;
128
+ this.updated = false;
129
+ this.rotation = rotation;
130
+ }
131
+ setScreenSize(size) {
132
+ this.screenSize = size;
133
+ this.aspectRatio = size[0] / size[1];
134
+ this.updated = true;
135
+ }
136
+ getScreenSize() {
137
+ return this.screenSize;
138
+ }
139
+ hasUpdated() {
140
+ return this.updated;
141
+ }
142
+ updateConsumed() {
143
+ this.updated = false;
144
+ }
145
+ move(amount, t = 0, f) {
146
+ const initial = vector3();
147
+ vec3.clone(this.pos, initial);
148
+ return transitionValues((p) => {
149
+ const x = amount[0] * p;
150
+ const y = amount[1] * p;
151
+ const z = amount[2] * p;
152
+ const diff = vector3(x, y, z);
153
+ vec3.add(this.pos, diff, this.pos);
154
+ }, () => {
155
+ vec3.add(initial, amount, this.pos);
156
+ }, t, f);
157
+ }
158
+ moveTo(pos, t = 0, f) {
159
+ const diff = vector3();
160
+ vec3.sub(pos, this.pos, diff);
161
+ return transitionValues((p) => {
162
+ const x = diff[0] * p;
163
+ const y = diff[1] * p;
164
+ const z = diff[2] * p;
165
+ const amount = vector3(x, y, z);
166
+ vec3.add(this.pos, amount, this.pos);
167
+ }, () => {
168
+ vec3.clone(pos, this.pos);
169
+ }, t, f);
170
+ }
171
+ rotateTo(value, t = 0, f) {
172
+ const diff = vec3.clone(value);
173
+ vec3.sub(diff, diff, this.rotation);
174
+ return transitionValues((p) => {
175
+ const x = diff[0] * p;
176
+ const y = diff[1] * p;
177
+ const z = diff[2] * p;
178
+ vec3.add(this.rotation, this.rotation, vector3(x, y, z));
179
+ this.updated = true;
180
+ }, () => {
181
+ this.rotation = value;
182
+ }, t, f);
183
+ }
184
+ rotate(amount, t = 0, f) {
185
+ const initial = vector3();
186
+ vec3.clone(this.rotation, initial);
187
+ return transitionValues((p) => {
188
+ const x = amount[0] * p;
189
+ const y = amount[1] * p;
190
+ const z = amount[2] * p;
191
+ vec3.add(this.rotation, vector3(x, y, z), this.rotation);
192
+ this.updated = true;
193
+ }, () => {
194
+ vec3.add(initial, amount, this.rotation);
195
+ }, t, f);
196
+ }
197
+ getRotation() {
198
+ return this.rotation;
199
+ }
200
+ getPos() {
201
+ return this.pos;
202
+ }
203
+ getAspectRatio() {
204
+ return this.aspectRatio;
205
+ }
206
+ }
207
+ export let camera = new Camera(vector3());
208
+ export class Simulation extends Settings {
121
209
  canvasRef = null;
122
210
  bgColor = new Color(255, 255, 255);
123
211
  scene = [];
@@ -125,12 +213,12 @@ export class Simulation {
125
213
  running = true;
126
214
  initialized = false;
127
215
  frameRateView;
128
- camera;
129
216
  device = null;
130
217
  pipelines = null;
131
218
  renderInfo = null;
132
219
  resizeEvents;
133
- constructor(idOrCanvasRef, camera = null, showFrameRate = false) {
220
+ constructor(idOrCanvasRef, sceneCamera = null, showFrameRate = false) {
221
+ super();
134
222
  if (typeof idOrCanvasRef === 'string') {
135
223
  const ref = document.getElementById(idOrCanvasRef);
136
224
  if (ref !== null)
@@ -145,10 +233,9 @@ export class Simulation {
145
233
  throw logger.error(`Canvas ref/id provided is invalid`);
146
234
  }
147
235
  const parent = this.canvasRef.parentElement;
148
- if (!camera)
149
- this.camera = new Camera(vector3());
150
- else
151
- this.camera = camera;
236
+ if (sceneCamera) {
237
+ camera = sceneCamera;
238
+ }
152
239
  if (parent === null)
153
240
  throw logger.error('Canvas parent is null');
154
241
  this.resizeEvents = [];
@@ -175,10 +262,24 @@ export class Simulation {
175
262
  return (this.canvasRef?.height || 0) / devicePixelRatio;
176
263
  }
177
264
  add(el, id) {
178
- addObject(this.scene, el, this.device, id);
265
+ if (el instanceof SimulationElement3d) {
266
+ if (this.device !== null) {
267
+ el.propagateDevice(this.device);
268
+ }
269
+ const obj = new SimSceneObjInfo(el, id);
270
+ this.scene.unshift(obj);
271
+ }
272
+ else {
273
+ throw logger.error('Cannot add invalid SimulationElement');
274
+ }
179
275
  }
180
276
  remove(el) {
181
- removeObject(this.scene, el);
277
+ for (let i = 0; i < this.scene.length; i++) {
278
+ if (this.scene[i].getObj() === el) {
279
+ this.scene.splice(i, 1);
280
+ break;
281
+ }
282
+ }
182
283
  }
183
284
  removeId(id) {
184
285
  removeObjectId(this.scene, id);
@@ -230,16 +331,14 @@ export class Simulation {
230
331
  format: 'bgra8unorm'
231
332
  });
232
333
  const screenSize = vector2(this.canvasRef.width, this.canvasRef.height);
233
- this.camera.setScreenSize(screenSize);
334
+ camera.setScreenSize(screenSize);
234
335
  this.render(ctx);
235
336
  })();
236
337
  }
237
338
  propagateDevice(device) {
238
339
  for (let i = 0; i < this.scene.length; i++) {
239
340
  const el = this.scene[i].getObj();
240
- if (el instanceof Instance || el instanceof SceneCollection) {
241
- el.setDevice(device);
242
- }
341
+ el.propagateDevice(device);
243
342
  }
244
343
  }
245
344
  stop() {
@@ -268,64 +367,21 @@ export class Simulation {
268
367
  format: presentationFormat,
269
368
  alphaMode: 'premultiplied'
270
369
  });
271
- const uniformBufferSize = 4 * 16 + 4 * 16 + 4 * 2 + 8; // 4x4 matrix + 4x4 matrix + vec2<f32> + 8 bc 144 is cool
272
- const uniformBuffer = device.createBuffer({
273
- size: uniformBufferSize,
274
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
275
- });
276
370
  const instanceBuffer = device.createBuffer({
277
371
  size: 10 * 4 * 16,
278
372
  usage: GPUBufferUsage.STORAGE
279
373
  });
280
- const bindGroupLayout = device.createBindGroupLayout({
281
- entries: [
282
- {
283
- binding: 0,
284
- visibility: GPUShaderStage.VERTEX,
285
- buffer: {
286
- type: 'uniform'
287
- }
288
- },
289
- {
290
- binding: 1,
291
- visibility: GPUShaderStage.VERTEX,
292
- buffer: {
293
- type: 'read-only-storage'
294
- }
295
- }
296
- ]
297
- });
374
+ const bindGroupLayout = device.createBindGroupLayout(baseBindGroupLayout);
298
375
  this.renderInfo = {
299
- uniformBuffer,
300
376
  bindGroupLayout,
301
377
  instanceBuffer,
302
378
  vertexBuffer: null
303
379
  };
304
380
  this.pipelines = {
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')
381
+ triangleList: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'triangle-list'),
382
+ triangleStrip: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'triangle-strip'),
383
+ lineStrip: createPipeline(device, shaderModule, [bindGroupLayout], presentationFormat, 'line-strip')
311
384
  };
312
- const uniformBindGroup = device.createBindGroup({
313
- layout: bindGroupLayout,
314
- entries: [
315
- {
316
- binding: 0,
317
- resource: {
318
- buffer: uniformBuffer
319
- }
320
- },
321
- {
322
- binding: 1,
323
- resource: {
324
- buffer: instanceBuffer
325
- }
326
- }
327
- ]
328
- });
329
385
  const colorAttachment = {
330
386
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
331
387
  // @ts-ignore
@@ -334,18 +390,13 @@ export class Simulation {
334
390
  loadOp: 'clear',
335
391
  storeOp: 'store'
336
392
  };
337
- let aspect = canvas.width / canvas.height;
338
- let projectionMatrix = buildProjectionMatrix(aspect);
339
- let modelViewProjectionMatrix;
340
- let orthoMatrix;
341
- const updateModelViewProjectionMatrix = () => {
342
- modelViewProjectionMatrix = getTransformationMatrix(this.camera.getPos(), this.camera.getRotation(), projectionMatrix);
343
- };
344
- updateModelViewProjectionMatrix();
345
- const updateOrthoMatrix = () => {
346
- orthoMatrix = getOrthoMatrix(this.camera.getScreenSize());
347
- };
348
- updateOrthoMatrix();
393
+ const newAspectRatio = canvas.width / canvas.height;
394
+ if (newAspectRatio !== aspectRatio) {
395
+ updateProjectionMatrix(projMat, newAspectRatio);
396
+ aspectRatio = newAspectRatio;
397
+ }
398
+ updateWorldProjectionMatrix(worldProjMat, projMat);
399
+ updateOrthoProjectionMatrix(orthoMatrix, camera.getScreenSize());
349
400
  let multisampleTexture = buildMultisampleTexture(device, ctx, canvas.width, canvas.height);
350
401
  let depthTexture = buildDepthTexture(device, canvas.width, canvas.height);
351
402
  const renderPassDescriptor = {
@@ -378,14 +429,14 @@ export class Simulation {
378
429
  prevFps = fps;
379
430
  canvas.width = canvas.clientWidth * devicePixelRatio;
380
431
  canvas.height = canvas.clientHeight * devicePixelRatio;
381
- const screenSize = this.camera.getScreenSize();
432
+ const screenSize = camera.getScreenSize();
382
433
  if (screenSize[0] !== canvas.width || screenSize[1] !== canvas.height) {
383
- this.camera.setScreenSize(vector2(canvas.width, canvas.height));
434
+ camera.setScreenSize(vector2(canvas.width, canvas.height));
384
435
  screenSize[0] = canvas.width;
385
436
  screenSize[1] = canvas.height;
386
- aspect = this.camera.getAspectRatio();
387
- projectionMatrix = buildProjectionMatrix(aspect);
388
- updateModelViewProjectionMatrix();
437
+ aspectRatio = camera.getAspectRatio();
438
+ updateProjectionMatrix(projMat, aspectRatio);
439
+ updateWorldProjectionMatrix(worldProjMat, projMat);
389
440
  multisampleTexture = buildMultisampleTexture(device, ctx, screenSize[0], screenSize[1]);
390
441
  depthTexture = buildDepthTexture(device, screenSize[0], screenSize[1]);
391
442
  renderPassDescriptor.depthStencilAttachment.view = depthTexture.createView();
@@ -396,17 +447,13 @@ export class Simulation {
396
447
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
397
448
  // @ts-ignore
398
449
  renderPassDescriptor.colorAttachments[0].resolveTarget = ctx.getCurrentTexture().createView();
399
- if (this.camera.hasUpdated()) {
400
- updateOrthoMatrix();
401
- updateModelViewProjectionMatrix();
450
+ if (camera.hasUpdated()) {
451
+ updateOrthoProjectionMatrix(orthoMatrix, camera.getScreenSize());
452
+ updateWorldProjectionMatrix(worldProjMat, projMat);
402
453
  }
403
- device.queue.writeBuffer(uniformBuffer, 0, modelViewProjectionMatrix.buffer, modelViewProjectionMatrix.byteOffset, modelViewProjectionMatrix.byteLength);
404
- device.queue.writeBuffer(uniformBuffer, 4 * 16, // 4x4 matrix
405
- orthoMatrix.buffer, orthoMatrix.byteOffset, orthoMatrix.byteLength);
406
454
  const commandEncoder = device.createCommandEncoder();
407
455
  const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
408
- passEncoder.setPipeline(this.pipelines.triangleList3d);
409
- passEncoder.setBindGroup(0, uniformBindGroup);
456
+ passEncoder.setPipeline(this.pipelines.triangleList);
410
457
  const totalVertices = getTotalVertices(this.scene);
411
458
  if (this.renderInfo.vertexBuffer === null ||
412
459
  this.renderInfo.vertexBuffer.size / (4 * BUF_LEN) < totalVertices) {
@@ -416,7 +463,7 @@ export class Simulation {
416
463
  });
417
464
  }
418
465
  this.renderScene(device, passEncoder, this.renderInfo.vertexBuffer, this.scene, 0, diff);
419
- this.camera.updateConsumed();
466
+ camera.updateConsumed();
420
467
  passEncoder.end();
421
468
  device.queue.submit([commandEncoder.finish()]);
422
469
  };
@@ -438,7 +485,7 @@ export class Simulation {
438
485
  scene[i].traverseLife(diff);
439
486
  }
440
487
  const obj = scene[i].getObj();
441
- if (obj instanceof SceneCollection) {
488
+ if (obj.hasChildren()) {
442
489
  let shaderInfo = undefined;
443
490
  if (obj instanceof ShaderGroup) {
444
491
  const pipeline = obj.getPipeline();
@@ -448,76 +495,70 @@ export class Simulation {
448
495
  paramGenerator: obj.getVertexParamGenerator(),
449
496
  bufferInfo: obj.hasBindGroup()
450
497
  ? {
451
- buffers: obj.getBindGroupBuffers(),
498
+ buffers: obj.getBindGroupBuffers(device),
452
499
  layout: obj.getBindGroupLayout()
453
500
  }
454
501
  : null
455
502
  };
456
503
  }
457
504
  }
458
- currentOffset += this.renderScene(device, passEncoder, vertexBuffer, obj.getScene(), currentOffset, diff, shaderInfo || undefined);
459
- continue;
505
+ currentOffset += this.renderScene(device, passEncoder, vertexBuffer, obj.getChildrenInfos(), currentOffset, diff, shaderInfo);
460
506
  }
461
- const buffer = new Float32Array(obj.getBuffer(this.camera, shaderInfo?.paramGenerator));
507
+ if (obj.isEmpty)
508
+ continue;
509
+ const buffer = new Float32Array(obj.getBuffer(shaderInfo?.paramGenerator));
462
510
  const bufLen = shaderInfo?.paramGenerator?.bufferSize || BUF_LEN;
463
511
  const vertexCount = buffer.length / bufLen;
464
- device.queue.writeBuffer(vertexBuffer, currentOffset, buffer);
512
+ device.queue.writeBuffer(vertexBuffer, currentOffset, buffer.buffer, buffer.byteOffset, buffer.byteLength);
465
513
  vertexBuffer.unmap();
466
- const is3d = Boolean(obj.is3d);
514
+ passEncoder.setVertexBuffer(0, vertexBuffer, currentOffset, buffer.byteLength);
515
+ const modelMatrix = obj.getModelMatrix();
516
+ const uniformBuffer = obj.getUniformBuffer(device, modelMatrix);
517
+ const projBuf = obj.is3d ? worldProjMat : orthoMatrix;
518
+ device.queue.writeBuffer(uniformBuffer, worldProjMatOffset, projBuf.buffer, projBuf.byteOffset, projBuf.byteLength);
467
519
  if (shaderInfo) {
468
520
  passEncoder.setPipeline(shaderInfo.pipeline);
469
521
  }
470
522
  else if (obj.isWireframe()) {
471
- if (is3d) {
472
- passEncoder.setPipeline(this.pipelines.lineStrip3d);
473
- }
474
- else {
475
- passEncoder.setPipeline(this.pipelines.lineStrip2d);
476
- }
523
+ passEncoder.setPipeline(this.pipelines.lineStrip);
477
524
  }
478
525
  else {
479
526
  const type = obj.getGeometryType();
480
527
  if (type === 'strip') {
481
- if (is3d) {
482
- passEncoder.setPipeline(this.pipelines.triangleStrip3d);
483
- }
484
- else {
485
- passEncoder.setPipeline(this.pipelines.triangleStrip2d);
486
- }
528
+ passEncoder.setPipeline(this.pipelines.triangleStrip);
487
529
  }
488
530
  else if (type === 'list') {
489
- if (is3d) {
490
- passEncoder.setPipeline(this.pipelines.triangleList3d);
491
- }
492
- else {
493
- passEncoder.setPipeline(this.pipelines.triangleList2d);
494
- }
531
+ passEncoder.setPipeline(this.pipelines.triangleList);
495
532
  }
496
533
  }
497
534
  let instances = 1;
498
- if (obj instanceof Instance) {
499
- instances = obj.getNumInstances();
500
- const buf = obj.getMatrixBuffer();
501
- if (buf && this.renderInfo) {
502
- const uniformBindGroup = device.createBindGroup({
503
- layout: this.renderInfo.bindGroupLayout,
504
- entries: [
505
- {
506
- binding: 0,
507
- resource: {
508
- buffer: this.renderInfo.uniformBuffer
509
- }
510
- },
511
- {
512
- binding: 1,
513
- resource: {
514
- buffer: buf
515
- }
516
- }
517
- ]
518
- });
519
- passEncoder.setBindGroup(0, uniformBindGroup);
535
+ if (this.renderInfo) {
536
+ let instanceBuffer;
537
+ if (obj.isInstance) {
538
+ instances = obj.getNumInstances();
539
+ instanceBuffer = obj.getMatrixBuffer(device);
520
540
  }
541
+ else {
542
+ instanceBuffer = this.renderInfo.instanceBuffer;
543
+ }
544
+ const uniformBindGroup = device.createBindGroup({
545
+ layout: this.renderInfo.bindGroupLayout,
546
+ entries: [
547
+ {
548
+ binding: 0,
549
+ resource: {
550
+ buffer: uniformBuffer
551
+ }
552
+ },
553
+ {
554
+ binding: 1,
555
+ resource: {
556
+ buffer: instanceBuffer
557
+ }
558
+ }
559
+ ]
560
+ });
561
+ passEncoder.setBindGroup(0, uniformBindGroup);
521
562
  }
522
563
  if (shaderInfo && shaderInfo.bufferInfo) {
523
564
  const bindGroupEntries = shaderInfo.bufferInfo.buffers.map((buffer, index) => ({
@@ -532,12 +573,12 @@ export class Simulation {
532
573
  });
533
574
  passEncoder.setBindGroup(1, bindGroup);
534
575
  }
535
- passEncoder.setVertexBuffer(0, vertexBuffer, currentOffset, buffer.byteLength);
576
+ // TODO maybe switch to drawIndexed
536
577
  passEncoder.draw(vertexCount, instances, 0, 0);
537
578
  currentOffset += buffer.byteLength;
538
579
  }
539
580
  for (let i = toRemove.length - 1; i >= 0; i--) {
540
- removeObject(scene, scene[i].getObj());
581
+ this.remove(scene[i].getObj());
541
582
  }
542
583
  return currentOffset - startOffset;
543
584
  }
@@ -553,189 +594,17 @@ export class Simulation {
553
594
  }
554
595
  }
555
596
  }
556
- export class SceneCollection extends SimulationElement3d {
557
- geometry;
558
- name;
559
- scene;
560
- device = null;
561
- constructor(name) {
562
- super(vector3());
563
- this.wireframe = false;
564
- this.name = name || null;
565
- this.scene = [];
566
- this.geometry = new BlankGeometry();
567
- }
568
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
569
- setWireframe(_) { }
570
- getName() {
571
- return this.name;
572
- }
573
- getScene() {
574
- return this.scene;
575
- }
576
- setDevice(device) {
577
- this.device = device;
578
- this.propagateDevice(device);
579
- }
580
- propagateDevice(device) {
581
- for (let i = 0; i < this.scene.length; i++) {
582
- const el = this.scene[i].getObj();
583
- if (el instanceof Instance || el instanceof SceneCollection) {
584
- el.setDevice(device);
585
- }
586
- }
587
- }
588
- getVertexCount() {
589
- let total = 0;
590
- for (let i = 0; i < this.scene.length; i++) {
591
- total += this.scene[i].getObj().getVertexCount();
592
- }
593
- return total;
594
- }
595
- getSceneObjects() {
596
- return this.scene.map((item) => item.getObj());
597
- }
598
- setSceneObjects(newScene) {
599
- this.scene = toSceneObjInfoMany(newScene);
600
- }
601
- setScene(newScene) {
602
- this.scene = newScene;
603
- }
604
- add(el, id) {
605
- addObject(this.scene, el, this.device, id);
606
- }
607
- remove(el) {
608
- removeObject(this.scene, el);
609
- }
610
- removeId(id) {
611
- removeObjectId(this.scene, id);
612
- }
613
- /**
614
- * @param lifetime - ms
615
- */
616
- setLifetime(el, lifetime) {
617
- for (let i = 0; i < this.scene.length; i++) {
618
- if (this.scene[i].getObj() === el)
619
- this.scene[i].setLifetime(lifetime);
620
- }
621
- }
622
- empty() {
623
- this.scene = [];
624
- }
625
- getSceneBuffer(camera) {
626
- return this.scene.map((item) => item.getObj().getBuffer(camera)).flat();
627
- }
628
- // TODO - improve
629
- getWireframe(camera) {
630
- return this.getSceneBuffer(camera);
631
- }
632
- // TODO - improve
633
- getTriangles(camera) {
634
- return this.getSceneBuffer(camera);
635
- }
636
- updateMatrix(camera) {
637
- this.defaultUpdateMatrix(camera);
638
- }
639
- }
640
- export class Camera {
641
- pos;
642
- rotation;
643
- aspectRatio = 1;
644
- updated;
645
- screenSize = vector2();
646
- constructor(pos, rotation = vector3()) {
647
- this.pos = pos;
648
- this.updated = false;
649
- this.rotation = rotation;
650
- }
651
- setScreenSize(size) {
652
- this.screenSize = size;
653
- this.aspectRatio = size[0] / size[1];
654
- this.updated = true;
655
- }
656
- getScreenSize() {
657
- return this.screenSize;
658
- }
659
- hasUpdated() {
660
- return this.updated;
661
- }
662
- updateConsumed() {
663
- this.updated = false;
664
- }
665
- move(amount, t = 0, f) {
666
- const initial = vector3();
667
- vec3.clone(this.pos, initial);
668
- return transitionValues((p) => {
669
- const x = amount[0] * p;
670
- const y = amount[1] * p;
671
- const z = amount[2] * p;
672
- const diff = vector3(x, y, z);
673
- vec3.add(this.pos, diff, this.pos);
674
- }, () => {
675
- vec3.add(initial, amount, this.pos);
676
- }, t, f);
677
- }
678
- moveTo(pos, t = 0, f) {
679
- const diff = vector3();
680
- vec3.sub(pos, this.pos, diff);
681
- return transitionValues((p) => {
682
- const x = diff[0] * p;
683
- const y = diff[1] * p;
684
- const z = diff[2] * p;
685
- const amount = vector3(x, y, z);
686
- vec3.add(this.pos, amount, this.pos);
687
- }, () => {
688
- vec3.clone(pos, this.pos);
689
- }, t, f);
690
- }
691
- rotateTo(value, t = 0, f) {
692
- const diff = vec3.clone(value);
693
- vec3.sub(diff, diff, this.rotation);
694
- return transitionValues((p) => {
695
- const x = diff[0] * p;
696
- const y = diff[1] * p;
697
- const z = diff[2] * p;
698
- vec3.add(this.rotation, this.rotation, vector3(x, y, z));
699
- this.updated = true;
700
- }, () => {
701
- this.rotation = value;
702
- }, t, f);
703
- }
704
- rotate(amount, t = 0, f) {
705
- const initial = vector3();
706
- vec3.clone(this.rotation, initial);
707
- return transitionValues((p) => {
708
- const x = amount[0] * p;
709
- const y = amount[1] * p;
710
- const z = amount[2] * p;
711
- vec3.add(this.rotation, vector3(x, y, z), this.rotation);
712
- this.updated = true;
713
- }, () => {
714
- vec3.add(initial, amount, this.rotation);
715
- }, t, f);
716
- }
717
- getRotation() {
718
- return this.rotation;
719
- }
720
- getPos() {
721
- return this.pos;
722
- }
723
- getAspectRatio() {
724
- return this.aspectRatio;
725
- }
726
- }
727
597
  const defaultShaderCode = `
728
598
  struct Uniforms {
729
- modelViewProjectionMatrix : mat4x4<f32>,
730
- orthoProjectionMatrix : mat4x4<f32>
599
+ worldProjectionMatrix: mat4x4<f32>,
600
+ modelProjectionMatrix: mat4x4<f32>,
731
601
  }
732
602
 
733
- @group(0) @binding(0) var<uniform> uniforms : Uniforms;
603
+ @group(0) @binding(0) var<uniform> uniforms: Uniforms;
734
604
 
735
- @group(0) @binding(1) var<storage, read> instanceMatrices : array<mat4x4f>;
605
+ @group(0) @binding(1) var<storage> instanceMatrices: array<mat4x4f>;
736
606
  `;
737
- export class ShaderGroup extends SceneCollection {
738
- geometry;
607
+ export class ShaderGroup extends EmptyElement {
739
608
  code;
740
609
  module;
741
610
  pipeline;
@@ -758,28 +627,10 @@ export class ShaderGroup extends SceneCollection {
758
627
  this.vertexParams = vertexParams;
759
628
  this.valueBuffers = null;
760
629
  }
761
- propagateDevice(device) {
762
- super.propagateDevice(device);
630
+ onDeviceChange(device) {
763
631
  this.module = device.createShaderModule({ code: this.code });
764
632
  const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
765
- const bindGroupLayout = device.createBindGroupLayout({
766
- entries: [
767
- {
768
- binding: 0,
769
- visibility: GPUShaderStage.VERTEX,
770
- buffer: {
771
- type: 'uniform'
772
- }
773
- },
774
- {
775
- binding: 1,
776
- visibility: GPUShaderStage.VERTEX,
777
- buffer: {
778
- type: 'read-only-storage'
779
- }
780
- }
781
- ]
782
- });
633
+ const bindGroupLayout = device.createBindGroupLayout(baseBindGroupLayout);
783
634
  const bindGroups = [bindGroupLayout];
784
635
  if (this.bindGroup !== null) {
785
636
  const entryValues = this.bindGroup.bindings.map((binding, index) => ({
@@ -792,7 +643,7 @@ export class ShaderGroup extends SceneCollection {
792
643
  });
793
644
  bindGroups.push(this.bindGroupLayout);
794
645
  }
795
- this.pipeline = createPipeline(device, this.module, bindGroups, presentationFormat, 'vertex_main_2d', this.topology, this.vertexParams);
646
+ this.pipeline = createPipeline(device, this.module, bindGroups, presentationFormat, this.topology, this.vertexParams);
796
647
  }
797
648
  getBindGroupLayout() {
798
649
  return this.bindGroupLayout;
@@ -800,16 +651,16 @@ export class ShaderGroup extends SceneCollection {
800
651
  getPipeline() {
801
652
  return this.pipeline;
802
653
  }
803
- getBindGroupBuffers() {
654
+ getBindGroupBuffers(device) {
804
655
  if (this.bindGroup === null)
805
656
  return null;
806
- if (this.device === null)
657
+ if (device === null)
807
658
  return null;
808
659
  const values = this.bindGroup.values();
809
660
  if (this.valueBuffers === null) {
810
661
  this.valueBuffers = [];
811
662
  for (let i = 0; i < values.length; i++) {
812
- const buffer = this.createBuffer(this.device, values[i]);
663
+ const buffer = this.createBuffer(device, values[i]);
813
664
  this.valueBuffers.push(buffer);
814
665
  }
815
666
  }
@@ -818,12 +669,12 @@ export class ShaderGroup extends SceneCollection {
818
669
  const arrayConstructor = values[i].array;
819
670
  const array = new arrayConstructor(values[i].value);
820
671
  if (array.byteLength > this.valueBuffers[i].size) {
821
- const newBuffer = this.createBuffer(this.device, values[i]);
672
+ const newBuffer = this.createBuffer(device, values[i]);
822
673
  this.valueBuffers[i].destroy();
823
674
  this.valueBuffers[i] = newBuffer;
824
675
  }
825
676
  else {
826
- this.device.queue.writeBuffer(this.valueBuffers[i], 0, array.buffer, array.byteOffset, array.byteLength);
677
+ device.queue.writeBuffer(this.valueBuffers[i], 0, array.buffer, array.byteOffset, array.byteLength);
827
678
  }
828
679
  }
829
680
  }
@@ -842,9 +693,6 @@ export class ShaderGroup extends SceneCollection {
842
693
  buffer.unmap();
843
694
  return buffer;
844
695
  }
845
- updateMatrix(camera) {
846
- this.defaultUpdateMatrix(camera);
847
- }
848
696
  getVertexParamGenerator() {
849
697
  return this.paramGenerator;
850
698
  }