matrix-engine-wgpu 1.3.18 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/world.js CHANGED
@@ -5,13 +5,22 @@ import {ArcballCamera, WASDCamera} from "./engine/engine.js";
5
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
- import {LOG_WARN, genName, mb, scriptManager, urlQuery} from "./engine/utils.js";
8
+ import {LOG_FUNNY_SMALL, 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
12
  import {SpotLight} from "./engine/lights.js";
13
13
 
14
+ /**
15
+ * @description
16
+ * Main engine root class.
17
+ * @author Nikola Lukic 2025
18
+ * @email zlatnaspirala@gmail.com
19
+ * @web https://maximumroulette.com
20
+ * @github zlatnaspirala
21
+ */
14
22
  export default class MatrixEngineWGPU {
23
+ lightContainer
15
24
 
16
25
  mainRenderBundle = [];
17
26
  lightContainer = [];
@@ -29,9 +38,7 @@ export default class MatrixEngineWGPU {
29
38
  matrixAmmo = new MatrixAmmo();
30
39
  matrixSounds = new MatrixSounds();
31
40
 
32
- // The input handler
33
41
  constructor(options, callback) {
34
- // console.log('typeof options ', typeof options )
35
42
  if(typeof options == 'undefined' || typeof options == "function") {
36
43
  this.options = {
37
44
  useSingleRenderPass: true,
@@ -95,7 +102,6 @@ export default class MatrixEngineWGPU {
95
102
  this.label.get = r;
96
103
  });
97
104
  }
98
-
99
105
  this.init({canvas, callback})
100
106
  }
101
107
 
@@ -106,15 +112,7 @@ export default class MatrixEngineWGPU {
106
112
  extensions: ["ray_tracing"]
107
113
  });
108
114
 
109
- // Maybe works in ssl with webworkers...
110
- // const adapterInfo = await this.adapter.requestAdapterInfo();
111
- // var test = this.adapter.features()
112
- // console.log(adapterInfo.vendor);
113
- // console.log('test' + test);
114
- // console.log("FEATURES : " + this.adapter.features)
115
-
116
115
  this.context = canvas.getContext('webgpu');
117
-
118
116
  const devicePixelRatio = window.devicePixelRatio;
119
117
  canvas.width = canvas.clientWidth * devicePixelRatio;
120
118
  canvas.height = canvas.clientHeight * devicePixelRatio;
@@ -132,21 +130,20 @@ export default class MatrixEngineWGPU {
132
130
  this.frame = this.framePassPerObject;
133
131
  }
134
132
 
135
- // Global SCENE BUFFER Good idea for future
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
-
133
+ this.MAX_SPOTLIGHTS = 20;
145
134
  this.inputHandler = createInputHandler(window, canvas);
146
-
135
+ this.createGlobalStuff();
147
136
  this.run(callback)
148
137
  };
149
138
 
139
+ createGlobalStuff() {
140
+ this.spotlightUniformBuffer = this.device.createBuffer({
141
+ label: 'spotlightUniformBufferGLOBAL',
142
+ size: this.MAX_SPOTLIGHTS * 80,
143
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
144
+ });
145
+ }
146
+
150
147
  getSceneObjectByName(name) {
151
148
  return this.mainRenderBundle.find((sceneObject) => sceneObject.name === name)
152
149
  }
@@ -252,12 +249,11 @@ export default class MatrixEngineWGPU {
252
249
  }
253
250
 
254
251
  addLight(o) {
255
- // test light global; entity
256
252
  const camera = this.cameras[this.mainCameraParams.type];
257
253
  let newLight = new SpotLight(camera, this.inputHandler);
258
254
  newLight.prepareBuffer(this.device);
259
255
  this.lightContainer.push(newLight);
260
- console.log('Add light : ', newLight);
256
+ console.log(`%cAdd light: ${newLight}`, LOG_FUNNY_SMALL);
261
257
  }
262
258
 
263
259
  addMeshObj = (o, clearColor = this.options.clearColor) => {
@@ -289,18 +285,14 @@ export default class MatrixEngineWGPU {
289
285
  if(typeof o.physics.scale === 'undefined') {o.physics.scale = o.scale;}
290
286
  if(typeof o.physics.rotation === 'undefined') {o.physics.rotation = o.rotation;}
291
287
  o.physics.position = o.position;
292
- // console.log('Mesh procedure', o)
293
- // TEST OBJS SEQ ANIMS
294
288
  if(typeof o.objAnim == 'undefined' || typeof o.objAnim == null) {
295
289
  o.objAnim = null;
296
290
  } else {
297
- // console.log('o.anim', o.objAnim)
298
291
  if(typeof o.objAnim.animations !== 'undefined') {
299
292
  o.objAnim.play = play;
300
293
  }
301
294
  // no need for single test it in future
302
295
  o.objAnim.meshList = o.objAnim.meshList;
303
-
304
296
  if(typeof o.mesh === 'undefined') {
305
297
  o.mesh = o.objAnim.meshList[0];
306
298
  console.info('objSeq animation is active.');
@@ -314,7 +306,7 @@ export default class MatrixEngineWGPU {
314
306
  }
315
307
  }
316
308
  let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o);
317
- myMesh1.lightContainer = this.lightContainer;
309
+ myMesh1.spotlightUniformBuffer = this.spotlightUniformBuffer;
318
310
  myMesh1.inputHandler = this.inputHandler;
319
311
  myMesh1.clearColor = clearColor;
320
312
  if(o.physics.enabled == true) {
@@ -340,9 +332,6 @@ export default class MatrixEngineWGPU {
340
332
  if(!this.lastFrameMS) {dt = 16;}
341
333
  this.lastFrameMS = now;
342
334
  const camera = this.cameras[this.mainCameraParams.type];
343
-
344
- // engine, once per frame
345
- // const camera = this.cameras[this.mainCameraParams.type];
346
335
  camera.update(dt, this.inputHandler());
347
336
  const camVP = mat4.multiply(camera.projectionMatrix, camera.view); // P * V
348
337
 
@@ -356,27 +345,19 @@ export default class MatrixEngineWGPU {
356
345
  camVP.byteLength
357
346
  );
358
347
  }
359
- // engine frame
360
- // camera.update(dt, this.inputHandler());
361
- // const camVP = mat4.multiply(camera.projectionMatrix, camera.view);
362
-
363
- // for(const mesh of this.mainRenderBundle) {
364
- // // Light’s viewProj should come from your SpotLight
365
- // // If you have multiple lights, you’ll need an array UBO or multiple passes.
366
- // const sceneData = new Float32Array(16 + 16 + 4); // lightVP, camVP, lightPos(+pad)
367
- // sceneData.set(this.lightContainer[0].viewProjMatrix, 0);
368
- // sceneData.set(camVP, 16);
369
- // sceneData.set(this.lightContainer[0].position, 32);
370
-
371
- // // sceneUniformBuffer
372
- // this.device.queue.writeBuffer(
373
- // mesh.sceneUniformBuffer, // or a shared one if/when you centralize it
374
- // 0,
375
- // sceneData.buffer,
376
- // sceneData.byteOffset,
377
- // sceneData.byteLength
378
- // );
379
- // }
348
+ }
349
+
350
+ updateLights() {
351
+ // Update buffer every frame
352
+ const data = new Float32Array(this.MAX_SPOTLIGHTS * 20);
353
+ for(let i = 0;i < this.MAX_SPOTLIGHTS;i++) {
354
+ if(i < this.lightContainer.length) {
355
+ data.set(this.lightContainer[i].getLightDataBuffer(), i * 20);
356
+ } else {
357
+ data.set(new Float32Array(20), i * 20);
358
+ }
359
+ }
360
+ this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, data.buffer);
380
361
  }
381
362
 
382
363
  frameSinglePass = () => {
@@ -389,35 +370,24 @@ export default class MatrixEngineWGPU {
389
370
  let renderPass;
390
371
  let commandEncoder = this.device.createCommandEncoder();
391
372
 
373
+ this.updateLights()
392
374
  this.test()
375
+
393
376
  // 1️⃣ Update light data (position, direction, uniforms)
394
377
  for(const light of this.lightContainer) {
395
- light.updateLightBuffer();
396
378
  this.mainRenderBundle.forEach((meItem, index) => {
379
+
397
380
  light.updateSceneUniforms(this.mainRenderBundle, this.cameras.WASD);
398
381
  })
399
382
  }
400
383
 
401
384
  this.mainRenderBundle.forEach((meItem, index) => {meItem.position.update()})
402
385
  if(this.matrixAmmo) this.matrixAmmo.updatePhysics();
403
-
404
- // no cast WORKING
405
- // this.mainRenderBundle.forEach((meItem, index) => {
406
- // meItem.draw(commandEncoder);
407
-
408
- // shadowPass = commandEncoder.beginRenderPass(meItem.shadowPassDescriptor);
409
- // shadowPass.setPipeline(meItem.shadowPipeline);
410
- // meItem.drawShadows(shadowPass);
411
- // shadowPass.end();
412
- // })
413
-
414
- // cast!
415
386
  const firstItem = this.mainRenderBundle[0];
416
387
  shadowPass = commandEncoder.beginRenderPass(firstItem.shadowPassDescriptor);
417
388
  shadowPass.setPipeline(firstItem.shadowPipeline);
418
389
  for(const meItem of this.mainRenderBundle) {
419
- // meItem.draw(commandEncoder);
420
- meItem.drawShadows(shadowPass); // Draw ALL objects
390
+ meItem.drawShadows(shadowPass);
421
391
  }
422
392
  shadowPass.end();
423
393
 
@@ -441,7 +411,7 @@ export default class MatrixEngineWGPU {
441
411
  this.device.queue.submit([commandEncoder.finish()]);
442
412
  requestAnimationFrame(this.frame);
443
413
  } catch(err) {
444
- console.log('%cDraw func (err):' + err, LOG_WARN)
414
+ console.log('%cLoop (err):' + err, LOG_WARN)
445
415
  requestAnimationFrame(this.frame);
446
416
  }
447
417
  }
@@ -456,7 +426,7 @@ export default class MatrixEngineWGPU {
456
426
  if(meItem.renderPassDescriptor) meItem.renderPassDescriptor.colorAttachments[0].loadOp = 'load';
457
427
  }
458
428
  // Update transforms, physics, etc. (optional)
459
- meItem.draw(commandEncoder); // optional: if this does per-frame updates
429
+ meItem.draw(commandEncoder);
460
430
  if(meItem.renderBundle) {
461
431
  // Set up view per object
462
432
  meItem.renderPassDescriptor.colorAttachments[0].view =
@@ -465,7 +435,7 @@ export default class MatrixEngineWGPU {
465
435
  passEncoder.executeBundles([meItem.renderBundle]); // ✅ Use only this bundle
466
436
  passEncoder.end();
467
437
  } else {
468
- meItem.draw(commandEncoder); // fallback if no renderBundle
438
+ meItem.draw(commandEncoder);
469
439
  }
470
440
  });
471
441
  this.device.queue.submit([commandEncoder.finish()]);