matrix-engine-wgpu 1.3.12 → 1.3.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matrix-engine-wgpu",
3
- "version": "1.3.12",
3
+ "version": "1.3.13",
4
4
  "description": "obj sequence anim +HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.",
5
5
  "main": "index.js",
6
6
  "files": [
package/readme.md CHANGED
@@ -338,6 +338,18 @@ TEST.loadVideoTexture({
338
338
  });
339
339
  ```
340
340
 
341
+ For canvasinline attach this to arg (example for direct draw on canvas2d and passing intro webgpu pipeline):
342
+ ```js
343
+ canvaInlineProgram: (ctx, canvas) => {
344
+ ctx.fillStyle = 'black';
345
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
346
+ ctx.fillStyle = 'white';
347
+ ctx.font = '20px Orbitron';
348
+ ctx.fillText(`FPS: ${Math.round(performance.now() % 60)}`, 10, 30);
349
+ }
350
+ ```
351
+
352
+
341
353
  <pre>
342
354
  | Scenario | Best Approach |
343
355
  | ------------------------------ | ---------------------------------- |
@@ -2,6 +2,7 @@
2
2
  // 'wgpu-matrix' library, so produces many temporary vectors and matrices.
3
3
  // This is intentional, as this sample prefers readability over performance.
4
4
  import { mat4, vec3 } from 'wgpu-matrix';
5
+ import {LOG_INFO} from './utils';
5
6
  // import Input from './input';
6
7
 
7
8
  // // Common interface for camera implementations
@@ -47,6 +48,10 @@ class CameraBase {
47
48
  mat4.copy(mat, this.matrix_);
48
49
  }
49
50
 
51
+ setProjection(fov = (2 * Math.PI) / 5, aspect = 1, near = 1, far = 1000) {
52
+ this.projectionMatrix = mat4.perspective(fov, aspect, near, far);
53
+ }
54
+
50
55
  // Returns the camera view matrix
51
56
  get view() {
52
57
  return this.view_;
@@ -135,7 +140,9 @@ export class WASDCamera extends CameraBase {
135
140
  const forward = vec3.normalize(vec3.sub(target, position));
136
141
  this.recalculateAngles(forward);
137
142
  this.position = position;
138
- // console.log(`%cCamera pos: ${position}`, LOG_INFO);
143
+
144
+ this.setProjection()
145
+ console.log(`%cCamera constructor : ${position}`, LOG_INFO);
139
146
  }
140
147
  }
141
148
 
@@ -0,0 +1,126 @@
1
+ import {mat4, vec3} from 'wgpu-matrix';
2
+
3
+ export class SpotLight {
4
+ position;
5
+ target;
6
+ up;
7
+ direction;
8
+
9
+ viewMatrix;
10
+ projectionMatrix;
11
+ viewProjMatrix;
12
+
13
+ fov;
14
+ aspect;
15
+ near;
16
+ far;
17
+
18
+ innerCutoff;
19
+ outerCutoff;
20
+
21
+ spotlightUniformBuffer;
22
+
23
+ constructor(
24
+ position = vec3.create(0, 5, 10),
25
+ target = vec3.create(0, 0, 0),
26
+ fov = 45,
27
+ aspect = 1.0,
28
+ near = 0.1,
29
+ far = 100
30
+ ) {
31
+ this.position = position;
32
+ this.target = target;
33
+ this.up = vec3.create(0, 1, 0);
34
+ this.direction = vec3.normalize(vec3.subtract(target, position));
35
+
36
+ this.viewMatrix = mat4.lookAt(position, target, this.up);
37
+ this.projectionMatrix = mat4.perspective(
38
+ (fov * Math.PI) / 180,
39
+ aspect,
40
+ near,
41
+ far
42
+ );
43
+ this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
44
+
45
+ this.fov = fov;
46
+ this.aspect = aspect;
47
+ this.near = near;
48
+ this.far = far;
49
+
50
+ this.innerCutoff = Math.cos((Math.PI / 180) * 12.5);
51
+ this.outerCutoff = Math.cos((Math.PI / 180) * 17.5);
52
+ }
53
+
54
+ update() {
55
+ this.direction = vec3.normalize(vec3.subtract(this.target, this.position));
56
+ this.viewMatrix = mat4.lookAt(this.position, this.target, this.up);
57
+ this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
58
+ console.log('test light update this.position : ', this.position)
59
+ }
60
+
61
+ updateSceneUniforms = (sceneUniformBuffer, camera, inputHandler) => {
62
+
63
+ console.log('test camera.view ', camera.view)
64
+ // Update spotlight matrices
65
+ this.update();
66
+
67
+ const now = Date.now();
68
+
69
+ // Get camera matrices
70
+ camera.update(now, inputHandler()); // Your camera class should also update view/projection
71
+ const camVP = mat4.multiply(camera.projectionMatrix, camera.view);
72
+
73
+ // Prepare float data
74
+ const sceneData = new Float32Array(16 + 16 + 4); // 2 matrices + vec3
75
+
76
+ // Light view-proj matrix
77
+ sceneData.set(this.viewProjMatrix, 0);
78
+
79
+ // Camera view-proj matrix
80
+ sceneData.set(camVP, 16);
81
+
82
+ // Light position (vec3f + padding)
83
+ sceneData.set(this.position, 32);
84
+
85
+ // Write to GPU
86
+ this.device.queue.writeBuffer(
87
+ sceneUniformBuffer,
88
+ 0,
89
+ sceneData.buffer,
90
+ sceneData.byteOffset,
91
+ sceneData.byteLength
92
+ );
93
+
94
+ console.log('light.viewProj[0..3]', this.viewProjMatrix.slice(0, 4));
95
+ console.log('camera.vp[0..3]', camVP.slice(0, 4));
96
+ }
97
+
98
+ prepareBuffer(device) {
99
+ this.device = device;
100
+ this.spotlightUniformBuffer = device.createBuffer({
101
+ size: 16 * 4, // 64 bytes
102
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
103
+ });
104
+
105
+ const spotlightData = this.getLightDataBuffer();
106
+
107
+ device.queue.writeBuffer(
108
+ this.spotlightUniformBuffer,
109
+ 0,
110
+ spotlightData.buffer,
111
+ spotlightData.byteOffset,
112
+ spotlightData.byteLength
113
+ );
114
+ }
115
+
116
+ getLightDataBuffer() {
117
+ return new Float32Array([
118
+ ...this.position, 0.0,
119
+ ...this.direction, 0.0,
120
+ this.innerCutoff,
121
+ this.outerCutoff,
122
+ 0.0,
123
+ 0.0,
124
+ ]);
125
+ }
126
+ }
@@ -22,6 +22,25 @@ export default class Materials {
22
22
  magFilter: 'linear',
23
23
  minFilter: 'linear',
24
24
  });
25
+
26
+ // FX effect
27
+ this.postFXModeBuffer = this.device.createBuffer({
28
+ size: 4, // u32 = 4 bytes
29
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
30
+ });
31
+
32
+ // Dymmy buffer
33
+ this.dummySpotlightUniformBuffer = this.device.createBuffer({
34
+ size: 64, // Must match size in shader
35
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
36
+ });
37
+ this.device.queue.writeBuffer(this.dummySpotlightUniformBuffer, 0, new Float32Array(16));
38
+
39
+ }
40
+
41
+ updatePostFXMode(mode) {
42
+ const arrayBuffer = new Uint32Array([mode]);
43
+ this.device.queue.writeBuffer(this.postFXModeBuffer, 0, arrayBuffer);
25
44
  }
26
45
 
27
46
  async loadTex0(texturesPaths) {
@@ -190,6 +209,7 @@ export default class Materials {
190
209
  binding: 4,
191
210
  resource: this.videoSampler,
192
211
  },
212
+ {binding: 5, resource: {buffer: this.postFXModeBuffer}}
193
213
  ],
194
214
  });
195
215
  } else {
@@ -216,6 +236,10 @@ export default class Materials {
216
236
  binding: 4,
217
237
  resource: this.imageSampler,
218
238
  },
239
+ {
240
+ binding: 5,
241
+ resource: {buffer: this.lightContainer.length == 0 ? this.dummySpotlightUniformBuffer : this.lightContainer[0].spotlightUniformBuffer},
242
+ },
219
243
  ],
220
244
  });
221
245
  }
@@ -251,6 +275,11 @@ export default class Materials {
251
275
  visibility: GPUShaderStage.FRAGMENT,
252
276
  sampler: {type: 'filtering'}, // for video sampling
253
277
  },
278
+ {
279
+ binding: 5,
280
+ visibility: GPUShaderStage.FRAGMENT,
281
+ buffer: {type: 'uniform'},
282
+ }
254
283
  ]
255
284
  : [ // IMAGE
256
285
  {
@@ -266,6 +295,11 @@ export default class Materials {
266
295
  visibility: GPUShaderStage.FRAGMENT,
267
296
  sampler: {type: 'filtering'},
268
297
  },
298
+ {
299
+ binding: 5,
300
+ visibility: GPUShaderStage.FRAGMENT,
301
+ buffer: {type: 'uniform'},
302
+ }
269
303
  ])
270
304
  ],
271
305
  });
@@ -1,15 +1,15 @@
1
1
  import {mat4, vec3} from 'wgpu-matrix';
2
2
  import {Position, Rotation} from "./matrix-class";
3
- import {createInputHandler} from "./engine";
4
3
  import {vertexShadowWGSL} from '../shaders/vertexShadow.wgsl';
5
4
  import {fragmentWGSL} from '../shaders/fragment.wgsl';
6
5
  import {vertexWGSL} from '../shaders/vertex.wgsl';
7
6
  import {degToRad, genName, LOG_FUNNY_SMALL} from './utils';
8
7
  import Materials from './materials';
9
8
  import {fragmentVideoWGSL} from '../shaders/fragment.video.wgsl';
9
+ import {createInputHandler} from './engine';
10
10
 
11
11
  export default class MEMeshObj extends Materials {
12
- constructor(canvas, device, context, o) {
12
+ constructor(canvas, device, context, o, sceneUniformBuffer) {
13
13
  super(device);
14
14
  if(typeof o.name === 'undefined') o.name = genName(9);
15
15
  if(typeof o.raycast === 'undefined') {
@@ -30,6 +30,9 @@ export default class MEMeshObj extends Materials {
30
30
  // comes from engine not from args
31
31
  this.clearColor = "red";
32
32
 
33
+
34
+ // this.lightTest = new SpotLight();
35
+ // this.lightTest.prepareBuffer(this.device);
33
36
  this.video = null;
34
37
 
35
38
  // Mesh stuff - for single mesh or t-posed (fiktive-first in loading order)
@@ -47,7 +50,7 @@ export default class MEMeshObj extends Materials {
47
50
  this.drawElements = this.drawElementsAnim;
48
51
  }
49
52
 
50
- this.inputHandler = createInputHandler(window, canvas);
53
+ this.inputHandler = null;
51
54
  this.cameras = o.cameras;
52
55
 
53
56
  this.mainCameraParams = {
@@ -55,14 +58,10 @@ export default class MEMeshObj extends Materials {
55
58
  responseCoef: o.mainCameraParams.responseCoef
56
59
  }
57
60
 
58
- // touchCoordinate.enabled = true;
59
-
60
61
  this.lastFrameMS = 0;
61
62
  this.texturesPaths = [];
62
63
  o.texturesPaths.forEach((t) => {this.texturesPaths.push(t)})
63
-
64
64
  this.presentationFormat = navigator.gpu.getPreferredCanvasFormat();
65
-
66
65
  this.position = new Position(o.position.x, o.position.y, o.position.z);
67
66
  this.rotation = new Rotation(o.rotation.x, o.rotation.y, o.rotation.z);
68
67
  this.rotation.rotationSpeed.x = o.rotationSpeed.x;
@@ -74,7 +73,7 @@ export default class MEMeshObj extends Materials {
74
73
  return new Promise(async (resolve) => {
75
74
  this.shadowDepthTextureSize = 1024;
76
75
  const aspect = canvas.width / canvas.height;
77
- this.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 2000.0);
76
+ // this.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 2000.0);
78
77
  this.modelViewProjectionMatrix = mat4.create();
79
78
  // console.log('cube added texturesPaths: ', this.texturesPaths)
80
79
  this.loadTex0(this.texturesPaths).then(() => {
@@ -235,43 +234,13 @@ export default class MEMeshObj extends Materials {
235
234
  // Create a bind group layout which holds the scene uniforms and
236
235
  // the texture+sampler for depth. We create it manually because the WebPU
237
236
  // implementation doesn't infer this from the shader (yet).
238
- this.createLayoutForRender()
239
-
237
+ this.createLayoutForRender();
240
238
  this.setupPipeline();
241
- // this.pipeline = this.device.createRenderPipeline({
242
- // layout: this.device.createPipelineLayout({
243
- // bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout],
244
- // }),
245
- // vertex: {
246
- // module: this.device.createShaderModule({
247
- // code: vertexWGSL,
248
- // }),
249
- // buffers: this.vertexBuffers,
250
- // },
251
- // fragment: {
252
- // module: this.device.createShaderModule({
253
- // code: fragmentWGSL,
254
- // }),
255
- // targets: [
256
- // {
257
- // format: presentationFormat,
258
- // },
259
- // ],
260
- // constants: {
261
- // shadowDepthTextureSize: this.shadowDepthTextureSize,
262
- // },
263
- // },
264
- // depthStencil: {
265
- // depthWriteEnabled: true,
266
- // depthCompare: 'less',
267
- // format: 'depth24plus-stencil8',
268
- // },
269
- // primitive,
270
- // });
271
239
 
272
240
  const depthTexture = this.device.createTexture({
273
241
  size: [canvas.width, canvas.height],
274
- format: 'depth24plus-stencil8',
242
+ // format: 'depth24plus-stencil8',
243
+ format: 'depth24plus',
275
244
  usage: GPUTextureUsage.RENDER_ATTACHMENT,
276
245
  });
277
246
 
@@ -290,9 +259,9 @@ export default class MEMeshObj extends Materials {
290
259
  depthClearValue: 1.0,
291
260
  depthLoadOp: 'clear',
292
261
  depthStoreOp: 'store',
293
- stencilClearValue: 0,
294
- stencilLoadOp: 'clear',
295
- stencilStoreOp: 'store',
262
+ // stencilClearValue: 0,
263
+ // stencilLoadOp: 'clear',
264
+ // stencilStoreOp: 'store',
296
265
  },
297
266
  };
298
267
 
@@ -309,6 +278,9 @@ export default class MEMeshObj extends Materials {
309
278
  size: 2 * 4 * 16 + 4 * 4,
310
279
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
311
280
  });
281
+ // cant be global
282
+ // console.log('sceneUniformBuffer', sceneUniformBuffer)
283
+ // this.sceneUniformBuffer = sceneUniformBuffer;
312
284
 
313
285
  this.sceneBindGroupForShadow = this.device.createBindGroup({
314
286
  layout: this.uniformBufferBindGroupLayout,
@@ -322,7 +294,6 @@ export default class MEMeshObj extends Materials {
322
294
  ],
323
295
  });
324
296
 
325
- // --------------------------
326
297
  this.createBindGroupForRender();
327
298
 
328
299
  this.modelBindGroup = this.device.createBindGroup({
@@ -364,7 +335,9 @@ export default class MEMeshObj extends Materials {
364
335
  mat4.rotateZ(this.viewMatrix, Math.PI * this.rotation.getRotZ(), this.viewMatrix);
365
336
  // console.info('NOT PHYSICS angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
366
337
  }
367
- mat4.multiply(this.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
338
+
339
+ // console.info('NOT camera.projectionMatrix: ', camera.projectionMatrix )
340
+ mat4.multiply(camera.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
368
341
  return this.modelViewProjectionMatrix;
369
342
  }
370
343
 
@@ -387,62 +360,16 @@ export default class MEMeshObj extends Materials {
387
360
  return modelMatrix;
388
361
  };
389
362
 
390
- this.upVector = vec3.fromValues(0, 1, 0);
391
- this.origin = vec3.fromValues(0, 0, 0);
392
-
393
- this.lightPosition = vec3.fromValues(0, 0, 0);
394
- this.lightViewMatrix = mat4.lookAt(this.lightPosition, this.origin, this.upVector);
395
- const lightProjectionMatrix = mat4.create();
396
-
397
- var myLMargin = 100;
398
- {
399
- const left = -myLMargin;
400
- const right = myLMargin;
401
- const bottom = -myLMargin;
402
- const top = myLMargin;
403
- const near = -200;
404
- const far = 300;
405
- mat4.ortho(left, right, bottom, top, near, far, lightProjectionMatrix);
406
- // test
407
- // mat4.ortho(right, left, top, bottom, near, far, lightProjectionMatrix);
408
- }
409
-
410
- this.lightViewProjMatrix = mat4.multiply(
411
- lightProjectionMatrix,
412
- this.lightViewMatrix
413
- );
414
-
415
363
  // looks like affect on transformations for now const 0
416
364
  const modelMatrix = mat4.translation([0, 0, 0]);
417
- // The camera/light aren't moving, so write them into buffers now.
418
- {
419
- const lightMatrixData = this.lightViewProjMatrix; // as Float32Array;
420
- this.device.queue.writeBuffer(
421
- this.sceneUniformBuffer,
422
- 0,
423
- lightMatrixData.buffer,
424
- lightMatrixData.byteOffset,
425
- lightMatrixData.byteLength
426
- );
427
-
428
- const lightData = this.lightPosition;
429
- this.device.queue.writeBuffer(
430
- this.sceneUniformBuffer,
431
- 128,
432
- lightData.buffer,
433
- lightData.byteOffset,
434
- lightData.byteLength
435
- );
436
-
437
- const modelData = modelMatrix;
438
- this.device.queue.writeBuffer(
439
- this.modelUniformBuffer,
440
- 0,
441
- modelData.buffer,
442
- modelData.byteOffset,
443
- modelData.byteLength
444
- );
445
- }
365
+ const modelData = modelMatrix;
366
+ this.device.queue.writeBuffer(
367
+ this.modelUniformBuffer,
368
+ 0,
369
+ modelData.buffer,
370
+ modelData.byteOffset,
371
+ modelData.byteLength
372
+ );
446
373
 
447
374
  this.shadowPassDescriptor = {
448
375
  colorAttachments: [],
@@ -463,73 +390,8 @@ export default class MEMeshObj extends Materials {
463
390
  })
464
391
  }
465
392
 
466
- updateLightsTest = (position) => {
467
- console.log('Update light position.', position)
468
- this.lightPosition = vec3.fromValues(position[0], position[1], position[2]);
469
- this.lightViewMatrix = mat4.lookAt(this.lightPosition, this.origin, this.upVector);
470
-
471
- const lightProjectionMatrix = mat4.create();
472
- {
473
- const left = -80;
474
- const right = 80;
475
- const bottom = -80;
476
- const top = 80;
477
- const near = -200;
478
- const far = 300;
479
- mat4.ortho(left, right, bottom, top, near, far, lightProjectionMatrix);
480
- }
481
-
482
- this.lightViewProjMatrix = mat4.multiply(
483
- lightProjectionMatrix,
484
- this.lightViewMatrix
485
- );
486
-
487
- // looks like affect on transformations for now const 0
488
- const modelMatrix = mat4.translation([0, 0, 0]);
489
- // The camera/light aren't moving, so write them into buffers now.
490
- {
491
- const lightMatrixData = this.lightViewProjMatrix; // as Float32Array;
492
- this.device.queue.writeBuffer(
493
- this.sceneUniformBuffer,
494
- 0, // 0 ori
495
- lightMatrixData.buffer,
496
- lightMatrixData.byteOffset,
497
- lightMatrixData.byteLength
498
- );
499
-
500
- const lightData = this.lightPosition;
501
- this.device.queue.writeBuffer(
502
- this.sceneUniformBuffer,
503
- 256,
504
- lightData.buffer,
505
- lightData.byteOffset,
506
- lightData.byteLength
507
- );
508
-
509
- const modelData = modelMatrix;
510
- this.device.queue.writeBuffer(
511
- this.modelUniformBuffer,
512
- 0,
513
- modelData.buffer,
514
- modelData.byteOffset,
515
- modelData.byteLength
516
- );
517
- }
518
-
519
- this.shadowPassDescriptor = {
520
- colorAttachments: [],
521
- depthStencilAttachment: {
522
- view: this.shadowDepthTextureView,
523
- depthClearValue: 1.0, // ori 1.0
524
- depthLoadOp: 'clear',
525
- depthStoreOp: 'store',
526
- },
527
- };
528
-
529
- ///////////////////////
530
- }
531
-
532
393
  setupPipeline = () => {
394
+ console.log('test >>>>>>>>>>>>>>>>>>>FORMAT>✅' + this.presentationFormat);
533
395
  this.pipeline = this.device.createRenderPipeline({
534
396
  layout: this.device.createPipelineLayout({
535
397
  bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout],
@@ -558,7 +420,8 @@ export default class MEMeshObj extends Materials {
558
420
  depthStencil: {
559
421
  depthWriteEnabled: true,
560
422
  depthCompare: 'less',
561
- format: 'depth24plus-stencil8',
423
+ // format: 'depth24plus-stencil8',
424
+ format: 'depth24plus',
562
425
  },
563
426
  primitive: this.primitive,
564
427
  });
@@ -586,7 +449,6 @@ export default class MEMeshObj extends Materials {
586
449
  renderPass.drawIndexed(this.indexCount);
587
450
  }
588
451
 
589
- // test
590
452
  createGPUBuffer(dataArray, usage) {
591
453
  if(!dataArray || typeof dataArray.length !== 'number') {
592
454
  throw new Error('Invalid data array passed to createGPUBuffer');
@@ -613,7 +475,6 @@ export default class MEMeshObj extends Materials {
613
475
  return buffer;
614
476
  }
615
477
 
616
-
617
478
  updateMeshListBuffers() {
618
479
  for(const key in this.objAnim.meshList) {
619
480
  const mesh = this.objAnim.meshList[key];
@@ -12,6 +12,8 @@ struct Scene {
12
12
  @group(0) @binding(3) var meshTexture: texture_external;
13
13
  @group(0) @binding(4) var meshSampler: sampler;
14
14
 
15
+ @group(0) @binding(5) var<uniform> postFXMode: u32;
16
+
15
17
  // ❌ No binding(4) here!
16
18
 
17
19
  struct FragmentInput {
@@ -45,7 +47,37 @@ fn main(input : FragmentInput) -> @location(0) vec4f {
45
47
 
46
48
  // ✅ Correct way to sample video texture
47
49
  let textureColor = textureSampleBaseClampToEdge(meshTexture, meshSampler, input.uv);
50
+ let color: vec4f = vec4(textureColor.rgb * lightingFactor * albedo, 1.0);
51
+
52
+ switch (postFXMode) {
53
+ case 0: {
54
+ // Default
55
+ return color;
56
+ }
57
+ case 1: {
58
+ // Invert
59
+ return vec4f(1.0 - color.rgb, color.a);
60
+ }
61
+ case 2: {
62
+ // Grayscale
63
+ let gray = dot(color.rgb, vec3f(0.299, 0.587, 0.114));
64
+ return vec4f(vec3f(gray), color.a);
65
+ }
66
+ case 3: {
67
+ // Chroma Key
68
+ let keyColor = vec3f(0.0, 1.0, 0.0);
69
+ let threshold = 0.3;
70
+ let diff = distance(color.rgb, keyColor);
71
+ if (diff < threshold) {
72
+ return vec4f(0.0, 0.0, 0.0, 0.0);
73
+ }
74
+ return color;
75
+ }
76
+ default: {
77
+ return color;
78
+ }
79
+ }
48
80
 
49
- return vec4(textureColor.rgb * lightingFactor * albedo, 1.0);
81
+ // return color;
50
82
  }
51
83
  `;
@@ -4,6 +4,17 @@ struct Scene {
4
4
  lightViewProjMatrix : mat4x4f,
5
5
  cameraViewProjMatrix : mat4x4f,
6
6
  lightPos : vec3f,
7
+ padding : f32, // Required for alignment
8
+ }
9
+
10
+ struct SpotLight {
11
+ position: vec3f,
12
+ _pad1: f32,
13
+ direction: vec3f,
14
+ _pad2: f32,
15
+ innerCutoff: f32,
16
+ outerCutoff: f32,
17
+ _pad3: vec2f,
7
18
  }
8
19
 
9
20
  @group(0) @binding(0) var<uniform> scene : Scene;
@@ -11,6 +22,7 @@ struct Scene {
11
22
  @group(0) @binding(2) var shadowSampler: sampler_comparison;
12
23
  @group(0) @binding(3) var meshTexture: texture_2d<f32>;
13
24
  @group(0) @binding(4) var meshSampler: sampler;
25
+ @group(0) @binding(5) var<uniform> spotlight: SpotLight;
14
26
 
15
27
  struct FragmentInput {
16
28
  @location(0) shadowPos : vec3f,
@@ -22,16 +34,22 @@ struct FragmentInput {
22
34
  const albedo = vec3f(0.9);
23
35
  const ambientFactor = 0.7;
24
36
 
37
+ fn calculateSpotlightFactor(light: SpotLight, fragPos: vec3f) -> f32 {
38
+ let L = normalize(light.position - fragPos);
39
+ let theta = dot(L, normalize(-light.direction));
40
+ let epsilon = light.innerCutoff - light.outerCutoff;
41
+ let intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
42
+ return intensity;
43
+ }
44
+
25
45
  @fragment
26
46
  fn main(input : FragmentInput) -> @location(0) vec4f {
27
- // Percentage-closer filtering. Sample texels in the region
28
- // to smooth the result.
47
+ // Shadow PFC
29
48
  var visibility = 0.0;
30
- let oneOverShadowDepthTextureSize = 1.0 / shadowDepthTextureSize;
49
+ let oneOverSize = 1.0 / shadowDepthTextureSize;
31
50
  for (var y = -1; y <= 1; y++) {
32
51
  for (var x = -1; x <= 1; x++) {
33
- let offset = vec2f(vec2(x, y)) * oneOverShadowDepthTextureSize;
34
-
52
+ let offset = vec2f(vec2(x, y)) * oneOverSize;
35
53
  visibility += textureSampleCompare(
36
54
  shadowMap, shadowSampler,
37
55
  input.shadowPos.xy + offset, input.shadowPos.z - 0.007
@@ -39,12 +57,19 @@ fn main(input : FragmentInput) -> @location(0) vec4f {
39
57
  }
40
58
  }
41
59
  visibility /= 9.0;
42
- let lambertFactor = max(dot(normalize(scene.lightPos - input.fragPos), normalize(input.fragNorm)), 0.0);
43
- let lightingFactor = min(ambientFactor + visibility * lambertFactor, 1.0);
44
- let textureColor = textureSample(meshTexture, meshSampler, input.uv);
45
-
46
- return vec4(textureColor.rgb * lightingFactor * albedo, 1.0);
47
- // return vec4f(input.fragNorm * 0.5 + 0.5, 1)
48
- // return vec4f(input.uv, 0, 1)
49
- // return vec4(textureColor.rgb , 0.5);
50
- }`
60
+
61
+ // Lambert
62
+ let norm = normalize(input.fragNorm);
63
+ let lightDir = normalize(scene.lightPos - input.fragPos);
64
+ let lambert = max(dot(norm, lightDir), 0.0);
65
+
66
+ // Spotlight effect
67
+ let spotlightFactor = calculateSpotlightFactor(spotlight, input.fragPos);
68
+
69
+ // Combine
70
+ let lightIntensity = ambientFactor + lambert * visibility * spotlightFactor;
71
+ let texColor = textureSample(meshTexture, meshSampler, input.uv);
72
+
73
+ return vec4f(texColor.rgb * lightIntensity * albedo, 1.0);
74
+ }
75
+ `
package/src/world.js CHANGED
@@ -2,18 +2,19 @@ import {vec3} from "wgpu-matrix";
2
2
  import MEBall from "./engine/ball.js";
3
3
  import MECube from './engine/cube.js';
4
4
  import {ArcballCamera, WASDCamera} from "./engine/engine.js";
5
- import MEMesh from "./engine/mesh.js";
5
+ import {createInputHandler} from "./engine/engine.js";
6
6
  import MEMeshObj from "./engine/mesh-obj.js";
7
7
  import MatrixAmmo from "./physics/matrix-ammo.js";
8
8
  import {LOG_WARN, genName, mb, scriptManager, urlQuery} from "./engine/utils.js";
9
9
  import {MultiLang} from "./multilang/lang.js";
10
10
  import {MatrixSounds} from "./sounds/sounds.js";
11
11
  import {play} from "./engine/loader-obj.js";
12
+ import {SpotLight} from "./engine/lights.js";
12
13
 
13
14
  export default class MatrixEngineWGPU {
14
15
 
15
16
  mainRenderBundle = [];
16
- rbContainer = [];
17
+ lightContainer = [];
17
18
  frame = () => {};
18
19
 
19
20
  entityHolder = [];
@@ -131,11 +132,23 @@ export default class MatrixEngineWGPU {
131
132
  this.frame = this.framePassPerObject;
132
133
  }
133
134
 
135
+ // Global SCENE BUFFER
136
+ this.sceneUniformBuffer = this.device.createBuffer({
137
+ // Two 4x4 viewProj matrices,
138
+ // one for the camera and one for the light.
139
+ // Then a vec3 for the light position.
140
+ // Rounded to the nearest multiple of 16.
141
+ size: 2 * 4 * 16 + 4 * 4,
142
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
143
+ });
144
+
145
+ this.inputHandler = createInputHandler(window, canvas);
146
+
134
147
  this.run(callback)
135
148
  };
136
149
 
137
150
  getSceneObjectByName(name) {
138
- return this.mainRenderBundle.find((sceneObject) => sceneObject.name === name )
151
+ return this.mainRenderBundle.find((sceneObject) => sceneObject.name === name)
139
152
  }
140
153
 
141
154
  // Not in use for now
@@ -238,27 +251,15 @@ export default class MatrixEngineWGPU {
238
251
  this.mainRenderBundle.push(myBall1);
239
252
  }
240
253
 
241
- // Not in use for now
242
- addMesh = (o) => {
243
- if(typeof o.position === 'undefined') {o.position = {x: 0, y: 0, z: -4}}
244
- if(typeof o.rotation === 'undefined') {o.rotation = {x: 0, y: 0, z: 0}}
245
- if(typeof o.rotationSpeed === 'undefined') {o.rotationSpeed = {x: 0, y: 0, z: 0}}
246
- if(typeof o.texturesPaths === 'undefined') {o.texturesPaths = ['./res/textures/default.png']}
247
- if(typeof o.mainCameraParams === 'undefined') {o.mainCameraParams = this.mainCameraParams}
248
- if(typeof o.scale === 'undefined') {o.scale = 1;}
249
- o.entityArgPass = this.entityArgPass;
250
- o.cameras = this.cameras;
251
- if(typeof o.name === 'undefined') {o.name = 'random' + Math.random();}
252
- if(typeof o.mesh === 'undefined') {
253
- throw console.error('arg mesh is empty...');
254
- return;
255
- }
256
- console.log('Mesh procedure', o)
257
- let myMesh1 = new MEMesh(this.canvas, this.device, this.context, o)
258
- this.mainRenderBundle.push(myMesh1);
254
+ addLight(o) {
255
+ // test light global; entity
256
+ let newLight = new SpotLight();
257
+ newLight.prepareBuffer(this.device);
258
+ this.lightContainer.push(newLight);
259
+ console.log('Add light : ', newLight);
259
260
  }
260
261
 
261
- addMeshObj = (o , clearColor=this.options.clearColor) => {
262
+ addMeshObj = (o, clearColor = this.options.clearColor) => {
262
263
  if(typeof o.name === 'undefined') {o.name = genName(9)}
263
264
  if(typeof o.position === 'undefined') {o.position = {x: 0, y: 0, z: -4}}
264
265
  if(typeof o.rotation === 'undefined') {o.rotation = {x: 0, y: 0, z: 0}}
@@ -311,7 +312,9 @@ export default class MatrixEngineWGPU {
311
312
  }
312
313
  }
313
314
  }
314
- let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o);
315
+ let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o, this.sceneUniformBuffer);
316
+ myMesh1.lightContainer = this.lightContainer;
317
+ myMesh1.inputHandler = this.inputHandler;
315
318
  myMesh1.clearColor = clearColor;
316
319
  if(o.physics.enabled == true) {
317
320
  this.matrixAmmo.addPhysics(myMesh1, o.physics)
@@ -330,31 +333,56 @@ export default class MatrixEngineWGPU {
330
333
  }
331
334
 
332
335
  frameSinglePass = () => {
333
- if(typeof this.mainRenderBundle == 'undefined') return;
336
+ if(typeof this.mainRenderBundle == 'undefined' || this.mainRenderBundle.length == 0) {
337
+ setTimeout(() => {requestAnimationFrame(this.frame)}, 200);
338
+ return;
339
+ }
334
340
  try {
335
341
  let shadowPass = null;
336
342
  let renderPass;
337
343
  let commandEncoder = this.device.createCommandEncoder();
338
344
 
339
- this.mainRenderBundle.forEach((meItem, index) => {
340
- meItem.position.update();
341
- })
345
+ // 1️⃣ Update light data (position, direction, uniforms)
346
+ for(const light of this.lightContainer) {
347
+ // light.updateSceneUniforms(this.sceneUniformBuffer, this.cameras.WASD, this.inputHandler);
348
+ }
342
349
 
343
- if (this.matrixAmmo) this.matrixAmmo.updatePhysics();
350
+ this.mainRenderBundle.forEach((meItem, index) => {meItem.position.update()})
351
+ if(this.matrixAmmo) this.matrixAmmo.updatePhysics();
352
+
353
+ // no cast WORKING
354
+ // this.mainRenderBundle.forEach((meItem, index) => {
355
+ // meItem.draw(commandEncoder);
356
+
357
+ // shadowPass = commandEncoder.beginRenderPass(meItem.shadowPassDescriptor);
358
+ // shadowPass.setPipeline(meItem.shadowPipeline);
359
+ // meItem.drawShadows(shadowPass);
360
+ // shadowPass.end();
361
+ // })
362
+
363
+ // cast!
364
+ const firstItem = this.mainRenderBundle[0];
365
+ shadowPass = commandEncoder.beginRenderPass(firstItem.shadowPassDescriptor);
366
+ shadowPass.setPipeline(firstItem.shadowPipeline);
367
+ for(const meItem of this.mainRenderBundle) {
368
+ // meItem.draw(commandEncoder);
369
+ meItem.drawShadows(shadowPass); // Draw ALL objects
370
+ }
371
+ shadowPass.end();
344
372
 
345
373
  this.mainRenderBundle.forEach((meItem, index) => {
346
- meItem.draw(commandEncoder);
374
+ if(index == 0) {
347
375
 
348
- shadowPass = commandEncoder.beginRenderPass(meItem.shadowPassDescriptor);
349
- shadowPass.setPipeline(meItem.shadowPipeline);
350
- meItem.drawShadows(shadowPass);
351
- shadowPass.end();
352
- })
376
+ // meItem. updateSceneUniforms
377
+ meItem.draw(commandEncoder);
378
+
379
+ meItem.renderPassDescriptor.colorAttachments[0].view =
380
+ this.context.getCurrentTexture().createView();
353
381
 
354
- this.mainRenderBundle.forEach((meItem, index) => {
355
- if(index == 0) {
356
382
  renderPass = commandEncoder.beginRenderPass(meItem.renderPassDescriptor);
357
383
  renderPass.setPipeline(meItem.pipeline);
384
+ } else {
385
+ meItem.draw(commandEncoder);
358
386
  }
359
387
  })
360
388