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.
- package/TODO.md +15 -3
- package/dist/constants.d.ts +10 -4
- package/dist/constants.js +11 -4
- package/dist/geometry.d.ts +25 -9
- package/dist/geometry.js +85 -25
- package/dist/graphics.d.ts +91 -52
- package/dist/graphics.js +371 -252
- package/dist/internalUtils.d.ts +15 -11
- package/dist/internalUtils.js +73 -47
- package/dist/settings.d.ts +7 -0
- package/dist/settings.js +9 -0
- package/dist/simulation.d.ts +29 -59
- package/dist/simulation.js +231 -383
- package/dist/types.d.ts +13 -13
- package/dist/utils.d.ts +9 -8
- package/dist/utils.js +18 -20
- package/package.json +1 -1
package/dist/simulation.js
CHANGED
|
@@ -1,64 +1,42 @@
|
|
|
1
1
|
import { vec3 } from 'wgpu-matrix';
|
|
2
|
-
import {
|
|
3
|
-
import { BUF_LEN } from './constants.js';
|
|
4
|
-
import { Color,
|
|
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 {
|
|
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
|
-
|
|
10
|
-
|
|
10
|
+
worldProjectionMatrix: mat4x4<f32>,
|
|
11
|
+
modelProjectionMatrix: mat4x4<f32>,
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
@group(0) @binding(0) var<uniform> uniforms
|
|
14
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
14
15
|
|
|
15
|
-
@group(0) @binding(1) var<storage> instanceMatrices
|
|
16
|
+
@group(0) @binding(1) var<storage> instanceMatrices: array<mat4x4f>;
|
|
16
17
|
|
|
17
18
|
struct VertexOutput {
|
|
18
|
-
@builtin(position) Position
|
|
19
|
-
@location(0) fragUV
|
|
20
|
-
@location(1) fragColor
|
|
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
|
|
26
|
-
@builtin(instance_index) instanceIdx
|
|
27
|
-
@location(0) position
|
|
28
|
-
@location(1) color
|
|
29
|
-
@location(2) uv
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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 =
|
|
432
|
+
const screenSize = camera.getScreenSize();
|
|
382
433
|
if (screenSize[0] !== canvas.width || screenSize[1] !== canvas.height) {
|
|
383
|
-
|
|
434
|
+
camera.setScreenSize(vector2(canvas.width, canvas.height));
|
|
384
435
|
screenSize[0] = canvas.width;
|
|
385
436
|
screenSize[1] = canvas.height;
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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 (
|
|
400
|
-
|
|
401
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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.
|
|
459
|
-
continue;
|
|
505
|
+
currentOffset += this.renderScene(device, passEncoder, vertexBuffer, obj.getChildrenInfos(), currentOffset, diff, shaderInfo);
|
|
460
506
|
}
|
|
461
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
730
|
-
|
|
599
|
+
worldProjectionMatrix: mat4x4<f32>,
|
|
600
|
+
modelProjectionMatrix: mat4x4<f32>,
|
|
731
601
|
}
|
|
732
602
|
|
|
733
|
-
@group(0) @binding(0) var<uniform> uniforms
|
|
603
|
+
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
|
|
734
604
|
|
|
735
|
-
@group(0) @binding(1) var<storage
|
|
605
|
+
@group(0) @binding(1) var<storage> instanceMatrices: array<mat4x4f>;
|
|
736
606
|
`;
|
|
737
|
-
export class ShaderGroup extends
|
|
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
|
-
|
|
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,
|
|
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 (
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
}
|