matrix-engine-wgpu 1.0.6 → 1.1.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.
Files changed (71) hide show
  1. package/.codesandbox/tasks.json +46 -0
  2. package/.devcontainer/devcontainer.json +22 -0
  3. package/.github/dependabot.yml +12 -0
  4. package/REFERENCE.md +3 -5
  5. package/dev.md +460 -0
  6. package/empty.js +7 -6
  7. package/examples/games/jamb/jamb.js +1133 -0
  8. package/examples/load-obj-file.js +65 -28
  9. package/examples/unlit-textures.js +26 -23
  10. package/examples.js +35 -3
  11. package/index.js +8 -2
  12. package/main.js +454 -48
  13. package/non-project-files/dev.txt +21 -0
  14. package/non-project-files/image1.png +0 -0
  15. package/non-project-files/image6.png +0 -0
  16. package/package.json +31 -13
  17. package/public/app.js +2234 -114
  18. package/public/css/style.css +371 -110
  19. package/public/empty.html +1 -1
  20. package/public/empty.js +9887 -9264
  21. package/public/examples.html +10 -8
  22. package/public/examples.js +2035 -247
  23. package/public/index.html +3 -5
  24. package/public/manifest copy.web +35 -0
  25. package/public/res/audios/block.mp3 +0 -0
  26. package/public/res/audios/dice1.mp3 +0 -0
  27. package/public/res/audios/dice2.mp3 +0 -0
  28. package/public/res/audios/start.mp3 +0 -0
  29. package/public/res/meshes/jamb/bg.blend +0 -0
  30. package/public/res/meshes/jamb/bg.blend1 +0 -0
  31. package/public/res/meshes/jamb/bg.mtl +12 -0
  32. package/public/res/meshes/jamb/bg.obj +17 -0
  33. package/public/res/meshes/jamb/bg.png +0 -0
  34. package/public/res/meshes/jamb/dice-default.png +0 -0
  35. package/public/res/meshes/jamb/dice-mark.png +0 -0
  36. package/public/res/meshes/jamb/dice.mtl +12 -0
  37. package/public/res/meshes/jamb/dice.obj +40 -0
  38. package/public/res/meshes/jamb/dice.png +0 -0
  39. package/public/res/meshes/jamb/jamb-title.mtl +12 -0
  40. package/public/res/meshes/jamb/jamb-title.obj +26008 -0
  41. package/public/res/meshes/jamb/jamb.blend +0 -0
  42. package/public/res/meshes/jamb/jamb.blend1 +0 -0
  43. package/public/res/meshes/jamb/logo.png +0 -0
  44. package/public/res/meshes/jamb/nidzaDice.blend +0 -0
  45. package/public/res/meshes/jamb/nidzaDice.blend1 +0 -0
  46. package/public/res/meshes/jamb/pile.blend +0 -0
  47. package/public/res/meshes/jamb/simpleCube.blend +0 -0
  48. package/public/res/meshes/jamb/simpleCube.blend1 +0 -0
  49. package/public/res/meshes/jamb/sounds/roll1.wav +0 -0
  50. package/public/res/meshes/jamb/text.png +0 -0
  51. package/public/res/multilang/en.json +27 -0
  52. package/public/res/multilang/sr.json +27 -0
  53. package/public/test.html +636 -0
  54. package/public/three-test.js +165 -0
  55. package/public/worker.html +1 -1
  56. package/readme.md +193 -115
  57. package/src/engine/ball.js +477 -468
  58. package/src/engine/cube.js +486 -468
  59. package/src/engine/engine.js +4 -6
  60. package/src/engine/loader-obj.js +9 -6
  61. package/src/engine/matrix-class.js +237 -204
  62. package/src/engine/mesh-obj.js +603 -515
  63. package/src/engine/raycast.js +101 -0
  64. package/src/engine/utils.js +69 -3
  65. package/src/multilang/lang.js +35 -0
  66. package/src/physics/matrix-ammo.js +168 -15
  67. package/src/shaders/fragment.wgsl.js +4 -2
  68. package/src/shaders/shaders.js +1 -1
  69. package/src/shaders/vertexShadow.wgsl.js +1 -1
  70. package/src/sounds/sounds.js +47 -0
  71. package/src/world.js +311 -248
@@ -4,476 +4,494 @@ import {Position, Rotation} from "./matrix-class";
4
4
  import {createInputHandler} from "./engine";
5
5
 
6
6
  var SphereLayout = {
7
- vertexStride: 8 * 4,
8
- positionsOffset: 0,
9
- normalOffset: 3 * 4,
10
- uvOffset: 6 * 4,
7
+ vertexStride: 8 * 4,
8
+ positionsOffset: 0,
9
+ normalOffset: 3 * 4,
10
+ uvOffset: 6 * 4,
11
11
  };
12
12
 
13
13
  export default class MECube {
14
14
 
15
- constructor(canvas, device, context, o) {
16
- this.device = device;
17
- this.context = context;
18
- this.entityArgPass = o.entityArgPass;
19
- this.inputHandler = createInputHandler(window, canvas);
20
- this.cameras = o.cameras;
21
- console.log('passed : o.mainCameraParams.responseCoef ', o.mainCameraParams.responseCoef)
22
- this.mainCameraParams = {
23
- type: o.mainCameraParams.type,
24
- responseCoef: o.mainCameraParams.responseCoef
25
- } // | WASD 'arcball' };
26
-
27
- this.lastFrameMS = 0;
28
- this.shaderModule = device.createShaderModule({
29
- code: UNLIT_SHADER,
30
- });
31
-
32
- this.texturesPaths = [];
33
-
34
- o.texturesPaths.forEach((t) => {
35
- this.texturesPaths.push(t)
36
- })
37
-
38
- this.presentationFormat = navigator.gpu.getPreferredCanvasFormat();
39
- this.position = new Position(o.position.x, o.position.y, o.position.z);
40
- console.log('cube added on pos : ', this.position)
41
- this.rotation = new Rotation(o.rotation.x, o.rotation.y, o.rotation.z);
42
- this.rotation.rotationSpeed.x = o.rotationSpeed.x;
43
- this.rotation.rotationSpeed.y = o.rotationSpeed.y;
44
- this.rotation.rotationSpeed.z = o.rotationSpeed.z;
45
-
46
- this.scale = o.scale;
47
- this.pipeline = device.createRenderPipeline({
48
- layout: 'auto',
49
- vertex: {
50
- module: this.shaderModule,
51
- entryPoint: 'vertexMain',
52
- buffers: [
53
- {
54
- arrayStride: SphereLayout.vertexStride,
55
- attributes: [
56
- // position
57
- {shaderLocation: 0, offset: SphereLayout.positionsOffset, format: 'float32x3'},
58
- // normal
59
- {shaderLocation: 1, offset: SphereLayout.normalOffset, format: 'float32x3'},
60
- // uv
61
- {shaderLocation: 2, offset: SphereLayout.uvOffset, format: 'float32x2', },
62
- ],
63
- },
64
- ],
65
- },
66
- fragment: {
67
- module: this.shaderModule,
68
- entryPoint: 'fragmentMain',
69
- targets: [{format: this.presentationFormat, },],
70
- },
71
- primitive: {
72
- topology: 'triangle-list',
73
- // Backface culling since the sphere is solid piece of geometry.
74
- // Faces pointing away from the camera will be occluded by faces
75
- // pointing toward the camera.
76
- cullMode: 'back',
77
- },
78
- // Enable depth testing so that the fragment closest to the camera
79
- // is rendered in front.
80
- depthStencil: {
81
- depthWriteEnabled: true,
82
- depthCompare: 'less',
83
- format: 'depth24plus',
84
- },
85
- });
86
-
87
- this.depthTexture = device.createTexture({
88
- size: [canvas.width, canvas.height],
89
- format: 'depth24plus',
90
- usage: GPUTextureUsage.RENDER_ATTACHMENT,
91
- });
92
-
93
- this.uniformBufferSize = 4 * 16; // 4x4 matrix
94
- this.uniformBuffer = device.createBuffer({
95
- size: this.uniformBufferSize,
96
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
97
- });
98
-
99
- // Fetch the images and upload them into a GPUTexture.
100
- this.texture0 = null;
101
- this.moonTexture = null;
102
-
103
- this.settings = {
104
- useRenderBundles: true,
105
- asteroidCount: 15,
106
- };
107
-
108
- this.loadTex0(this.texturesPaths, device).then(() => {
109
- this.loadTex1(device).then(() => {
110
-
111
- this.sampler = device.createSampler({
112
- magFilter: 'linear',
113
- minFilter: 'linear',
114
- });
115
-
116
- this.transform = mat4.create();
117
- mat4.identity(this.transform);
118
-
119
- // Create one large central planet surrounded by a large ring of asteroids
120
- this.planet = this.createGeometry({
121
- scale: this.scale,
122
- useUVShema4x2: false
123
- });
124
- this.planet.bindGroup = this.createSphereBindGroup(this.texture0, this.transform);
125
-
126
- // can be used like instance draws
127
- var asteroids = [
128
- // this.createGeometry(0.2, 8, 6, 0.15),
129
- ];
130
-
131
- this.renderables = [this.planet];
132
- // this.ensureEnoughAsteroids(asteroids, this.transform);
133
- this.renderPassDescriptor = {
134
- colorAttachments: [
135
- {
136
- view: undefined,
137
- clearValue: {r: 0.0, g: 0.0, b: 0.0, a: 1.0},
138
- loadOp: this.entityArgPass.loadOp,
139
- storeOp: this.entityArgPass.storeOp,
140
- },
141
- ],
142
- depthStencilAttachment: {
143
- view: this.depthTexture.createView(),
144
- depthClearValue: 1.0,
145
- depthLoadOp: this.entityArgPass.depthLoadOp,
146
- depthStoreOp: this.entityArgPass.depthStoreOp,
147
- },
148
- };
149
-
150
- const aspect = canvas.width / canvas.height;
151
- this.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 1000.0);
152
- this.modelViewProjectionMatrix = mat4.create();
153
-
154
- this.frameBindGroup = device.createBindGroup({
155
- layout: this.pipeline.getBindGroupLayout(0),
156
- entries: [
157
- {
158
- binding: 0,
159
- resource: {buffer: this.uniformBuffer, },
160
- },
161
- ],
162
- });
163
-
164
- // The render bundle can be encoded once and re-used as many times as needed.
165
- // Because it encodes all of the commands needed to render at the GPU level,
166
- // those commands will not need to execute the associated JavaScript code upon
167
- // execution or be re-validated, which can represent a significant time savings.
168
- //
169
- // However, because render bundles are immutable once created, they are only
170
- // appropriate for rendering content where the same commands will be executed
171
- // every time, with the only changes being the contents of the buffers and
172
- // textures used. Cases where the executed commands differ from frame-to-frame,
173
- // such as when using frustrum or occlusion culling, will not benefit from
174
- // using render bundles as much.
175
- this.renderBundle;
176
- this.updateRenderBundle();
177
- })
178
- })
179
- }
180
-
181
- ensureEnoughAsteroids(asteroids, transform) {
182
- for(let i = this.renderables.length;i <= this.settings.asteroidCount;++i) {
183
- // Place copies of the asteroid in a ring.
184
- const radius = Math.random() * 1.7 + 1.25;
185
- const angle = Math.random() * Math.PI * 2;
186
- const x = Math.sin(angle) * radius;
187
- const y = (Math.random() - 0.5) * 0.015;
188
- const z = Math.cos(angle) * radius;
189
-
190
- mat4.identity(transform);
191
- mat4.translate(transform, [x, y, z], transform);
192
- mat4.rotateX(transform, Math.random() * Math.PI, transform);
193
- mat4.rotateY(transform, Math.random() * Math.PI, transform);
194
- this.renderables.push({
195
- ...asteroids[i % asteroids.length],
196
- bindGroup: this.createSphereBindGroup(this.moonTexture, transform),
197
- });
198
- }
199
- }
200
-
201
- updateRenderBundle() {
202
- console.log('[CUBE] updateRenderBundle')
203
- const renderBundleEncoder = this.device.createRenderBundleEncoder({
204
- colorFormats: [this.presentationFormat],
205
- depthStencilFormat: 'depth24plus',
206
- });
207
- this.renderScene(renderBundleEncoder);
208
- this.renderBundle = renderBundleEncoder.finish();
209
- }
210
-
211
- createGeometry(options) {
212
- const mesh = this.createCubeVertices(options);
213
- // Create a vertex buffer from the sphere data.
214
- const vertices = this.device.createBuffer({
215
- size: mesh.vertices.byteLength,
216
- usage: GPUBufferUsage.VERTEX,
217
- mappedAtCreation: true,
218
- });
219
- new Float32Array(vertices.getMappedRange()).set(mesh.vertices);
220
- vertices.unmap();
221
-
222
- const indices = this.device.createBuffer({
223
- size: mesh.indices.byteLength,
224
- usage: GPUBufferUsage.INDEX,
225
- mappedAtCreation: true,
226
- });
227
- new Uint16Array(indices.getMappedRange()).set(mesh.indices);
228
- indices.unmap();
229
-
230
- return {
231
- vertices,
232
- indices,
233
- indexCount: mesh.indices.length,
234
- };
235
- }
236
-
237
- createSphereBindGroup(texture, transform) {
238
-
239
- const uniformBufferSize = 4 * 16; // 4x4 matrix
240
- const uniformBuffer = this.device.createBuffer({
241
- size: uniformBufferSize,
242
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
243
- mappedAtCreation: true,
244
- });
245
- new Float32Array(uniformBuffer.getMappedRange()).set(transform);
246
- uniformBuffer.unmap();
247
-
248
- const bindGroup = this.device.createBindGroup({
249
- layout: this.pipeline.getBindGroupLayout(1),
250
- entries: [
251
- {
252
- binding: 0,
253
- resource: {
254
- buffer: uniformBuffer,
255
- },
256
- },
257
- {
258
- binding: 1,
259
- resource: this.sampler,
260
- },
261
- {
262
- binding: 2,
263
- resource: texture.createView(),
264
- },
265
- ],
266
- });
267
-
268
- return bindGroup;
269
- }
270
-
271
- getTransformationMatrix(pos) {
272
- const now = Date.now();
273
- const deltaTime = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
274
- this.lastFrameMS = now;
275
-
276
- // const viewMatrix = mat4.identity(); ORI
277
- const camera = this.cameras[this.mainCameraParams.type];
278
- const viewMatrix = camera.update(deltaTime, this.inputHandler());
279
-
280
- mat4.translate(viewMatrix, vec3.fromValues(pos.x, pos.y, pos.z), viewMatrix);
281
- mat4.rotateX(viewMatrix, Math.PI * this.rotation.getRotX(), viewMatrix);
282
- mat4.rotateY(viewMatrix, Math.PI * this.rotation.getRotY(), viewMatrix);
283
- mat4.rotateZ(viewMatrix, Math.PI * this.rotation.getRotZ(), viewMatrix);
284
- mat4.multiply(this.projectionMatrix, viewMatrix, this.modelViewProjectionMatrix);
285
-
286
- return this.modelViewProjectionMatrix;
287
- }
288
-
289
- async loadTex1(device) {
290
- return new Promise(async (resolve) => {
291
- const response = await fetch('./res/textures/tex1.jpg');
292
- const imageBitmap = await createImageBitmap(await response.blob());
293
- this.moonTexture = device.createTexture({
294
- size: [imageBitmap.width, imageBitmap.height, 1],
295
- format: 'rgba8unorm',
296
- usage:
297
- GPUTextureUsage.TEXTURE_BINDING |
298
- GPUTextureUsage.COPY_DST |
299
- GPUTextureUsage.RENDER_ATTACHMENT,
300
- });
301
- var moonTexture = this.moonTexture
302
- device.queue.copyExternalImageToTexture(
303
- {source: imageBitmap},
304
- {texture: moonTexture},
305
- [imageBitmap.width, imageBitmap.height]
306
- );
307
- resolve()
308
- })
309
- }
310
-
311
- async loadTex0(texturesPaths, device) {
312
- return new Promise(async (resolve) => {
313
- const response = await fetch(texturesPaths[0]);
314
- const imageBitmap = await createImageBitmap(await response.blob());
315
- console.log('WHAT IS THIS ', this)
316
- this.texture0 = device.createTexture({
317
- size: [imageBitmap.width, imageBitmap.height, 1],
318
- format: 'rgba8unorm',
319
- usage:
320
- GPUTextureUsage.TEXTURE_BINDING |
321
- GPUTextureUsage.COPY_DST |
322
- GPUTextureUsage.RENDER_ATTACHMENT,
323
- });
324
- var texture0 = this.texture0
325
- device.queue.copyExternalImageToTexture(
326
- {source: imageBitmap},
327
- {texture: texture0},
328
- [imageBitmap.width, imageBitmap.height]
329
- );
330
- resolve()
331
- })
332
-
333
- }
334
-
335
-
336
- // Render bundles function as partial, limited render passes, so we can use the
337
- // same code both to render the scene normally and to build the render bundle.
338
- renderScene(passEncoder) {
339
-
340
- if(typeof this.renderables === 'undefined') return;
341
-
342
- passEncoder.setPipeline(this.pipeline);
343
- passEncoder.setBindGroup(0, this.frameBindGroup);
344
-
345
- // Loop through every renderable object and draw them individually.
346
- // (Because many of these meshes are repeated, with only the transforms
347
- // differing, instancing would be highly effective here. This sample
348
- // intentionally avoids using instancing in order to emulate a more complex
349
- // scene, which helps demonstrate the potential time savings a render bundle
350
- // can provide.)
351
- let count = 0;
352
- for(const renderable of this.renderables) {
353
- passEncoder.setBindGroup(1, renderable.bindGroup);
354
- passEncoder.setVertexBuffer(0, renderable.vertices);
355
- passEncoder.setIndexBuffer(renderable.indices, 'uint16');
356
- passEncoder.drawIndexed(renderable.indexCount);
357
- if(++count > this.settings.asteroidCount) {
358
- break;
359
- }
360
- }
361
- }
362
-
363
- createCubeVertices(options) {
364
- if(typeof options === 'undefined') {
365
- var options = {
366
- scale: 1,
367
- useUVShema4x2: false
368
- }
369
- }
370
- if(typeof options.scale === 'undefined') options.scale = 1;
371
-
372
- let vertices;
373
- if(options.useUVShema4x2 == true) {
374
- vertices = new Float32Array([
375
- // position | texture coordinate
376
- //-------------+----------------------
377
- // front face select the top left image 1, 0.5,
378
- -1, 1, 1, 1, 0, 0, 0, 0,
379
- -1, -1, 1, 1, 0, 0, 0, 0.5,
380
- 1, 1, 1, 1, 0, 0, 0.25, 0,
381
- 1, -1, 1, 1, 0, 0, 0.25, 0.5,
382
- // right face select the top middle image
383
- 1, 1, -1, 1, 0, 0, 0.25, 0,
384
- 1, 1, 1, 1, 0, 0, 0.5, 0,
385
- 1, -1, -1, 1, 0, 0, 0.25, 0.5,
386
- 1, -1, 1, 1, 0, 0, 0.5, 0.5,
387
- // back face select to top right image
388
- 1, 1, -1, 1, 0, 0, 0.5, 0,
389
- 1, -1, -1, 1, 0, 0, 0.5, 0.5,
390
- -1, 1, -1, 1, 0, 0, 0.75, 0,
391
- -1, -1, -1, 1, 0, 0, 0.75, 0.5,
392
- // left face select the bottom left image
393
- -1, 1, 1, 1, 0, 0, 0, 0.5,
394
- -1, 1, -1, 1, 0, 0, 0.25, 0.5,
395
- -1, -1, 1, 1, 0, 0, 0, 1,
396
- -1, -1, -1, 1, 0, 0, 0.25, 1,
397
- // bottom face select the bottom middle image
398
- 1, -1, 1, 1, 0, 0, 0.25, 0.5,
399
- -1, -1, 1, 1, 0, 0, 0.5, 0.5,
400
- 1, -1, -1, 1, 0, 0, 0.25, 1,
401
- -1, -1, -1, 1, 0, 0, 0.5, 1,
402
- // top face select the bottom right image
403
- -1, 1, 1, 1, 0, 0, 0.5, 0.5,
404
- 1, 1, 1, 1, 0, 0, 0.75, 0.5,
405
- -1, 1, -1, 1, 0, 0, 0.5, 1,
406
- 1, 1, -1, 1, 0, 0, 0.75, 1,
407
- ]);
408
- } else {
409
- vertices = new Float32Array([
410
- // position | texture coordinate
411
- //------------- +----------------------
412
- // front face select the top left image 1, 0.5,
413
- -1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
414
- -1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
415
- 1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 0,
416
- 1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 1,
417
- // right face select the top middle image
418
- 1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 0,
419
- 1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
420
- 1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
421
- 1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 1,
422
- // back face select to top right image
423
- 1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 0,
424
- 1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 1,
425
- -1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
426
- -1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
427
- // left face select the bottom left image
428
- -1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
429
- -1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 1,
430
- -1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 0,
431
- -1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
432
- // bottom face select the bottom middle image
433
- 1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
434
- -1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
435
- 1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
436
- -1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
437
- // top face select the bottom right image
438
- -1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
439
- 1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
440
- -1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
441
- 1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
442
- ])
443
- }
444
-
445
-
446
- const indices = new Uint16Array([
447
- 0, 1, 2, 2, 1, 3, // front
448
- 4, 5, 6, 6, 5, 7, // right
449
- 8, 9, 10, 10, 9, 11, // back
450
- 12, 13, 14, 14, 13, 15, // left
451
- 16, 17, 18, 18, 17, 19, // bottom
452
- 20, 21, 22, 22, 21, 23, // top
453
- ]);
454
-
455
- return {
456
- vertices,
457
- indices,
458
- numVertices: indices.length,
459
- };
460
- }
461
-
462
- draw = () => {
463
- if(this.moonTexture == null) {
464
- console.log('not ready')
465
- return;
466
- }
467
- const transformationMatrix = this.getTransformationMatrix(this.position);
468
- this.device.queue.writeBuffer(
469
- this.uniformBuffer,
470
- 0,
471
- transformationMatrix.buffer,
472
- transformationMatrix.byteOffset,
473
- transformationMatrix.byteLength
474
- );
475
- this.renderPassDescriptor.colorAttachments[0].view = this.context
476
- .getCurrentTexture()
477
- .createView();
478
- }
15
+ constructor(canvas, device, context, o) {
16
+ this.device = device;
17
+ this.context = context;
18
+ this.entityArgPass = o.entityArgPass;
19
+ this.inputHandler = createInputHandler(window, canvas);
20
+ this.cameras = o.cameras;
21
+ console.log('passed : o.mainCameraParams.responseCoef ', o.mainCameraParams.responseCoef)
22
+ this.mainCameraParams = {
23
+ type: o.mainCameraParams.type,
24
+ responseCoef: o.mainCameraParams.responseCoef
25
+ } // | WASD 'arcball' };
26
+
27
+ this.lastFrameMS = 0;
28
+ this.shaderModule = device.createShaderModule({
29
+ code: UNLIT_SHADER,
30
+ });
31
+
32
+ this.texturesPaths = [];
33
+
34
+ if(typeof o.raycast === 'undefined') {
35
+ this.raycast = {
36
+ enabled: false,
37
+ radius: 2
38
+ };
39
+ } else {
40
+ this.raycast = o.raycast;
41
+ }
42
+
43
+ // useUVShema4x2 pass this from top !
44
+
45
+ o.texturesPaths.forEach((t) => {
46
+ this.texturesPaths.push(t)
47
+ })
48
+
49
+ this.presentationFormat = navigator.gpu.getPreferredCanvasFormat();
50
+ this.position = new Position(o.position.x, o.position.y, o.position.z);
51
+ console.log('cube added on pos : ', this.position)
52
+ this.rotation = new Rotation(o.rotation.x, o.rotation.y, o.rotation.z);
53
+ this.rotation.rotationSpeed.x = o.rotationSpeed.x;
54
+ this.rotation.rotationSpeed.y = o.rotationSpeed.y;
55
+ this.rotation.rotationSpeed.z = o.rotationSpeed.z;
56
+
57
+ this.scale = o.scale;
58
+ this.pipeline = device.createRenderPipeline({
59
+ layout: 'auto',
60
+ vertex: {
61
+ module: this.shaderModule,
62
+ entryPoint: 'vertexMain',
63
+ buffers: [
64
+ {
65
+ arrayStride: SphereLayout.vertexStride,
66
+ attributes: [
67
+ // position
68
+ {shaderLocation: 0, offset: SphereLayout.positionsOffset, format: 'float32x3'},
69
+ // normal
70
+ {shaderLocation: 1, offset: SphereLayout.normalOffset, format: 'float32x3'},
71
+ // uv
72
+ {shaderLocation: 2, offset: SphereLayout.uvOffset, format: 'float32x2', },
73
+ ],
74
+ },
75
+ ],
76
+ },
77
+ fragment: {
78
+ module: this.shaderModule,
79
+ entryPoint: 'fragmentMain',
80
+ targets: [{format: this.presentationFormat, },],
81
+ },
82
+ primitive: {
83
+ topology: 'triangle-list',
84
+ // Backface culling since the sphere is solid piece of geometry.
85
+ // Faces pointing away from the camera will be occluded by faces
86
+ // pointing toward the camera.
87
+ cullMode: 'back',
88
+ },
89
+ // Enable depth testing so that the fragment closest to the camera
90
+ // is rendered in front.
91
+ depthStencil: {
92
+ depthWriteEnabled: true,
93
+ depthCompare: 'less',
94
+ format: 'depth24plus',
95
+ },
96
+ });
97
+
98
+ this.depthTexture = device.createTexture({
99
+ size: [canvas.width, canvas.height],
100
+ format: 'depth24plus',
101
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
102
+ });
103
+
104
+ this.uniformBufferSize = 4 * 16; // 4x4 matrix
105
+ this.uniformBuffer = device.createBuffer({
106
+ size: this.uniformBufferSize,
107
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
108
+ });
109
+
110
+ // Fetch the images and upload them into a GPUTexture.
111
+ this.texture0 = null;
112
+ this.moonTexture = null;
113
+
114
+ this.settings = {
115
+ useRenderBundles: true,
116
+ asteroidCount: 15,
117
+ };
118
+
119
+ this.loadTex0(this.texturesPaths, device).then(() => {
120
+ this.loadTex1(this.texturesPaths, device).then(() => {
121
+
122
+ this.sampler = device.createSampler({
123
+ magFilter: 'linear',
124
+ minFilter: 'linear',
125
+ });
126
+
127
+ this.transform = mat4.create();
128
+ mat4.identity(this.transform);
129
+
130
+ this.planet = this.createGeometry({
131
+ scale: this.scale,
132
+ useUVShema4x2: false
133
+ });
134
+ this.planet.bindGroup = this.createSphereBindGroup(this.texture0, this.transform);
135
+
136
+ // can be used like instance draws
137
+ var asteroids = [
138
+ // this.createGeometry(0.2, 8, 6, 0.15),
139
+ ];
140
+
141
+ this.renderables = [this.planet];
142
+ // this.ensureEnoughAsteroids(asteroids, this.transform);
143
+ this.renderPassDescriptor = {
144
+ colorAttachments: [
145
+ {
146
+ view: undefined,
147
+ clearValue: {r: 0.0, g: 0.0, b: 0.0, a: 1.0},
148
+ loadOp: this.entityArgPass.loadOp,
149
+ storeOp: this.entityArgPass.storeOp,
150
+ },
151
+ ],
152
+ depthStencilAttachment: {
153
+ view: this.depthTexture.createView(),
154
+ depthClearValue: 1.0,
155
+ depthLoadOp: this.entityArgPass.depthLoadOp,
156
+ depthStoreOp: this.entityArgPass.depthStoreOp,
157
+ },
158
+ };
159
+
160
+ const aspect = canvas.width / canvas.height;
161
+ this.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 1000.0);
162
+ this.modelViewProjectionMatrix = mat4.create();
163
+
164
+ this.frameBindGroup = device.createBindGroup({
165
+ layout: this.pipeline.getBindGroupLayout(0),
166
+ entries: [
167
+ {
168
+ binding: 0,
169
+ resource: {buffer: this.uniformBuffer, },
170
+ },
171
+ ],
172
+ });
173
+
174
+ // The render bundle can be encoded once and re-used as many times as needed.
175
+ // Because it encodes all of the commands needed to render at the GPU level,
176
+ // those commands will not need to execute the associated JavaScript code upon
177
+ // execution or be re-validated, which can represent a significant time savings.
178
+ //
179
+ // However, because render bundles are immutable once created, they are only
180
+ // appropriate for rendering content where the same commands will be executed
181
+ // every time, with the only changes being the contents of the buffers and
182
+ // textures used. Cases where the executed commands differ from frame-to-frame,
183
+ // such as when using frustrum or occlusion culling, will not benefit from
184
+ // using render bundles as much.
185
+ this.renderBundle;
186
+ this.updateRenderBundle();
187
+ })
188
+ })
189
+ }
190
+
191
+ ensureEnoughAsteroids(asteroids, transform) {
192
+ for(let i = this.renderables.length;i <= this.settings.asteroidCount;++i) {
193
+ // Place copies of the asteroid in a ring.
194
+ const radius = Math.random() * 1.7 + 1.25;
195
+ const angle = Math.random() * Math.PI * 2;
196
+ const x = Math.sin(angle) * radius;
197
+ const y = (Math.random() - 0.5) * 0.015;
198
+ const z = Math.cos(angle) * radius;
199
+
200
+ mat4.identity(transform);
201
+ mat4.translate(transform, [x, y, z], transform);
202
+ mat4.rotateX(transform, Math.random() * Math.PI, transform);
203
+ mat4.rotateY(transform, Math.random() * Math.PI, transform);
204
+ this.renderables.push({
205
+ ...asteroids[i % asteroids.length],
206
+ bindGroup: this.createSphereBindGroup(this.moonTexture, transform),
207
+ });
208
+ }
209
+ }
210
+
211
+ updateRenderBundle() {
212
+ console.log('[CUBE] updateRenderBundle')
213
+ const renderBundleEncoder = this.device.createRenderBundleEncoder({
214
+ colorFormats: [this.presentationFormat],
215
+ depthStencilFormat: 'depth24plus',
216
+ });
217
+ this.renderScene(renderBundleEncoder);
218
+ this.renderBundle = renderBundleEncoder.finish();
219
+ }
220
+
221
+ createGeometry(options) {
222
+ const mesh = this.createCubeVertices(options);
223
+ // Create a vertex buffer from the sphere data.
224
+ const vertices = this.device.createBuffer({
225
+ size: mesh.vertices.byteLength,
226
+ usage: GPUBufferUsage.VERTEX,
227
+ mappedAtCreation: true,
228
+ });
229
+ new Float32Array(vertices.getMappedRange()).set(mesh.vertices);
230
+ vertices.unmap();
231
+
232
+ const indices = this.device.createBuffer({
233
+ size: mesh.indices.byteLength,
234
+ usage: GPUBufferUsage.INDEX,
235
+ mappedAtCreation: true,
236
+ });
237
+ new Uint16Array(indices.getMappedRange()).set(mesh.indices);
238
+ indices.unmap();
239
+
240
+ return {
241
+ vertices,
242
+ indices,
243
+ indexCount: mesh.indices.length,
244
+ };
245
+ }
246
+
247
+ createSphereBindGroup(texture, transform) {
248
+
249
+ const uniformBufferSize = 4 * 16; // 4x4 matrix
250
+ const uniformBuffer = this.device.createBuffer({
251
+ size: uniformBufferSize,
252
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
253
+ mappedAtCreation: true,
254
+ });
255
+ new Float32Array(uniformBuffer.getMappedRange()).set(transform);
256
+ uniformBuffer.unmap();
257
+
258
+ const bindGroup = this.device.createBindGroup({
259
+ layout: this.pipeline.getBindGroupLayout(1),
260
+ entries: [
261
+ {
262
+ binding: 0,
263
+ resource: {
264
+ buffer: uniformBuffer,
265
+ },
266
+ },
267
+ {
268
+ binding: 1,
269
+ resource: this.sampler,
270
+ },
271
+ {
272
+ binding: 2,
273
+ resource: texture.createView(),
274
+ },
275
+ ],
276
+ });
277
+
278
+ return bindGroup;
279
+ }
280
+
281
+
282
+ // TEST
283
+ getViewMatrix() {
284
+ const camera = this.cameras[this.mainCameraParams.type];
285
+ const viewMatrix = camera.update(deltaTime, this.inputHandler());
286
+ return viewMatrix;
287
+ }
288
+
289
+ getTransformationMatrix(pos) {
290
+ const now = Date.now();
291
+ const deltaTime = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
292
+ this.lastFrameMS = now;
293
+
294
+ // const viewMatrix = mat4.identity(); ORI
295
+ const camera = this.cameras[this.mainCameraParams.type];
296
+ const viewMatrix = camera.update(deltaTime, this.inputHandler());
297
+
298
+ mat4.translate(viewMatrix, vec3.fromValues(pos.x, pos.y, pos.z), viewMatrix);
299
+ mat4.rotateX(viewMatrix, Math.PI * this.rotation.getRotX(), viewMatrix);
300
+ mat4.rotateY(viewMatrix, Math.PI * this.rotation.getRotY(), viewMatrix);
301
+ mat4.rotateZ(viewMatrix, Math.PI * this.rotation.getRotZ(), viewMatrix);
302
+ mat4.multiply(this.projectionMatrix, viewMatrix, this.modelViewProjectionMatrix);
303
+
304
+ return this.modelViewProjectionMatrix;
305
+ }
306
+
307
+ async loadTex1(textPath, device) {
308
+ return new Promise(async (resolve) => {
309
+ const response = await fetch(textPath[0]);
310
+ const imageBitmap = await createImageBitmap(await response.blob());
311
+ this.moonTexture = device.createTexture({
312
+ size: [imageBitmap.width, imageBitmap.height, 1],
313
+ format: 'rgba8unorm',
314
+ usage:
315
+ GPUTextureUsage.TEXTURE_BINDING |
316
+ GPUTextureUsage.COPY_DST |
317
+ GPUTextureUsage.RENDER_ATTACHMENT,
318
+ });
319
+ var moonTexture = this.moonTexture
320
+ device.queue.copyExternalImageToTexture(
321
+ {source: imageBitmap},
322
+ {texture: moonTexture},
323
+ [imageBitmap.width, imageBitmap.height]
324
+ );
325
+ resolve()
326
+ })
327
+ }
328
+
329
+ async loadTex0(texturesPaths, device) {
330
+ return new Promise(async (resolve) => {
331
+ const response = await fetch(texturesPaths[0]);
332
+ const imageBitmap = await createImageBitmap(await response.blob());
333
+ console.log('WHAT IS THIS ', this)
334
+ this.texture0 = device.createTexture({
335
+ size: [imageBitmap.width, imageBitmap.height, 1],
336
+ format: 'rgba8unorm',
337
+ usage:
338
+ GPUTextureUsage.TEXTURE_BINDING |
339
+ GPUTextureUsage.COPY_DST |
340
+ GPUTextureUsage.RENDER_ATTACHMENT,
341
+ });
342
+ var texture0 = this.texture0
343
+ device.queue.copyExternalImageToTexture(
344
+ {source: imageBitmap},
345
+ {texture: texture0},
346
+ [imageBitmap.width, imageBitmap.height]
347
+ );
348
+ resolve()
349
+ })
350
+
351
+ }
352
+
353
+
354
+ // Render bundles function as partial, limited render passes, so we can use the
355
+ // same code both to render the scene normally and to build the render bundle.
356
+ renderScene(passEncoder) {
357
+
358
+ if(typeof this.renderables === 'undefined') return;
359
+
360
+ passEncoder.setPipeline(this.pipeline);
361
+ passEncoder.setBindGroup(0, this.frameBindGroup);
362
+
363
+ // Loop through every renderable object and draw them individually.
364
+ // (Because many of these meshes are repeated, with only the transforms
365
+ // differing, instancing would be highly effective here. This sample
366
+ // intentionally avoids using instancing in order to emulate a more complex
367
+ // scene, which helps demonstrate the potential time savings a render bundle
368
+ // can provide.)
369
+ let count = 0;
370
+ for(const renderable of this.renderables) {
371
+ passEncoder.setBindGroup(1, renderable.bindGroup);
372
+ passEncoder.setVertexBuffer(0, renderable.vertices);
373
+ passEncoder.setIndexBuffer(renderable.indices, 'uint16');
374
+ passEncoder.drawIndexed(renderable.indexCount);
375
+ if(++count > this.settings.asteroidCount) {
376
+ break;
377
+ }
378
+ }
379
+ }
380
+
381
+ createCubeVertices(options) {
382
+ if(typeof options === 'undefined') {
383
+ var options = {
384
+ scale: 1,
385
+ useUVShema4x2: false
386
+ }
387
+ }
388
+ if(typeof options.scale === 'undefined') options.scale = 1;
389
+
390
+ let vertices;
391
+ if(options.useUVShema4x2 == true) {
392
+ vertices = new Float32Array([
393
+ // position | texture coordinate
394
+ //-------------+----------------------
395
+ // front face select the top left image 1, 0.5,
396
+ -1, 1, 1, 1, 0, 0, 0, 0,
397
+ -1, -1, 1, 1, 0, 0, 0, 0.5,
398
+ 1, 1, 1, 1, 0, 0, 0.25, 0,
399
+ 1, -1, 1, 1, 0, 0, 0.25, 0.5,
400
+ // right face select the top middle image
401
+ 1, 1, -1, 1, 0, 0, 0.25, 0,
402
+ 1, 1, 1, 1, 0, 0, 0.5, 0,
403
+ 1, -1, -1, 1, 0, 0, 0.25, 0.5,
404
+ 1, -1, 1, 1, 0, 0, 0.5, 0.5,
405
+ // back face select to top right image
406
+ 1, 1, -1, 1, 0, 0, 0.5, 0,
407
+ 1, -1, -1, 1, 0, 0, 0.5, 0.5,
408
+ -1, 1, -1, 1, 0, 0, 0.75, 0,
409
+ -1, -1, -1, 1, 0, 0, 0.75, 0.5,
410
+ // left face select the bottom left image
411
+ -1, 1, 1, 1, 0, 0, 0, 0.5,
412
+ -1, 1, -1, 1, 0, 0, 0.25, 0.5,
413
+ -1, -1, 1, 1, 0, 0, 0, 1,
414
+ -1, -1, -1, 1, 0, 0, 0.25, 1,
415
+ // bottom face select the bottom middle image
416
+ 1, -1, 1, 1, 0, 0, 0.25, 0.5,
417
+ -1, -1, 1, 1, 0, 0, 0.5, 0.5,
418
+ 1, -1, -1, 1, 0, 0, 0.25, 1,
419
+ -1, -1, -1, 1, 0, 0, 0.5, 1,
420
+ // top face select the bottom right image
421
+ -1, 1, 1, 1, 0, 0, 0.5, 0.5,
422
+ 1, 1, 1, 1, 0, 0, 0.75, 0.5,
423
+ -1, 1, -1, 1, 0, 0, 0.5, 1,
424
+ 1, 1, -1, 1, 0, 0, 0.75, 1,
425
+ ]);
426
+ } else {
427
+ vertices = new Float32Array([
428
+ // position | texture coordinate
429
+ //------------- +----------------------
430
+ // front face select the top left image 1, 0.5,
431
+ -1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
432
+ -1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
433
+ 1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 0,
434
+ 1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 1,
435
+ // right face select the top middle image
436
+ 1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 0,
437
+ 1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
438
+ 1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
439
+ 1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 1,
440
+ // back face select to top right image
441
+ 1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 0,
442
+ 1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 1,
443
+ -1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
444
+ -1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
445
+ // left face select the bottom left image
446
+ -1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
447
+ -1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 0, 1,
448
+ -1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 1, 0,
449
+ -1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
450
+ // bottom face select the bottom middle image
451
+ 1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
452
+ -1 * options.scale, -1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
453
+ 1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
454
+ -1 * options.scale, -1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
455
+ // top face select the bottom right image
456
+ -1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 0,
457
+ 1 * options.scale, 1 * options.scale, 1 * options.scale, 1, 0, 0, 0, 1,
458
+ -1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 0,
459
+ 1 * options.scale, 1 * options.scale, -1 * options.scale, 1, 0, 0, 1, 1,
460
+ ])
461
+ }
462
+
463
+
464
+ const indices = new Uint16Array([
465
+ 0, 1, 2, 2, 1, 3, // front
466
+ 4, 5, 6, 6, 5, 7, // right
467
+ 8, 9, 10, 10, 9, 11, // back
468
+ 12, 13, 14, 14, 13, 15, // left
469
+ 16, 17, 18, 18, 17, 19, // bottom
470
+ 20, 21, 22, 22, 21, 23, // top
471
+ ]);
472
+
473
+ return {
474
+ vertices,
475
+ indices,
476
+ numVertices: indices.length,
477
+ };
478
+ }
479
+
480
+ draw = () => {
481
+ if(this.moonTexture == null) {
482
+ console.log('not ready')
483
+ return;
484
+ }
485
+ const transformationMatrix = this.getTransformationMatrix(this.position);
486
+ this.device.queue.writeBuffer(
487
+ this.uniformBuffer,
488
+ 0,
489
+ transformationMatrix.buffer,
490
+ transformationMatrix.byteOffset,
491
+ transformationMatrix.byteLength
492
+ );
493
+ this.renderPassDescriptor.colorAttachments[0].view = this.context
494
+ .getCurrentTexture()
495
+ .createView();
496
+ }
479
497
  }