matrix-engine-wgpu 1.3.19 → 1.4.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.
@@ -95,6 +95,7 @@ var loadCameraTexture = function () {
95
95
  a: 1
96
96
  }
97
97
  }, () => {
98
+ cameraTexture.addLight();
98
99
  addEventListener('AmmoReady', () => {
99
100
  (0, _loaderObj.downloadMeshes)({
100
101
  welcomeText: "./res/meshes/blender/piramyd.obj",
@@ -158,8 +159,9 @@ exports.loadObjFile = void 0;
158
159
  var _world = _interopRequireDefault(require("../src/world.js"));
159
160
  var _loaderObj = require("../src/engine/loader-obj.js");
160
161
  var _utils = require("../src/engine/utils.js");
161
- var _raycast = require("../src/engine/raycast.js");
162
162
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
163
+ // import {addRaycastsAABBListener} from "../src/engine/raycast.js";
164
+
163
165
  var loadObjFile = function () {
164
166
  let loadObjFile = new _world.default({
165
167
  useSingleRenderPass: true,
@@ -175,8 +177,6 @@ var loadObjFile = function () {
175
177
  a: 1
176
178
  }
177
179
  }, () => {
178
- // loadObjFile.addLight()
179
-
180
180
  addEventListener('AmmoReady', () => {
181
181
  (0, _loaderObj.downloadMeshes)({
182
182
  ball: "./res/meshes/blender/sphere.obj",
@@ -187,7 +187,7 @@ var loadObjFile = function () {
187
187
  (0, _loaderObj.downloadMeshes)({
188
188
  cube: "./res/meshes/blender/cube.obj"
189
189
  }, onGround, {
190
- scale: [25, 0.1, 25]
190
+ scale: [20, 1, 20]
191
191
  });
192
192
 
193
193
  // loadObjFile.addLight();
@@ -223,13 +223,13 @@ var loadObjFile = function () {
223
223
  function onLoadObj(m) {
224
224
  loadObjFile.myLoadedMeshes = m;
225
225
  for (var key in m) {
226
- console.log(`%c Loaded objs: ${key} `, _utils.LOG_MATRIX);
226
+ // console.log(`%c Loaded objs: ${key} `, LOG_MATRIX);
227
227
  }
228
228
  loadObjFile.addMeshObj({
229
229
  position: {
230
230
  x: 0,
231
231
  y: 2,
232
- z: -10
232
+ z: -20
233
233
  },
234
234
  rotation: {
235
235
  x: 0,
@@ -245,16 +245,16 @@ var loadObjFile = function () {
245
245
  name: 'cube1',
246
246
  mesh: m.cube,
247
247
  physics: {
248
- enabled: true,
248
+ enabled: false,
249
249
  geometry: "Cube"
250
250
  }
251
251
  // raycast: { enabled: true , radius: 2 }
252
252
  });
253
253
  loadObjFile.addMeshObj({
254
254
  position: {
255
- x: 3,
256
- y: 2,
257
- z: -15
255
+ x: 0,
256
+ y: -1,
257
+ z: -20
258
258
  },
259
259
  rotation: {
260
260
  x: 0,
@@ -270,7 +270,7 @@ var loadObjFile = function () {
270
270
  name: 'ball1',
271
271
  mesh: m.ball,
272
272
  physics: {
273
- enabled: true,
273
+ enabled: false,
274
274
  geometry: "Sphere"
275
275
  }
276
276
  // raycast: { enabled: true , radius: 2 }
@@ -278,6 +278,7 @@ var loadObjFile = function () {
278
278
  var TEST = loadObjFile.getSceneObjectByName('cube2');
279
279
  console.log(`%c Test access scene ${TEST} object.`, _utils.LOG_MATRIX);
280
280
  loadObjFile.addLight();
281
+ // loadObjFile.addLight();
281
282
  }
282
283
  });
283
284
  // just for dev
@@ -285,7 +286,7 @@ var loadObjFile = function () {
285
286
  };
286
287
  exports.loadObjFile = loadObjFile;
287
288
 
288
- },{"../src/engine/loader-obj.js":12,"../src/engine/raycast.js":16,"../src/engine/utils.js":17,"../src/world.js":26}],4:[function(require,module,exports){
289
+ },{"../src/engine/loader-obj.js":12,"../src/engine/utils.js":17,"../src/world.js":26}],4:[function(require,module,exports){
289
290
  "use strict";
290
291
 
291
292
  Object.defineProperty(exports, "__esModule", {
@@ -306,6 +307,12 @@ var loadObjsSequence = function () {
306
307
  }
307
308
  }, () => {
308
309
  addEventListener('AmmoReady', () => {
310
+ // requied now
311
+ loadObjFile.addLight();
312
+
313
+ // adapt
314
+ app.lightContainer[0].position[2] = -5;
315
+ app.lightContainer[0].position[1] = 22;
309
316
  (0, _loaderObj.downloadMeshes)((0, _loaderObj.makeObjSeqArg)({
310
317
  id: "swat-walk-pistol",
311
318
  path: "res/meshes/objs-sequence/swat-walk-pistol",
@@ -442,6 +449,7 @@ var _loaderObj = require("../src/engine/loader-obj.js");
442
449
  var _utils = require("../src/engine/utils.js");
443
450
  var _raycast = require("../src/engine/raycast.js");
444
451
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
452
+ // @group(0) @binding(5) var<uniform> postFXMode: u32;
445
453
  var loadVideoTexture = function () {
446
454
  let videoTexture = new _world.default({
447
455
  useSingleRenderPass: true,
@@ -457,6 +465,10 @@ var loadVideoTexture = function () {
457
465
  a: 1
458
466
  }
459
467
  }, () => {
468
+ // For now one light perscene must be added.
469
+ // if you dont wanna light just use intesity = 0
470
+ // videoTexture is app main instance
471
+ videoTexture.addLight();
460
472
  addEventListener('AmmoReady', () => {
461
473
  (0, _loaderObj.downloadMeshes)({
462
474
  welcomeText: "./res/meshes/blender/piramyd.obj",
@@ -498,13 +510,11 @@ var loadVideoTexture = function () {
498
510
  // raycast: { enabled: true , radius: 2 }
499
511
  });
500
512
  var TEST = videoTexture.getSceneObjectByName('MyVideoTex');
501
- setTimeout(() => {
502
- console.log(`%c Test video-texture...`, _utils.LOG_MATRIX);
503
- TEST.loadVideoTexture({
504
- type: 'video',
505
- src: 'res/videos/tunel.mp4'
506
- });
507
- }, 4000);
513
+ console.log(`%c Test video-texture...`, _utils.LOG_MATRIX);
514
+ TEST.loadVideoTexture({
515
+ type: 'video',
516
+ src: 'res/videos/tunel.mp4'
517
+ });
508
518
  }
509
519
  });
510
520
  window.app = videoTexture;
@@ -6733,7 +6743,7 @@ class CameraBase {
6733
6743
  set matrix(mat) {
6734
6744
  _wgpuMatrix.mat4.copy(mat, this.matrix_);
6735
6745
  }
6736
- setProjection(fov = 2 * Math.PI / 5, aspect = 1, near = 1, far = 1000) {
6746
+ setProjection(fov = 2 * Math.PI / 5, aspect = 1, near = 0.5, far = 1000) {
6737
6747
  this.projectionMatrix = _wgpuMatrix.mat4.perspective(fov, aspect, near, far);
6738
6748
  }
6739
6749
 
@@ -7119,12 +7129,16 @@ Object.defineProperty(exports, "__esModule", {
7119
7129
  });
7120
7130
  exports.SpotLight = void 0;
7121
7131
  var _wgpuMatrix = require("wgpu-matrix");
7132
+ var _vertexShadow = require("../shaders/vertexShadow.wgsl");
7133
+ /**
7134
+ * @description
7135
+ * Spot light with shodow cast.
7136
+ * @author Nikola Lukic
7137
+ * @email zlatnaspirala@gmail.com
7138
+ */
7122
7139
  class SpotLight {
7123
- // injected
7124
7140
  camera;
7125
7141
  inputHandler;
7126
-
7127
- // Light
7128
7142
  position;
7129
7143
  target;
7130
7144
  up;
@@ -7139,15 +7153,29 @@ class SpotLight {
7139
7153
  innerCutoff;
7140
7154
  outerCutoff;
7141
7155
  spotlightUniformBuffer;
7142
- constructor(camera, inputHandler, position = _wgpuMatrix.vec3.create(0, 5, -10), target = _wgpuMatrix.vec3.create(0, 0, 0), fov = 45, aspect = 1.0, near = 0.1, far = 200) {
7156
+ constructor(camera, inputHandler, device, position = _wgpuMatrix.vec3.create(0, 10, -20), target = _wgpuMatrix.vec3.create(0, 0, -20), fov = 45, aspect = 1.0, near = 0.1, far = 200) {
7157
+ this.fov = fov;
7158
+ this.aspect = aspect;
7159
+ this.near = near;
7160
+ this.far = far;
7143
7161
  this.camera = camera;
7144
7162
  this.inputHandler = inputHandler;
7145
7163
  this.position = position;
7146
7164
  this.target = target;
7147
- this.up = _wgpuMatrix.vec3.create(0, 1, 0);
7165
+ this.up = _wgpuMatrix.vec3.create(0, 0, -1);
7148
7166
  this.direction = _wgpuMatrix.vec3.normalize(_wgpuMatrix.vec3.subtract(target, position));
7167
+ this.intensity = 1.0;
7168
+ this.color = _wgpuMatrix.vec3.create(1.0, 1.0, 1.0); // white
7169
+
7149
7170
  this.viewMatrix = _wgpuMatrix.mat4.lookAt(position, target, this.up);
7150
- this.projectionMatrix = _wgpuMatrix.mat4.perspective(fov * Math.PI / 180, aspect, near, far);
7171
+ this.projectionMatrix = _wgpuMatrix.mat4.perspective(this.fov * Math.PI / 180, this.aspect, this.near, this.far);
7172
+ this.setProjection = function (fov = 45, aspect = 1.0, near = 0.1, far = 200) {
7173
+ this.projectionMatrix = _wgpuMatrix.mat4.perspective(fov, aspect, near, far);
7174
+ };
7175
+ this.updateProjection = function () {
7176
+ this.projectionMatrix = _wgpuMatrix.mat4.perspective(this.fov, this.aspect, this.near, this.far);
7177
+ };
7178
+ this.device = device;
7151
7179
  this.viewProjMatrix = _wgpuMatrix.mat4.multiply(this.projectionMatrix, this.viewMatrix);
7152
7180
  this.fov = fov;
7153
7181
  this.aspect = aspect;
@@ -7155,77 +7183,145 @@ class SpotLight {
7155
7183
  this.far = far;
7156
7184
  this.innerCutoff = Math.cos(Math.PI / 180 * 12.5);
7157
7185
  this.outerCutoff = Math.cos(Math.PI / 180 * 17.5);
7186
+ this.ambientFactor = 0.5;
7187
+ this.range = 20.0;
7188
+ this.shadowBias = 0.01;
7189
+ this.SHADOW_RES = 1024;
7190
+ this.primitive = {
7191
+ topology: 'triangle-list',
7192
+ cullMode: 'back',
7193
+ // for front interest border drawen shadows !
7194
+ frontFace: 'ccw'
7195
+ };
7196
+ this.shadowTexture = this.device.createTexture({
7197
+ label: 'shadowTexture[light]',
7198
+ size: [this.SHADOW_RES, this.SHADOW_RES, 1],
7199
+ format: "depth32float",
7200
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
7201
+ });
7202
+ this.shadowSampler = device.createSampler({
7203
+ label: 'shadowSampler[light]',
7204
+ compare: 'less',
7205
+ magFilter: 'linear',
7206
+ minFilter: 'linear'
7207
+ });
7208
+ this.renderPassDescriptor = {
7209
+ label: "renderPassDescriptor shadowPass [per SpotLigth]",
7210
+ colorAttachments: [],
7211
+ depthStencilAttachment: {
7212
+ view: this.shadowTexture.createView(),
7213
+ depthClearValue: 1.0,
7214
+ depthLoadOp: "clear",
7215
+ depthStoreOp: "store"
7216
+ }
7217
+ };
7218
+ this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
7219
+ label: 'modelBindGroup in light',
7220
+ entries: [{
7221
+ binding: 0,
7222
+ visibility: GPUShaderStage.VERTEX,
7223
+ buffer: {
7224
+ type: 'uniform'
7225
+ }
7226
+ }]
7227
+ });
7228
+ this.shadowBindGroupContainer = [];
7229
+ this.getShadowBindGroup = (mesh, index) => {
7230
+ if (this.shadowBindGroupContainer[index]) {
7231
+ return this.shadowBindGroupContainer[index];
7232
+ }
7233
+ this.shadowBindGroupContainer[index] = this.device.createBindGroup({
7234
+ label: 'sceneBindGroupForShadow in light',
7235
+ layout: this.uniformBufferBindGroupLayout,
7236
+ entries: [{
7237
+ binding: 0,
7238
+ resource: {
7239
+ buffer: mesh.sceneUniformBuffer
7240
+ }
7241
+ }]
7242
+ });
7243
+ return this.shadowBindGroupContainer[index];
7244
+ };
7245
+ this.modelBindGroupLayout = this.device.createBindGroupLayout({
7246
+ entries: [{
7247
+ binding: 0,
7248
+ visibility: GPUShaderStage.VERTEX,
7249
+ buffer: {
7250
+ type: 'uniform'
7251
+ }
7252
+ }]
7253
+ });
7254
+ this.shadowPipeline = this.device.createRenderPipeline({
7255
+ label: 'shadowPipeline per light',
7256
+ layout: this.device.createPipelineLayout({
7257
+ label: 'createPipelineLayout - uniformBufferBindGroupLayout',
7258
+ bindGroupLayouts: [this.uniformBufferBindGroupLayout, this.modelBindGroupLayout]
7259
+ }),
7260
+ vertex: {
7261
+ module: this.device.createShaderModule({
7262
+ code: _vertexShadow.vertexShadowWGSL
7263
+ }),
7264
+ buffers: [{
7265
+ arrayStride: 12,
7266
+ // 3 * 4 bytes (vec3f)
7267
+ attributes: [{
7268
+ shaderLocation: 0,
7269
+ // must match @location(0) in vertex shader
7270
+ offset: 0,
7271
+ format: "float32x3"
7272
+ }]
7273
+ }]
7274
+ },
7275
+ depthStencil: {
7276
+ depthWriteEnabled: true,
7277
+ depthCompare: 'less',
7278
+ format: 'depth32float'
7279
+ },
7280
+ primitive: this.primitive
7281
+ });
7282
+ this.getMainPassBindGroup = function (mesh) {
7283
+ // You can cache it per mesh to avoid recreating each frame
7284
+ if (!this.mainPassBindGroupContainer) this.mainPassBindGroupContainer = [];
7285
+ const index = mesh._lightBindGroupIndex || 0; // assign unique per mesh if needed
7286
+ if (this.mainPassBindGroupContainer[index]) {
7287
+ return this.mainPassBindGroupContainer[index];
7288
+ }
7289
+ this.mainPassBindGroupContainer[index] = this.device.createBindGroup({
7290
+ label: `mainPassBindGroup for mesh`,
7291
+ layout: mesh.mainPassBindGroupLayout,
7292
+ // this should match the pipeline
7293
+ entries: [{
7294
+ binding: 0,
7295
+ // must match @binding in shader for shadow texture
7296
+ resource: this.shadowTexture.createView()
7297
+ }, {
7298
+ binding: 1,
7299
+ // must match @binding in shader for shadow sampler
7300
+ resource: this.shadowSampler
7301
+ }]
7302
+ });
7303
+ return this.mainPassBindGroupContainer[index];
7304
+ };
7158
7305
  }
7159
7306
  update() {
7160
- // this.direction = vec3.normalize(vec3.subtract(this.target, this.position));
7161
- // this.viewMatrix = mat4.lookAt(this.position, this.target, this.up);
7162
- // this.viewProjMatrix = mat4.multiply(this.projectionMatrix, this.viewMatrix);
7163
- // console.log('test light update this.target : ', this.target)
7164
- // Use the existing direction
7307
+ // this.target = vec3.create(x, y, z); // new target
7308
+ this.direction = _wgpuMatrix.vec3.normalize(_wgpuMatrix.vec3.subtract(this.target, this.position));
7165
7309
  const target = _wgpuMatrix.vec3.add(this.position, this.direction);
7166
7310
  this.viewMatrix = _wgpuMatrix.mat4.lookAt(this.position, target, this.up);
7167
7311
  this.viewProjMatrix = _wgpuMatrix.mat4.multiply(this.projectionMatrix, this.viewMatrix);
7168
7312
  }
7169
- updateSceneUniforms(mainRenderBundle) {
7170
- const now = Date.now();
7171
- // First frame safety
7172
- let dt = (now - this.lastFrameMS) / 1000;
7173
- if (!this.lastFrameMS) {
7174
- dt = 16;
7175
- }
7176
- this.lastFrameMS = now;
7177
- // engine, once per frame
7178
- this.camera.update(dt, this.inputHandler());
7179
- const camVP = _wgpuMatrix.mat4.multiply(this.camera.projectionMatrix, this.camera.view); // P * V
7180
-
7181
- for (const mesh of mainRenderBundle) {
7182
- // scene buffer layout = 0..63 lightVP, 64..127 camVP, 128..143 lightPos(+pad)
7183
- this.device.queue.writeBuffer(mesh.sceneUniformBuffer, 64,
7184
- // cameraViewProjMatrix offset
7185
- camVP.buffer, camVP.byteOffset, camVP.byteLength);
7186
- }
7187
- // const camVP = mat4.multiply(camera.projectionMatrix, camera.view);
7188
- // const sceneData = new Float32Array(36); // 16 + 16 + 4
7189
- // sceneData.set(this.viewProjMatrix, 0);
7190
- // sceneData.set(camVP, 16);
7191
- // sceneData.set(this.position, 32);
7192
- // if(!this.device) {
7193
- // console.warn("Device not set for SpotLight");
7194
- // return;
7195
- // }
7196
- // this.device.queue.writeBuffer(
7197
- // sceneUniformBuffer,
7198
- // // this.spotlightUniformBuffer,
7199
- // 0,
7200
- // sceneData.buffer,
7201
- // sceneData.byteOffset,
7202
- // sceneData.byteLength
7203
- // );
7204
- }
7205
- prepareBuffer(device) {
7206
- if (!this.device) this.device = device;
7207
- this.spotlightUniformBuffer = this.device.createBuffer({
7208
- size: 16 * 4,
7209
- // 64 bytes
7210
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
7211
- });
7212
- const spotlightData = this.getLightDataBuffer();
7213
- this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, spotlightData.buffer, spotlightData.byteOffset, spotlightData.byteLength);
7214
- }
7215
- updateLightBuffer() {
7216
- if (!this.device || !this.spotlightUniformBuffer) {
7217
- return;
7218
- }
7219
- const spotlightData = this.getLightDataBuffer();
7220
- this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, spotlightData.buffer, spotlightData.byteOffset, spotlightData.byteLength);
7221
- }
7222
7313
  getLightDataBuffer() {
7223
- return new Float32Array([...this.position, 0.0, ...this.direction, 0.0, this.innerCutoff, this.outerCutoff, 0.0, 0.0]);
7314
+ const m = this.viewProjMatrix;
7315
+ return new Float32Array([...this.position, 0.0, ...this.direction, 0.0, this.innerCutoff, this.outerCutoff, this.intensity, 0.0, ...this.color, 0.0, this.range, this.ambientFactor, this.shadowBias,
7316
+ // <<--- use shadowBias
7317
+ 0.0,
7318
+ // keep padding
7319
+ ...m]);
7224
7320
  }
7225
7321
  }
7226
7322
  exports.SpotLight = SpotLight;
7227
7323
 
7228
- },{"wgpu-matrix":7}],12:[function(require,module,exports){
7324
+ },{"../shaders/vertexShadow.wgsl":24,"wgpu-matrix":7}],12:[function(require,module,exports){
7229
7325
  "use strict";
7230
7326
 
7231
7327
  Object.defineProperty(exports, "__esModule", {
@@ -7712,8 +7808,17 @@ class Materials {
7712
7808
  constructor(device) {
7713
7809
  this.device = device;
7714
7810
  this.isVideo = false;
7811
+ this.videoIsReady = 'NONE';
7812
+ // this.compareSampler = this.device.createSampler({compare: 'less'});
7715
7813
  this.compareSampler = this.device.createSampler({
7716
- compare: 'less'
7814
+ compare: 'less-equal',
7815
+ // safer for shadow comparison
7816
+ addressModeU: 'clamp-to-edge',
7817
+ // prevents UV leaking outside
7818
+ addressModeV: 'clamp-to-edge',
7819
+ magFilter: 'linear',
7820
+ // smooth PCF
7821
+ minFilter: 'linear'
7717
7822
  });
7718
7823
  // For image textures (standard sampler)
7719
7824
  this.imageSampler = this.device.createSampler({
@@ -7735,7 +7840,7 @@ class Materials {
7735
7840
 
7736
7841
  // Dymmy buffer
7737
7842
  this.dummySpotlightUniformBuffer = this.device.createBuffer({
7738
- size: 64,
7843
+ size: 80,
7739
7844
  // Must match size in shader
7740
7845
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
7741
7846
  });
@@ -7768,7 +7873,8 @@ class Materials {
7768
7873
  });
7769
7874
  }
7770
7875
  async loadVideoTexture(arg) {
7771
- this.isVideo = true;
7876
+ // this.isVideo = true;
7877
+ this.videoIsReady = 'MAYBE';
7772
7878
  if (arg.type === 'video') {
7773
7879
  this.video = document.createElement('video');
7774
7880
  this.video.src = arg.src || 'res/videos/tunel.mp4';
@@ -7777,7 +7883,11 @@ class Materials {
7777
7883
  this.video.loop = true;
7778
7884
  document.body.append(this.video);
7779
7885
  this.video.style.display = 'none';
7886
+ this.video.style.position = 'absolute';
7887
+ this.video.style.top = '50px';
7888
+ this.video.style.left = '50px';
7780
7889
  await this.video.play();
7890
+ this.isVideo = true;
7781
7891
  } else if (arg.type === 'videoElement') {
7782
7892
  this.video = arg.el;
7783
7893
  await this.video.play();
@@ -7802,6 +7912,7 @@ class Materials {
7802
7912
  });
7803
7913
  this.video.srcObject = stream;
7804
7914
  await this.video.play();
7915
+ this.isVideo = true;
7805
7916
  } catch (err) {
7806
7917
  console.error("❌ Failed to access camera:", err);
7807
7918
  return;
@@ -7821,6 +7932,7 @@ class Materials {
7821
7932
  }
7822
7933
  this.video.srcObject = stream;
7823
7934
  await this.video.play();
7935
+ this.isVideo = true;
7824
7936
  } else if (arg.type === 'canvas2d-inline') {
7825
7937
  // Miniature inline-drawn canvas created dynamically
7826
7938
  const canvas = document.createElement('canvas');
@@ -7841,6 +7953,7 @@ class Materials {
7841
7953
  this.video.playsInline = true;
7842
7954
  this.video.style.display = 'none';
7843
7955
  document.body.append(this.video);
7956
+ this.isVideo = true;
7844
7957
  const stream = canvas.captureStream?.() || canvas.mozCaptureStream?.();
7845
7958
  if (!stream) {
7846
7959
  console.error('❌ Cannot capture stream from inline canvas');
@@ -7854,27 +7967,37 @@ class Materials {
7854
7967
  minFilter: 'linear'
7855
7968
  });
7856
7969
 
7857
- // ✅ Now
7970
+ // ✅ Now - maybe noT
7858
7971
  this.createLayoutForRender();
7859
- this.setupPipeline();
7860
- setTimeout(() => this.createBindGroupForRender(), 1500);
7861
7972
  }
7862
7973
  updateVideoTexture() {
7863
7974
  if (!this.video || this.video.readyState < 2) return;
7864
- this.externalTexture = this.device.importExternalTexture({
7865
- source: this.video
7866
- });
7867
- this.createBindGroupForRender();
7975
+ if (!this.externalTexture) {
7976
+ // create it once
7977
+ this.externalTexture = this.device.importExternalTexture({
7978
+ source: this.video
7979
+ });
7980
+ this.createBindGroupForRender();
7981
+ this.videoIsReady = 'YES';
7982
+ console.log("✅ video bind group created [createBindGroupForRender()]");
7983
+ } else {
7984
+ this.externalTexture = this.device.importExternalTexture({
7985
+ source: this.video
7986
+ });
7987
+ this.createBindGroupForRender();
7988
+ }
7868
7989
  }
7869
7990
  createBindGroupForRender() {
7870
- const textureResource = this.isVideo ? this.externalTexture // must be set via updateVideoTexture
7871
- : this.texture0.createView();
7872
- if (!textureResource || !this.sceneUniformBuffer || !this.shadowDepthTextureView || !this.sampler) {
7873
- console.warn("❗Missing res skipping...");
7991
+ const textureResource = this.isVideo ? this.externalTexture : this.texture0.createView();
7992
+ if (!textureResource || !this.sceneUniformBuffer || !this.shadowDepthTextureView) {
7993
+ if (!textureResource) console.warn("❗Missing res texture: ", textureResource);
7994
+ if (!this.sceneUniformBuffer) console.warn("❗Missing res: this.sceneUniformBuffer: ", this.sceneUniformBuffer);
7995
+ if (!this.shadowDepthTextureView) console.warn("❗Missing res: this.shadowDepthTextureView: ", this.shadowDepthTextureView);
7996
+ if (typeof textureResource === 'undefined') this.updateVideoTexture();
7874
7997
  return;
7875
- }
7876
- // console.log('what is this.lightContainer.length ', this.lightContainer.length)
7998
+ } else {}
7877
7999
  if (this.isVideo == true) {
8000
+ console.info("✅ video sceneBindGroupForRender ");
7878
8001
  this.sceneBindGroupForRender = this.device.createBindGroup({
7879
8002
  layout: this.bglForRender,
7880
8003
  entries: [{
@@ -7901,6 +8024,10 @@ class Materials {
7901
8024
  }
7902
8025
  }]
7903
8026
  });
8027
+
8028
+ // special case for video meybe better solution exist
8029
+ // this.setupPipeline();
8030
+ this.video.play();
7904
8031
  } else {
7905
8032
  this.sceneBindGroupForRender = this.device.createBindGroup({
7906
8033
  layout: this.bglForRender,
@@ -7924,73 +8051,94 @@ class Materials {
7924
8051
  }, {
7925
8052
  binding: 5,
7926
8053
  resource: {
7927
- buffer: this.lightContainer.length == 0 ? this.dummySpotlightUniformBuffer : this.lightContainer[0].spotlightUniformBuffer
8054
+ buffer: !this.spotlightUniformBuffer ? this.dummySpotlightUniformBuffer : this.spotlightUniformBuffer
7928
8055
  }
7929
8056
  }]
7930
8057
  });
7931
8058
  }
7932
8059
  }
7933
8060
  createLayoutForRender() {
8061
+ if (this.isVideo == true) {
8062
+ console.info("✅ createLayoutForRender video [bglForRender]");
8063
+ } else {
8064
+ console.info("✅ normal createLayoutForRender [bglForRender]");
8065
+ }
8066
+ let e = [{
8067
+ binding: 0,
8068
+ visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
8069
+ buffer: {
8070
+ type: 'uniform'
8071
+ }
8072
+ }, ...(this.isVideo == false ? [{
8073
+ binding: 1,
8074
+ visibility: GPUShaderStage.FRAGMENT,
8075
+ texture: {
8076
+ sampleType: "depth",
8077
+ viewDimension: "2d-array",
8078
+ // <- must match shadowMapArray
8079
+ multisampled: false
8080
+ }
8081
+ }] : [{
8082
+ binding: 1,
8083
+ visibility: GPUShaderStage.FRAGMENT,
8084
+ texture: {
8085
+ sampleType: "depth",
8086
+ viewDimension: "2d"
8087
+ }
8088
+ }]), {
8089
+ binding: 2,
8090
+ visibility: GPUShaderStage.FRAGMENT,
8091
+ sampler: {
8092
+ type: 'comparison'
8093
+ }
8094
+ }, ...(this.isVideo ? [
8095
+ // VIDEO
8096
+ {
8097
+ binding: 3,
8098
+ visibility: GPUShaderStage.FRAGMENT,
8099
+ externalTexture: {}
8100
+ }, {
8101
+ binding: 4,
8102
+ visibility: GPUShaderStage.FRAGMENT,
8103
+ sampler: {
8104
+ type: 'filtering'
8105
+ } // for video sampling
8106
+ }, {
8107
+ binding: 5,
8108
+ visibility: GPUShaderStage.FRAGMENT,
8109
+ buffer: {
8110
+ type: 'uniform'
8111
+ }
8112
+ }] : [
8113
+ // IMAGE
8114
+ {
8115
+ binding: 3,
8116
+ visibility: GPUShaderStage.FRAGMENT,
8117
+ texture: {
8118
+ sampleType: 'float',
8119
+ viewDimension: '2d'
8120
+ }
8121
+ }, {
8122
+ binding: 4,
8123
+ visibility: GPUShaderStage.FRAGMENT,
8124
+ sampler: {
8125
+ type: 'filtering'
8126
+ }
8127
+ }, {
8128
+ binding: 5,
8129
+ visibility: GPUShaderStage.FRAGMENT,
8130
+ buffer: {
8131
+ type: 'uniform'
8132
+ }
8133
+ }])];
8134
+ console.log("BG E : ", e);
7934
8135
  this.bglForRender = this.device.createBindGroupLayout({
7935
- entries: [{
7936
- binding: 0,
7937
- visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
7938
- buffer: {
7939
- type: 'uniform'
7940
- }
7941
- }, {
7942
- binding: 1,
7943
- visibility: GPUShaderStage.FRAGMENT,
7944
- texture: {
7945
- sampleType: 'depth'
7946
- }
7947
- }, {
7948
- binding: 2,
7949
- visibility: GPUShaderStage.FRAGMENT,
7950
- sampler: {
7951
- type: 'comparison'
7952
- }
7953
- }, ...(this.isVideo ? [
7954
- // VIDEO
7955
- {
7956
- binding: 3,
7957
- visibility: GPUShaderStage.FRAGMENT,
7958
- externalTexture: {}
7959
- }, {
7960
- binding: 4,
7961
- visibility: GPUShaderStage.FRAGMENT,
7962
- sampler: {
7963
- type: 'filtering'
7964
- } // for video sampling
7965
- }, {
7966
- binding: 5,
7967
- visibility: GPUShaderStage.FRAGMENT,
7968
- buffer: {
7969
- type: 'uniform'
7970
- }
7971
- }] : [
7972
- // IMAGE
7973
- {
7974
- binding: 3,
7975
- visibility: GPUShaderStage.FRAGMENT,
7976
- texture: {
7977
- sampleType: 'float',
7978
- viewDimension: '2d'
7979
- }
7980
- }, {
7981
- binding: 4,
7982
- visibility: GPUShaderStage.FRAGMENT,
7983
- sampler: {
7984
- type: 'filtering'
7985
- }
7986
- }, {
7987
- binding: 5,
7988
- visibility: GPUShaderStage.FRAGMENT,
7989
- buffer: {
7990
- type: 'uniform'
7991
- }
7992
- }])]
8136
+ label: 'bglForRender',
8137
+ entries: e
7993
8138
  });
8139
+ if (this.isVideo == true) {
8140
+ this.createBindGroupForRender();
8141
+ }
7994
8142
  }
7995
8143
  }
7996
8144
  exports.default = Materials;
@@ -8248,9 +8396,9 @@ var _materials = _interopRequireDefault(require("./materials"));
8248
8396
  var _fragmentVideo = require("../shaders/fragment.video.wgsl");
8249
8397
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
8250
8398
  class MEMeshObj extends _materials.default {
8251
- constructor(canvas, device, context, o, sceneUniformBuffer) {
8399
+ constructor(canvas, device, context, o, inputHandler) {
8252
8400
  super(device);
8253
- if (typeof o.name === 'undefined') o.name = (0, _utils.genName)(9);
8401
+ if (typeof o.name === 'undefined') o.name = (0, _utils.genName)(3);
8254
8402
  if (typeof o.raycast === 'undefined') {
8255
8403
  this.raycast = {
8256
8404
  enabled: false,
@@ -8266,6 +8414,7 @@ class MEMeshObj extends _materials.default {
8266
8414
  this.entityArgPass = o.entityArgPass;
8267
8415
  this.clearColor = "red";
8268
8416
  this.video = null;
8417
+ this.FINISH_VIDIO_INIT = false;
8269
8418
 
8270
8419
  // Mesh stuff - for single mesh or t-posed (fiktive-first in loading order)
8271
8420
  this.mesh = o.mesh;
@@ -8281,7 +8430,7 @@ class MEMeshObj extends _materials.default {
8281
8430
  console.log(`%c Mesh objAnim exist: ${o.objAnim}`, _utils.LOG_FUNNY_SMALL);
8282
8431
  this.drawElements = this.drawElementsAnim;
8283
8432
  }
8284
- this.inputHandler = null;
8433
+ this.inputHandler = inputHandler;
8285
8434
  this.cameras = o.cameras;
8286
8435
  this.mainCameraParams = {
8287
8436
  type: o.mainCameraParams.type,
@@ -8302,7 +8451,6 @@ class MEMeshObj extends _materials.default {
8302
8451
  this.runProgram = () => {
8303
8452
  return new Promise(async resolve => {
8304
8453
  this.shadowDepthTextureSize = 1024;
8305
- // const aspect = canvas.width / canvas.height;
8306
8454
  this.modelViewProjectionMatrix = _wgpuMatrix.mat4.create();
8307
8455
  this.loadTex0(this.texturesPaths).then(() => {
8308
8456
  resolve();
@@ -8310,7 +8458,6 @@ class MEMeshObj extends _materials.default {
8310
8458
  });
8311
8459
  };
8312
8460
  this.runProgram().then(() => {
8313
- // const aspect = canvas.width / canvas.height;
8314
8461
  this.context.configure({
8315
8462
  device: this.device,
8316
8463
  format: this.presentationFormat,
@@ -8367,14 +8514,6 @@ class MEMeshObj extends _materials.default {
8367
8514
  this.indexBuffer.unmap();
8368
8515
  this.indexCount = indexCount;
8369
8516
 
8370
- // Create the depth texture for rendering/sampling the shadow map.
8371
- this.shadowDepthTexture = this.device.createTexture({
8372
- size: [this.shadowDepthTextureSize, this.shadowDepthTextureSize, 1],
8373
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
8374
- format: 'depth32float'
8375
- });
8376
- this.shadowDepthTextureView = this.shadowDepthTexture.createView();
8377
-
8378
8517
  // Create some common descriptors used for both the shadow pipeline
8379
8518
  // and the color rendering pipeline.
8380
8519
  this.vertexBuffers = [{
@@ -8404,91 +8543,37 @@ class MEMeshObj extends _materials.default {
8404
8543
  }];
8405
8544
  this.primitive = {
8406
8545
  topology: 'triangle-list',
8407
- // cullMode: 'back',
8408
- cullMode: 'none'
8546
+ cullMode: 'back',
8547
+ // typical for shadow passes
8548
+ frontFace: 'ccw'
8409
8549
  };
8410
- this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
8411
- entries: [{
8412
- binding: 0,
8413
- visibility: GPUShaderStage.VERTEX,
8414
- buffer: {
8415
- type: 'uniform'
8416
- }
8417
- }]
8418
- });
8419
- this.shadowPipeline = this.device.createRenderPipeline({
8420
- layout: this.device.createPipelineLayout({
8421
- bindGroupLayouts: [this.uniformBufferBindGroupLayout, this.uniformBufferBindGroupLayout]
8422
- }),
8423
- vertex: {
8424
- module: this.device.createShaderModule({
8425
- code: _vertexShadow.vertexShadowWGSL
8426
- }),
8427
- buffers: this.vertexBuffers
8428
- },
8429
- depthStencil: {
8430
- depthWriteEnabled: true,
8431
- depthCompare: 'less',
8432
- format: 'depth32float'
8433
- },
8434
- primitive: this.primitive
8435
- });
8436
8550
 
8437
8551
  // Create a bind group layout which holds the scene uniforms and
8438
8552
  // the texture+sampler for depth. We create it manually because the WebPU
8439
8553
  // implementation doesn't infer this from the shader (yet).
8440
8554
  this.createLayoutForRender();
8441
- this.setupPipeline();
8442
- const depthTexture = this.device.createTexture({
8443
- size: [canvas.width, canvas.height],
8444
- // format: 'depth24plus-stencil8',
8445
- format: 'depth24plus',
8446
- usage: GPUTextureUsage.RENDER_ATTACHMENT
8447
- });
8448
- this.renderPassDescriptor = {
8449
- colorAttachments: [{
8450
- // view is acquired and set in render loop.
8451
- view: undefined,
8452
- clearValue: this.clearColor,
8453
- loadOp: 'clear',
8454
- // load -> clear = fix for FF
8455
- storeOp: 'store'
8456
- }],
8457
- depthStencilAttachment: {
8458
- view: depthTexture.createView(),
8459
- depthClearValue: 1.0,
8460
- depthLoadOp: 'clear',
8461
- depthStoreOp: 'store'
8462
- // stencilClearValue: 0,
8463
- // stencilLoadOp: 'clear',
8464
- // stencilStoreOp: 'store',
8465
- }
8466
- };
8467
8555
  this.modelUniformBuffer = this.device.createBuffer({
8468
8556
  size: 4 * 16,
8469
8557
  // 4x4 matrix
8470
8558
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
8471
8559
  });
8472
8560
  this.sceneUniformBuffer = this.device.createBuffer({
8473
- // Two 4x4 viewProj matrices,
8474
- // one for the camera and one for the light.
8475
- // Then a vec3 for the light position.
8476
- // Rounded to the nearest multiple of 16.
8477
- size: 2 * 4 * 16 + 4 * 4,
8561
+ label: 'sceneUniformBuffer per mesh',
8562
+ size: 160,
8478
8563
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
8479
8564
  });
8480
- console.log('test buffer sceneUniformBuffer ', this.sceneUniformBuffer);
8481
- this.sceneBindGroupForShadow = this.device.createBindGroup({
8482
- layout: this.uniformBufferBindGroupLayout,
8565
+ this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
8566
+ label: 'uniformBufferBindGroupLayout in mesh',
8483
8567
  entries: [{
8484
8568
  binding: 0,
8485
- resource: {
8486
- buffer: this.sceneUniformBuffer
8569
+ visibility: GPUShaderStage.VERTEX,
8570
+ buffer: {
8571
+ type: 'uniform'
8487
8572
  }
8488
8573
  }]
8489
8574
  });
8490
- this.createBindGroupForRender();
8491
8575
  this.modelBindGroup = this.device.createBindGroup({
8576
+ label: 'modelBindGroup in mesh',
8492
8577
  layout: this.uniformBufferBindGroupLayout,
8493
8578
  entries: [{
8494
8579
  binding: 0,
@@ -8497,49 +8582,49 @@ class MEMeshObj extends _materials.default {
8497
8582
  }
8498
8583
  }]
8499
8584
  });
8585
+ this.mainPassBindGroupLayout = this.device.createBindGroupLayout({
8586
+ entries: [{
8587
+ binding: 0,
8588
+ visibility: GPUShaderStage.FRAGMENT,
8589
+ texture: {
8590
+ sampleType: 'depth'
8591
+ }
8592
+ }, {
8593
+ binding: 1,
8594
+ visibility: GPUShaderStage.FRAGMENT,
8595
+ sampler: {
8596
+ type: 'comparison'
8597
+ }
8598
+ }]
8599
+ });
8500
8600
 
8501
8601
  // Rotates the camera around the origin based on time.
8502
- this.getTransformationMatrix = pos => {
8602
+ this.getTransformationMatrix = (mainRenderBundle, spotLight) => {
8503
8603
  const now = Date.now();
8504
- const deltaTime = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
8604
+ const dt = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
8505
8605
  this.lastFrameMS = now;
8506
- // const this.viewMatrix = mat4.identity()
8507
8606
  const camera = this.cameras[this.mainCameraParams.type];
8508
-
8509
- // engine frame
8510
8607
  camera.update(dt, inputHandler());
8511
8608
  const camVP = _wgpuMatrix.mat4.multiply(camera.projectionMatrix, camera.view);
8512
8609
  for (const mesh of mainRenderBundle) {
8513
- // Light’s viewProj should come from your SpotLight
8514
- // If you have multiple lights, you’ll need an array UBO or multiple passes.
8515
- const sceneData = new Float32Array(16 + 16 + 4); // lightVP, camVP, lightPos(+pad)
8610
+ // Flattened buffer: lightVP(16) + camVP(16) + cameraPos(3+pad) + lightPos(3+pad)
8611
+ const sceneData = new Float32Array(16 + 16 + 4 + 4); // 16+16+4+4 = 40 floats
8612
+
8613
+ // Light ViewProj
8516
8614
  sceneData.set(spotLight.viewProjMatrix, 0);
8615
+
8616
+ // Camera VP
8517
8617
  sceneData.set(camVP, 16);
8518
- sceneData.set(spotLight.position, 32);
8618
+
8619
+ // Camera position + padding
8620
+ sceneData.set([camera.position.x, camera.position.y, camera.position.z, 0.0], 32);
8621
+
8622
+ // Light position + padding
8623
+ sceneData.set([spotLight.position[0], spotLight.position[1], spotLight.position[2], 0.0], 36);
8519
8624
  device.queue.writeBuffer(mesh.sceneUniformBuffer,
8520
- // or a shared one if/when you centralize it
8625
+ // or shared buffer
8521
8626
  0, sceneData.buffer, sceneData.byteOffset, sceneData.byteLength);
8522
8627
  }
8523
- // this.viewMatrix = camera.update(deltaTime, this.inputHandler());
8524
- // const scaleVec = [1, 1, 1]; // your desired scale OPTION 1
8525
- // const scaleMatrix = mat4.scaling(scaleVec);
8526
- // // Apply scaling
8527
- // mat4.multiply(scaleMatrix, this.viewMatrix, this.viewMatrix);
8528
- // mat4.translate(this.viewMatrix, vec3.fromValues(pos.x, pos.y, pos.z), this.viewMatrix);
8529
-
8530
- // if(this.itIsPhysicsBody == true) {
8531
- // mat4.rotate(
8532
- // this.viewMatrix,
8533
- // vec3.fromValues(this.rotation.axis.x, this.rotation.axis.y, this.rotation.axis.z),
8534
- // degToRad(this.rotation.angle), this.viewMatrix)
8535
- // } else {
8536
- // mat4.rotateX(this.viewMatrix, Math.PI * this.rotation.getRotX(), this.viewMatrix);
8537
- // mat4.rotateY(this.viewMatrix, Math.PI * this.rotation.getRotY(), this.viewMatrix);
8538
- // mat4.rotateZ(this.viewMatrix, Math.PI * this.rotation.getRotZ(), this.viewMatrix);
8539
- // // console.info('NOT PHYSICS angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
8540
- // }
8541
- // mat4.multiply(camera.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
8542
- // return this.modelViewProjectionMatrix;
8543
8628
  };
8544
8629
  this.getModelMatrix = pos => {
8545
8630
  let modelMatrix = _wgpuMatrix.mat4.identity();
@@ -8560,32 +8645,29 @@ class MEMeshObj extends _materials.default {
8560
8645
  const modelMatrix = _wgpuMatrix.mat4.translation([0, 0, 0]);
8561
8646
  const modelData = modelMatrix;
8562
8647
  this.device.queue.writeBuffer(this.modelUniformBuffer, 0, modelData.buffer, modelData.byteOffset, modelData.byteLength);
8563
- this.shadowPassDescriptor = {
8564
- colorAttachments: [],
8565
- depthStencilAttachment: {
8566
- view: this.shadowDepthTextureView,
8567
- depthClearValue: 1.0,
8568
- depthLoadOp: 'clear',
8569
- depthStoreOp: 'store'
8570
- }
8571
- };
8572
8648
  this.done = true;
8649
+ try {
8650
+ this.setupPipeline();
8651
+ } catch (err) {
8652
+ console.log('err in create pipeline in init ', err);
8653
+ }
8573
8654
  }).then(() => {
8574
8655
  if (typeof this.objAnim !== 'undefined' && this.objAnim !== null) {
8575
- console.log('after all load configutr mesh list buffers');
8656
+ console.log('after all updateMeshListBuffers...');
8576
8657
  this.updateMeshListBuffers();
8577
8658
  }
8578
8659
  });
8579
8660
  }
8580
8661
  setupPipeline = () => {
8581
- console.log('test >>>>>>>>>>>>>>>>>>>FORMAT>✅' + this.presentationFormat);
8662
+ this.createBindGroupForRender();
8582
8663
  this.pipeline = this.device.createRenderPipeline({
8664
+ label: 'Mesh Pipeline ✅',
8583
8665
  layout: this.device.createPipelineLayout({
8666
+ label: 'createPipelineLayout Mesh',
8584
8667
  bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout]
8585
8668
  }),
8586
8669
  vertex: {
8587
8670
  entryPoint: 'main',
8588
- // ✅ Add this
8589
8671
  module: this.device.createShaderModule({
8590
8672
  code: _vertex.vertexWGSL
8591
8673
  }),
@@ -8593,7 +8675,6 @@ class MEMeshObj extends _materials.default {
8593
8675
  },
8594
8676
  fragment: {
8595
8677
  entryPoint: 'main',
8596
- // ✅ Add this
8597
8678
  module: this.device.createShaderModule({
8598
8679
  code: this.isVideo == true ? _fragmentVideo.fragmentVideoWGSL : _fragment.fragmentWGSL
8599
8680
  }),
@@ -8607,42 +8688,17 @@ class MEMeshObj extends _materials.default {
8607
8688
  depthStencil: {
8608
8689
  depthWriteEnabled: true,
8609
8690
  depthCompare: 'less',
8610
- // format: 'depth24plus-stencil8',
8611
8691
  format: 'depth24plus'
8612
8692
  },
8613
8693
  primitive: this.primitive
8614
8694
  });
8695
+ console.log('✅Set Pipeline done');
8615
8696
  };
8616
- draw = () => {
8617
- // This code -> light follow camera. can be used like options later!
8618
- // if(this.done == false) return;
8619
- // const transformationMatrix = this.getTransformationMatrix(this.position);
8620
- // this.device.queue.writeBuffer(this.sceneUniformBuffer, 64, transformationMatrix.buffer, transformationMatrix.byteOffset, transformationMatrix.byteLength);
8621
- // this.renderPassDescriptor.colorAttachments[0].view = this.context
8622
- // .getCurrentTexture()
8623
- // .createView();
8624
-
8625
- // test
8697
+ updateModelUniformBuffer = () => {
8626
8698
  if (this.done == false) return;
8627
-
8628
8699
  // Per-object model matrix only
8629
8700
  const modelMatrix = this.getModelMatrix(this.position);
8630
8701
  this.device.queue.writeBuffer(this.modelUniformBuffer, 0, modelMatrix.buffer, modelMatrix.byteOffset, modelMatrix.byteLength);
8631
-
8632
- // Acquire swapchain view for the pass
8633
- this.renderPassDescriptor.colorAttachments[0].view = this.context.getCurrentTexture().createView();
8634
- };
8635
- drawElements = renderPass => {
8636
- if (this.isVideo) {
8637
- this.updateVideoTexture();
8638
- }
8639
- renderPass.setBindGroup(0, this.sceneBindGroupForRender);
8640
- renderPass.setBindGroup(1, this.modelBindGroup);
8641
- renderPass.setVertexBuffer(0, this.vertexBuffer);
8642
- renderPass.setVertexBuffer(1, this.vertexNormalsBuffer);
8643
- renderPass.setVertexBuffer(2, this.vertexTexCoordsBuffer);
8644
- renderPass.setIndexBuffer(this.indexBuffer, 'uint16');
8645
- renderPass.drawIndexed(this.indexCount);
8646
8702
  };
8647
8703
  createGPUBuffer(dataArray, usage) {
8648
8704
  if (!dataArray || typeof dataArray.length !== 'number') {
@@ -8680,7 +8736,6 @@ class MEMeshObj extends _materials.default {
8680
8736
  });
8681
8737
  new Float32Array(mesh.vertexNormalsBuffer.getMappedRange()).set(mesh.vertexNormals);
8682
8738
  mesh.vertexNormalsBuffer.unmap();
8683
-
8684
8739
  // UVs
8685
8740
  mesh.vertexTexCoordsBuffer = this.device.createBuffer({
8686
8741
  size: mesh.textures.length * Float32Array.BYTES_PER_ELEMENT,
@@ -8689,7 +8744,6 @@ class MEMeshObj extends _materials.default {
8689
8744
  });
8690
8745
  new Float32Array(mesh.vertexTexCoordsBuffer.getMappedRange()).set(mesh.textures);
8691
8746
  mesh.vertexTexCoordsBuffer.unmap();
8692
-
8693
8747
  // Indices
8694
8748
  const indexCount = mesh.indices.length;
8695
8749
  const indexSize = Math.ceil(indexCount * Uint16Array.BYTES_PER_ELEMENT / 4) * 4;
@@ -8703,7 +8757,35 @@ class MEMeshObj extends _materials.default {
8703
8757
  mesh.indexCount = indexCount;
8704
8758
  }
8705
8759
  }
8760
+ drawElements = (pass, lightContainer) => {
8761
+ if (this.isVideo) {
8762
+ this.updateVideoTexture();
8763
+ }
8764
+ // Bind per-mesh uniforms
8765
+ pass.setBindGroup(0, this.sceneBindGroupForRender); // camera/light UBOs
8766
+ pass.setBindGroup(1, this.modelBindGroup); // mesh transforms/textures
8767
+ // Bind each light’s shadow texture & sampler
8768
+ if (this.isVideo == false) {
8769
+ let bindIndex = 2; // start after UBO & model
8770
+ for (const light of lightContainer) {
8771
+ pass.setBindGroup(bindIndex++, light.getMainPassBindGroup(this));
8772
+ }
8773
+ }
8774
+ pass.setVertexBuffer(0, this.vertexBuffer);
8775
+ pass.setVertexBuffer(1, this.vertexNormalsBuffer);
8776
+ pass.setVertexBuffer(2, this.vertexTexCoordsBuffer);
8777
+ pass.setIndexBuffer(this.indexBuffer, 'uint16');
8778
+ pass.drawIndexed(this.indexCount);
8779
+ };
8706
8780
  drawElementsAnim = renderPass => {
8781
+ if (!this.sceneBindGroupForRender || !this.modelBindGroup) {
8782
+ console.log(' NULL 1');
8783
+ return;
8784
+ }
8785
+ if (!this.objAnim.meshList[this.objAnim.id + this.objAnim.currentAni]) {
8786
+ console.log(' NULL 2');
8787
+ return;
8788
+ }
8707
8789
  renderPass.setBindGroup(0, this.sceneBindGroupForRender);
8708
8790
  renderPass.setBindGroup(1, this.modelBindGroup);
8709
8791
  const mesh = this.objAnim.meshList[this.objAnim.id + this.objAnim.currentAni];
@@ -8724,9 +8806,7 @@ class MEMeshObj extends _materials.default {
8724
8806
  }
8725
8807
  }
8726
8808
  };
8727
- drawShadows = shadowPass => {
8728
- shadowPass.setBindGroup(0, this.sceneBindGroupForShadow);
8729
- shadowPass.setBindGroup(1, this.modelBindGroup);
8809
+ drawShadows = (shadowPass, light) => {
8730
8810
  shadowPass.setVertexBuffer(0, this.vertexBuffer);
8731
8811
  shadowPass.setVertexBuffer(1, this.vertexNormalsBuffer);
8732
8812
  shadowPass.setVertexBuffer(2, this.vertexTexCoordsBuffer);
@@ -9946,7 +10026,6 @@ class MatrixAmmo {
9946
10026
  CF_KINEMATIC_OBJECT: 2
9947
10027
  };
9948
10028
  let Ammo = this.Ammo;
9949
- console.log(pOptions.radius + "<<pOptions.radius");
9950
10029
  var colShape = new Ammo.btSphereShape(Array.isArray(pOptions.radius) ? pOptions.radius[0] : pOptions.radius),
9951
10030
  startTransform = new Ammo.btTransform();
9952
10031
  startTransform.setIdentity();
@@ -10106,46 +10185,11 @@ class MatrixAmmo {
10106
10185
  // if(item.kB == contactManifold.getBody0().kB) {
10107
10186
  // // console.log('Detected body0 =', item.name)
10108
10187
  // }
10109
- // if(item.kB == contactManifold.getBody1().kB) {
10110
- // // console.log('Detected body1 =', item.name)
10111
- // }
10112
- // })
10113
-
10114
10188
  if (this.ground.kB == contactManifold.getBody0().kB && this.getNameByBody(contactManifold.getBody1()) == 'CubePhysics1') {
10115
10189
  // console.log(this.ground ,'GROUND IS IN CONTACT WHO IS BODY1 ', contactManifold.getBody1())
10116
10190
  // console.log('GROUND IS IN CONTACT WHO IS BODY1 getNameByBody ', this.getNameByBody(contactManifold.getBody1()))
10117
10191
  // CHECK ROTATION
10118
10192
  var testR = contactManifold.getBody1().getWorldTransform().getRotation();
10119
- if (Math.abs(testR.y()) < 0.00001) {
10120
- this.lastRoll += " 4 +";
10121
- this.presentScore += 4;
10122
- dispatchEvent(new CustomEvent('dice-1', {}));
10123
- }
10124
- if (Math.abs(testR.x()) < 0.00001) {
10125
- this.lastRoll += " 3 +";
10126
- this.presentScore += 3;
10127
- dispatchEvent(new CustomEvent('dice-4', {}));
10128
- }
10129
- if (testR.x().toString().substring(0, 5) == testR.y().toString().substring(1, 6)) {
10130
- this.lastRoll += " 2 +";
10131
- this.presentScore += 2;
10132
- dispatchEvent(new CustomEvent('dice-6', {}));
10133
- }
10134
- if (testR.x().toString().substring(0, 5) == testR.y().toString().substring(0, 5)) {
10135
- this.lastRoll += " 1 +";
10136
- this.presentScore += 1;
10137
- dispatchEvent(new CustomEvent('dice-2', {}));
10138
- }
10139
- if (testR.z().toString().substring(0, 5) == testR.y().toString().substring(1, 6)) {
10140
- this.lastRoll += " 6 +";
10141
- this.presentScore += 6;
10142
- dispatchEvent(new CustomEvent('dice-5', {}));
10143
- }
10144
- if (testR.z().toString().substring(0, 5) == testR.y().toString().substring(0, 5)) {
10145
- this.lastRoll += " 5 +";
10146
- this.presentScore += 5;
10147
- dispatchEvent(new CustomEvent('dice-3', {}));
10148
- }
10149
10193
  console.log('this.lastRoll = ', this.lastRoll, ' presentScore = ', this.presentScore);
10150
10194
  }
10151
10195
  }
@@ -10217,18 +10261,19 @@ struct Scene {
10217
10261
  @group(0) @binding(2) var shadowSampler: sampler_comparison;
10218
10262
  @group(0) @binding(3) var meshTexture: texture_external;
10219
10263
  @group(0) @binding(4) var meshSampler: sampler;
10220
-
10221
10264
  @group(0) @binding(5) var<uniform> postFXMode: u32;
10222
10265
 
10223
10266
  // ❌ No binding(4) here!
10224
10267
 
10225
10268
  struct FragmentInput {
10226
- @location(0) shadowPos : vec3f,
10269
+ @location(0) shadowPos : vec4f,
10227
10270
  @location(1) fragPos : vec3f,
10228
10271
  @location(2) fragNorm : vec3f,
10229
10272
  @location(3) uv : vec2f,
10230
10273
  }
10231
10274
 
10275
+
10276
+
10232
10277
  const albedo = vec3f(0.9);
10233
10278
  const ambientFactor = 0.7;
10234
10279
 
@@ -10297,79 +10342,141 @@ Object.defineProperty(exports, "__esModule", {
10297
10342
  exports.fragmentWGSL = void 0;
10298
10343
  let fragmentWGSL = exports.fragmentWGSL = `override shadowDepthTextureSize: f32 = 1024.0;
10299
10344
 
10345
+ // Created by Nikola Lukic with chatgtp assist.
10346
+
10300
10347
  struct Scene {
10301
- lightViewProjMatrix : mat4x4f,
10302
- cameraViewProjMatrix : mat4x4f,
10303
- lightPos : vec3f,
10304
- padding : f32, // Required for alignment
10305
- }
10348
+ lightViewProjMatrix : mat4x4f,
10349
+ cameraViewProjMatrix : mat4x4f,
10350
+ cameraPos : vec3f,
10351
+ padding2 : f32, // align to 16 bytes
10352
+ lightPos : vec3f,
10353
+ padding : f32, // align to 16 bytes
10354
+ };
10306
10355
 
10307
10356
  struct SpotLight {
10308
- position: vec3f,
10309
- _pad1: f32,
10310
- direction: vec3f,
10311
- _pad2: f32,
10312
- innerCutoff: f32,
10313
- outerCutoff: f32,
10314
- _pad3: vec2f,
10315
- }
10357
+ position : vec3f,
10358
+ _pad1 : f32,
10359
+
10360
+ direction : vec3f,
10361
+ _pad2 : f32,
10362
+
10363
+ innerCutoff : f32,
10364
+ outerCutoff : f32,
10365
+ intensity : f32,
10366
+ _pad3 : f32,
10367
+
10368
+ color : vec3f,
10369
+ _pad4 : f32,
10370
+
10371
+ range : f32,
10372
+ ambientFactor : f32,
10373
+ shadowBias : f32,
10374
+ _pad5 : f32,
10375
+
10376
+ lightViewProj : mat4x4<f32>,
10377
+ };
10378
+
10379
+ const MAX_SPOTLIGHTS = 20u;
10316
10380
 
10317
10381
  @group(0) @binding(0) var<uniform> scene : Scene;
10318
- @group(0) @binding(1) var shadowMap: texture_depth_2d;
10382
+ @group(0) @binding(1) var shadowMapArray: texture_depth_2d_array;
10319
10383
  @group(0) @binding(2) var shadowSampler: sampler_comparison;
10320
10384
  @group(0) @binding(3) var meshTexture: texture_2d<f32>;
10321
10385
  @group(0) @binding(4) var meshSampler: sampler;
10322
- @group(0) @binding(5) var<uniform> spotlight: SpotLight;
10386
+ @group(0) @binding(5) var<uniform> spotlights: array<SpotLight, MAX_SPOTLIGHTS>;
10323
10387
 
10324
10388
  struct FragmentInput {
10325
- @location(0) shadowPos : vec3f,
10326
- @location(1) fragPos : vec3f,
10327
- @location(2) fragNorm : vec3f,
10328
- @location(3) uv : vec2f,
10389
+ @location(0) shadowPos : vec4f,
10390
+ @location(1) fragPos : vec3f,
10391
+ @location(2) fragNorm : vec3f,
10392
+ @location(3) uv : vec2f,
10329
10393
  }
10330
10394
 
10331
10395
  const albedo = vec3f(0.9);
10332
- const ambientFactor = 0.7;
10333
10396
 
10334
10397
  fn calculateSpotlightFactor(light: SpotLight, fragPos: vec3f) -> f32 {
10335
- let L = normalize(light.position - fragPos);
10336
- let theta = dot(L, normalize(-light.direction));
10337
- let epsilon = light.innerCutoff - light.outerCutoff;
10338
- let intensity = clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
10339
- return intensity;
10398
+ let L = normalize(light.position - fragPos);
10399
+ let theta = dot(L, normalize(-light.direction));
10400
+ let epsilon = light.innerCutoff - light.outerCutoff;
10401
+ return clamp((theta - light.outerCutoff) / epsilon, 0.0, 1.0);
10340
10402
  }
10341
10403
 
10342
- @fragment
10343
- fn main(input : FragmentInput) -> @location(0) vec4f {
10344
- // Shadow PFC
10345
- var visibility = 0.0;
10346
- let oneOverSize = 1.0 / shadowDepthTextureSize;
10347
- for (var y = -1; y <= 1; y++) {
10348
- for (var x = -1; x <= 1; x++) {
10349
- let offset = vec2f(vec2(x, y)) * oneOverSize;
10350
- visibility += textureSampleCompare(
10351
- shadowMap, shadowSampler,
10352
- input.shadowPos.xy + offset, input.shadowPos.z - 0.007
10353
- );
10354
- }
10355
- }
10356
- visibility /= 9.0;
10404
+ fn computeSpotLight(light: SpotLight, normal: vec3f, fragPos: vec3f, viewDir: vec3f) -> vec3f {
10405
+ let L = light.position - fragPos;
10406
+ let distance = length(L);
10407
+ let lightDir = normalize(L);
10357
10408
 
10358
- // Lambert
10359
- let norm = normalize(input.fragNorm);
10360
- let lightDir = normalize(scene.lightPos - input.fragPos);
10361
- let lambert = max(dot(norm, lightDir), 0.0);
10409
+ let spotFactor = calculateSpotlightFactor(light, fragPos);
10410
+ let atten = clamp(1.0 - (distance / light.range), 0.0, 1.0);
10362
10411
 
10363
- // Spotlight effect
10364
- let spotlightFactor = calculateSpotlightFactor(spotlight, input.fragPos);
10412
+ let diff = max(dot(normal, lightDir), 0.0);
10413
+ let halfwayDir = normalize(lightDir + viewDir);
10414
+ let spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
10365
10415
 
10366
- // Combine
10367
- let lightIntensity = ambientFactor + lambert * visibility * spotlightFactor;
10368
- let texColor = textureSample(meshTexture, meshSampler, input.uv);
10416
+ let diffuse = diff * light.color * light.intensity * atten;
10417
+ let specular = spec * light.color * light.intensity * atten;
10369
10418
 
10370
- return vec4f(texColor.rgb * lightIntensity * albedo, 1.0);
10419
+ return (diffuse + specular) * spotFactor;
10371
10420
  }
10372
- `;
10421
+
10422
+ // Corrected PCF for texture_depth_2d_array
10423
+ fn sampleShadow(shadowUV: vec2f, layer: i32, depthRef: f32, normal: vec3f, lightDir: vec3f) -> f32 {
10424
+ var visibility: f32 = 0.0;
10425
+ let biasConstant: f32 = 0.001;
10426
+ // Slope bias: avoid self-shadowing on steep angles
10427
+ // let slopeBias: f32 = max(0.002 * (1.0 - dot(normal, lightDir)), 0.0);
10428
+ let bias = biasConstant;// + slopeBias;
10429
+
10430
+ let oneOverSize = 1.0 / (shadowDepthTextureSize * 0.5);
10431
+
10432
+ // 3x3 PCF kernel
10433
+ let offsets: array<vec2f, 9> = array<vec2f, 9>(
10434
+ vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0),
10435
+ vec2(-1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0),
10436
+ vec2(-1.0, 1.0), vec2(0.0, 1.0), vec2(1.0, 1.0)
10437
+ );
10438
+
10439
+ for(var i: u32 = 0u; i < 9u; i = i + 1u) {
10440
+ visibility += textureSampleCompare(
10441
+ shadowMapArray,
10442
+ shadowSampler,
10443
+ shadowUV + offsets[i] * oneOverSize,
10444
+ layer,
10445
+ depthRef //+ bias
10446
+ );
10447
+ }
10448
+ return visibility / 9.0;
10449
+ }
10450
+
10451
+ @fragment
10452
+ fn main(input: FragmentInput) -> @location(0) vec4f {
10453
+ let norm = normalize(input.fragNorm);
10454
+
10455
+ let viewDir = normalize(scene.cameraPos - input.fragPos);
10456
+ // let viewDir = normalize(scene.cameraViewProjMatrix[3].xyz - input.fragPos);
10457
+
10458
+ var lightContribution = vec3f(0.0);
10459
+ var ambient = vec3f(0.0);
10460
+
10461
+ for (var i: u32 = 0u; i < MAX_SPOTLIGHTS; i = i + 1u) {
10462
+ let sc = spotlights[i].lightViewProj * vec4<f32>(input.fragPos, 1.0);
10463
+ let p = sc.xyz / sc.w;
10464
+ let uv = clamp(p.xy * 0.5 + vec2<f32>(0.5), vec2<f32>(0.0), vec2<f32>(1.0));
10465
+ let depthRef = p.z * 0.5 + 0.5;
10466
+ let lightDir = normalize(spotlights[i].position - input.fragPos);
10467
+ let angleFactor = 1.0 - dot(norm, lightDir);
10468
+ let slopeBias = 0.01 * (1.0 - dot(norm, lightDir));
10469
+ let bias = spotlights[i].shadowBias + slopeBias;
10470
+ let visibility = sampleShadow(uv, i32(i), depthRef - bias, norm, lightDir);
10471
+ let contrib = computeSpotLight(spotlights[i], norm, input.fragPos, viewDir);
10472
+ lightContribution += contrib * visibility;
10473
+ ambient += spotlights[i].ambientFactor * spotlights[i].color;
10474
+ }
10475
+
10476
+ let texColor = textureSample(meshTexture, meshSampler, input.uv);
10477
+ let finalColor = texColor.rgb * (ambient + lightContribution); // * albedo;
10478
+ return vec4f(finalColor, 1.0);
10479
+ }`;
10373
10480
 
10374
10481
  },{}],22:[function(require,module,exports){
10375
10482
  "use strict";
@@ -10450,11 +10557,10 @@ struct Model {
10450
10557
  @group(1) @binding(0) var<uniform> model : Model;
10451
10558
 
10452
10559
  struct VertexOutput {
10453
- @location(0) shadowPos: vec3f,
10560
+ @location(0) shadowPos: vec4f, // now vec4
10454
10561
  @location(1) fragPos: vec3f,
10455
10562
  @location(2) fragNorm: vec3f,
10456
- @location(3) uv : vec2f,
10457
-
10563
+ @location(3) uv: vec2f,
10458
10564
  @builtin(position) Position: vec4f,
10459
10565
  }
10460
10566
 
@@ -10462,34 +10568,21 @@ struct VertexOutput {
10462
10568
  fn main(
10463
10569
  @location(0) position: vec3f,
10464
10570
  @location(1) normal: vec3f,
10465
- @location(2) uv : vec2f
10571
+ @location(2) uv: vec2f
10466
10572
  ) -> VertexOutput {
10467
10573
  var output : VertexOutput;
10468
10574
 
10469
- // XY is in (-1, 1) space, Z is in (0, 1) space
10470
10575
  let posFromLight = scene.lightViewProjMatrix * model.modelMatrix * vec4(position, 1.0);
10471
-
10472
- // Convert XY to (0, 1)
10473
- // Y is flipped because texture coords are Y-down.
10474
- output.shadowPos = vec3(
10475
- posFromLight.xy * vec2(0.5, -0.5) + vec2(0.5),
10476
- posFromLight.z
10477
- );
10478
-
10479
- // follewed camera code
10480
- // output.Position = scene.cameraViewProjMatrix * model.modelMatrix * vec4(position, 1.0);
10481
- // output.fragPos = output.Position.xyz;
10482
- // output.fragNorm = normal;
10576
+ output.shadowPos = posFromLight; // pass full vec4 for perspective divide
10483
10577
 
10484
10578
  let worldPos = model.modelMatrix * vec4(position, 1.0);
10485
10579
  output.Position = scene.cameraViewProjMatrix * worldPos;
10486
- output.fragPos = worldPos.xyz; // ✅ world space
10580
+ output.fragPos = worldPos.xyz;
10487
10581
 
10488
10582
  output.fragNorm = normalize((model.modelMatrix * vec4(normal, 0.0)).xyz);
10489
10583
  output.uv = uv;
10490
10584
  return output;
10491
- }
10492
- `;
10585
+ }`;
10493
10586
 
10494
10587
  },{}],24:[function(require,module,exports){
10495
10588
  "use strict";
@@ -10608,6 +10701,14 @@ var _sounds = require("./sounds/sounds.js");
10608
10701
  var _loaderObj = require("./engine/loader-obj.js");
10609
10702
  var _lights = require("./engine/lights.js");
10610
10703
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10704
+ /**
10705
+ * @description
10706
+ * Main engine root class.
10707
+ * @author Nikola Lukic 2025
10708
+ * @email zlatnaspirala@gmail.com
10709
+ * @web https://maximumroulette.com
10710
+ * @github zlatnaspirala
10711
+ */
10611
10712
  class MatrixEngineWGPU {
10612
10713
  mainRenderBundle = [];
10613
10714
  lightContainer = [];
@@ -10621,10 +10722,7 @@ class MatrixEngineWGPU {
10621
10722
  };
10622
10723
  matrixAmmo = new _matrixAmmo.default();
10623
10724
  matrixSounds = new _sounds.MatrixSounds();
10624
-
10625
- // The input handler
10626
10725
  constructor(options, callback) {
10627
- // console.log('typeof options ', typeof options )
10628
10726
  if (typeof options == 'undefined' || typeof options == "function") {
10629
10727
  this.options = {
10630
10728
  useSingleRenderPass: true,
@@ -10676,7 +10774,6 @@ class MatrixEngineWGPU {
10676
10774
 
10677
10775
  // The camera types
10678
10776
  const initialCameraPosition = _wgpuMatrix.vec3.create(0, 0, 0);
10679
- // console.log('passed : o.mainCameraParams.responseCoef ', o.mainCameraParams.responseCoef)
10680
10777
  this.mainCameraParams = {
10681
10778
  type: this.options.mainCameraParams.type,
10682
10779
  responseCoef: this.options.mainCameraParams.responseCoef
@@ -10713,14 +10810,6 @@ class MatrixEngineWGPU {
10713
10810
  this.device = await this.adapter.requestDevice({
10714
10811
  extensions: ["ray_tracing"]
10715
10812
  });
10716
-
10717
- // Maybe works in ssl with webworkers...
10718
- // const adapterInfo = await this.adapter.requestAdapterInfo();
10719
- // var test = this.adapter.features()
10720
- // console.log(adapterInfo.vendor);
10721
- // console.log('test' + test);
10722
- // console.log("FEATURES : " + this.adapter.features)
10723
-
10724
10813
  this.context = canvas.getContext('webgpu');
10725
10814
  const devicePixelRatio = window.devicePixelRatio;
10726
10815
  canvas.width = canvas.clientWidth * devicePixelRatio;
@@ -10736,20 +10825,87 @@ class MatrixEngineWGPU {
10736
10825
  } else {
10737
10826
  this.frame = this.framePassPerObject;
10738
10827
  }
10739
-
10740
- // Global SCENE BUFFER Good idea for future
10741
- // this.sceneUniformBuffer = this.device.createBuffer({
10742
- // // Two 4x4 viewProj matrices,
10743
- // // one for the camera and one for the light.
10744
- // // Then a vec3 for the light position.
10745
- // // Rounded to the nearest multiple of 16.
10746
- // size: 2 * 4 * 16 + 4 * 4,
10747
- // usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
10748
- // });
10749
-
10828
+ this.MAX_SPOTLIGHTS = 20;
10750
10829
  this.inputHandler = (0, _engine.createInputHandler)(window, canvas);
10830
+ this.createGlobalStuff();
10751
10831
  this.run(callback);
10752
10832
  };
10833
+ createGlobalStuff() {
10834
+ this.spotlightUniformBuffer = this.device.createBuffer({
10835
+ label: 'spotlightUniformBufferGLOBAL',
10836
+ size: this.MAX_SPOTLIGHTS * 144,
10837
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
10838
+ });
10839
+ this.SHADOW_RES = 1024;
10840
+ this.createTexArrayForShadows();
10841
+ this.mainDepthTexture = this.device.createTexture({
10842
+ size: [this.canvas.width, this.canvas.height],
10843
+ format: 'depth24plus',
10844
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
10845
+ });
10846
+ this.mainDepthView = this.mainDepthTexture.createView();
10847
+ this.mainRenderPassDesc = {
10848
+ label: 'mainRenderPassDesc',
10849
+ colorAttachments: [{
10850
+ view: undefined,
10851
+ // set each frame
10852
+ loadOp: 'clear',
10853
+ storeOp: 'store',
10854
+ clearValue: [0.02, 0.02, 0.02, 1]
10855
+ }],
10856
+ depthStencilAttachment: {
10857
+ view: this.mainDepthView,
10858
+ // fixed
10859
+ depthLoadOp: 'clear',
10860
+ depthStoreOp: 'store',
10861
+ depthClearValue: 1.0
10862
+ }
10863
+ };
10864
+ }
10865
+ createTexArrayForShadows() {
10866
+ let numberOfLights = this.lightContainer.length;
10867
+ if (this.lightContainer.length == 0) {
10868
+ // console.warn('Wait for init light instance')
10869
+ setTimeout(() => {
10870
+ // console.info('Test light again...')
10871
+ this.createMe();
10872
+ }, 800);
10873
+ }
10874
+ this.createMe = () => {
10875
+ Math.max(1, this.lightContainer.length);
10876
+ if (this.lightContainer.length == 0) {
10877
+ setTimeout(() => {
10878
+ console.warn('Create now test...');
10879
+ this.createMe();
10880
+ }, 800);
10881
+ return;
10882
+ }
10883
+ console.warn('Create this.shadowTextureArray...');
10884
+ this.shadowTextureArray = this.device.createTexture({
10885
+ label: `shadowTextureArray[GLOBAL] num of light ${numberOfLights}`,
10886
+ size: {
10887
+ width: 1024,
10888
+ height: 1024,
10889
+ depthOrArrayLayers: numberOfLights // at least 1
10890
+ },
10891
+ dimension: '2d',
10892
+ format: 'depth32float',
10893
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
10894
+ });
10895
+ this.shadowArrayView = this.shadowTextureArray.createView({
10896
+ dimension: '2d-array'
10897
+ });
10898
+ this.shadowVideoTexture = this.device.createTexture({
10899
+ size: [1024, 1024],
10900
+ format: "depth32float",
10901
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING
10902
+ });
10903
+ this.shadowVideoView = this.shadowVideoTexture.createView({
10904
+ dimension: "2d"
10905
+ });
10906
+ };
10907
+ this.createMe();
10908
+ }
10753
10909
  getSceneObjectByName(name) {
10754
10910
  return this.mainRenderBundle.find(sceneObject => sceneObject.name === name);
10755
10911
  }
@@ -10954,12 +11110,11 @@ class MatrixEngineWGPU {
10954
11110
  this.mainRenderBundle.push(myBall1);
10955
11111
  };
10956
11112
  addLight(o) {
10957
- // test light global; entity
10958
11113
  const camera = this.cameras[this.mainCameraParams.type];
10959
- let newLight = new _lights.SpotLight(camera, this.inputHandler);
10960
- newLight.prepareBuffer(this.device);
11114
+ let newLight = new _lights.SpotLight(camera, this.inputHandler, this.device);
10961
11115
  this.lightContainer.push(newLight);
10962
- console.log('Add light : ', newLight);
11116
+ this.createTexArrayForShadows();
11117
+ console.log(`%cAdd light: ${newLight}`, _utils.LOG_FUNNY_SMALL);
10963
11118
  }
10964
11119
  addMeshObj = (o, clearColor = this.options.clearColor) => {
10965
11120
  if (typeof o.name === 'undefined') {
@@ -11036,12 +11191,9 @@ class MatrixEngineWGPU {
11036
11191
  o.physics.rotation = o.rotation;
11037
11192
  }
11038
11193
  o.physics.position = o.position;
11039
- // console.log('Mesh procedure', o)
11040
- // TEST OBJS SEQ ANIMS
11041
11194
  if (typeof o.objAnim == 'undefined' || typeof o.objAnim == null) {
11042
11195
  o.objAnim = null;
11043
11196
  } else {
11044
- // console.log('o.anim', o.objAnim)
11045
11197
  if (typeof o.objAnim.animations !== 'undefined') {
11046
11198
  o.objAnim.play = _loaderObj.play;
11047
11199
  }
@@ -11054,14 +11206,13 @@ class MatrixEngineWGPU {
11054
11206
  // scale for all second option!
11055
11207
  o.objAnim.scaleAll = function (s) {
11056
11208
  for (var k in this.meshList) {
11057
- console.log('SCALE');
11209
+ console.log('SCALE meshList');
11058
11210
  this.meshList[k].setScale(s);
11059
11211
  }
11060
11212
  };
11061
11213
  }
11062
- let myMesh1 = new _meshObj.default(this.canvas, this.device, this.context, o);
11063
- myMesh1.lightContainer = this.lightContainer;
11064
- myMesh1.inputHandler = this.inputHandler;
11214
+ let myMesh1 = new _meshObj.default(this.canvas, this.device, this.context, o, this.inputHandler);
11215
+ myMesh1.spotlightUniformBuffer = this.spotlightUniformBuffer;
11065
11216
  myMesh1.clearColor = clearColor;
11066
11217
  if (o.physics.enabled == true) {
11067
11218
  this.matrixAmmo.addPhysics(myMesh1, o.physics);
@@ -11080,49 +11231,19 @@ class MatrixEngineWGPU {
11080
11231
  this.mainRenderBundle = [];
11081
11232
  this.canvas.remove();
11082
11233
  };
11083
- test = () => {
11084
- const now = Date.now();
11085
- // First frame safety
11086
- let dt = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
11087
- if (!this.lastFrameMS) {
11088
- dt = 16;
11234
+ updateLights() {
11235
+ const floatsPerLight = 36; // not 20 anymore
11236
+ const data = new Float32Array(this.MAX_SPOTLIGHTS * floatsPerLight);
11237
+ for (let i = 0; i < this.MAX_SPOTLIGHTS; i++) {
11238
+ if (i < this.lightContainer.length) {
11239
+ const buf = this.lightContainer[i].getLightDataBuffer();
11240
+ data.set(buf, i * floatsPerLight);
11241
+ } else {
11242
+ data.set(new Float32Array(floatsPerLight), i * floatsPerLight);
11243
+ }
11089
11244
  }
11090
- this.lastFrameMS = now;
11091
- const camera = this.cameras[this.mainCameraParams.type];
11092
-
11093
- // engine, once per frame
11094
- // const camera = this.cameras[this.mainCameraParams.type];
11095
- camera.update(dt, this.inputHandler());
11096
- const camVP = _wgpuMatrix.mat4.multiply(camera.projectionMatrix, camera.view); // P * V
11097
-
11098
- for (const mesh of this.mainRenderBundle) {
11099
- // scene buffer layout = 0..63 lightVP, 64..127 camVP, 128..143 lightPos(+pad)
11100
- this.device.queue.writeBuffer(mesh.sceneUniformBuffer, 64,
11101
- // cameraViewProjMatrix offset
11102
- camVP.buffer, camVP.byteOffset, camVP.byteLength);
11103
- }
11104
- // engine frame
11105
- // camera.update(dt, this.inputHandler());
11106
- // const camVP = mat4.multiply(camera.projectionMatrix, camera.view);
11107
-
11108
- // for(const mesh of this.mainRenderBundle) {
11109
- // // Light’s viewProj should come from your SpotLight
11110
- // // If you have multiple lights, you’ll need an array UBO or multiple passes.
11111
- // const sceneData = new Float32Array(16 + 16 + 4); // lightVP, camVP, lightPos(+pad)
11112
- // sceneData.set(this.lightContainer[0].viewProjMatrix, 0);
11113
- // sceneData.set(camVP, 16);
11114
- // sceneData.set(this.lightContainer[0].position, 32);
11115
-
11116
- // // sceneUniformBuffer
11117
- // this.device.queue.writeBuffer(
11118
- // mesh.sceneUniformBuffer, // or a shared one if/when you centralize it
11119
- // 0,
11120
- // sceneData.buffer,
11121
- // sceneData.byteOffset,
11122
- // sceneData.byteLength
11123
- // );
11124
- // }
11125
- };
11245
+ this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, data.buffer);
11246
+ }
11126
11247
  frameSinglePass = () => {
11127
11248
  if (typeof this.mainRenderBundle == 'undefined' || this.mainRenderBundle.length == 0) {
11128
11249
  setTimeout(() => {
@@ -11130,66 +11251,109 @@ class MatrixEngineWGPU {
11130
11251
  }, 200);
11131
11252
  return;
11132
11253
  }
11254
+ let noPass = false;
11255
+ this.mainRenderBundle.forEach((meItem, index) => {
11256
+ if (meItem.isVideo == true) {
11257
+ if (!meItem.externalTexture || meItem.video.readyState < 2) {
11258
+ console.log('no rendere for video not ready');
11259
+ // this.externalTexture = this.device.importExternalTexture({source: this.video});
11260
+ noPass = true;
11261
+ setTimeout(() => requestAnimationFrame(this.frame), 1500);
11262
+ return;
11263
+ }
11264
+ }
11265
+ });
11266
+ if (noPass == true) {
11267
+ console.log('no rendere for video not ready !!!!');
11268
+ return;
11269
+ }
11270
+
11271
+ // let pass;
11272
+ // let commandEncoder;
11133
11273
  try {
11134
- let shadowPass = null;
11135
- let renderPass;
11136
11274
  let commandEncoder = this.device.createCommandEncoder();
11137
- this.test();
11275
+ this.updateLights();
11138
11276
  // 1️⃣ Update light data (position, direction, uniforms)
11139
11277
  for (const light of this.lightContainer) {
11140
- light.updateLightBuffer();
11278
+ light.update();
11279
+ // light.updateSceneUniforms(this.mainRenderBundle, this.cameras.WASD);
11141
11280
  this.mainRenderBundle.forEach((meItem, index) => {
11142
- light.updateSceneUniforms(this.mainRenderBundle, this.cameras.WASD);
11281
+ meItem.position.update();
11282
+ meItem.updateModelUniformBuffer();
11283
+ // if(meItem.isVideo != true) {
11284
+ meItem.getTransformationMatrix(this.mainRenderBundle, light);
11285
+ // }
11143
11286
  });
11144
11287
  }
11145
- this.mainRenderBundle.forEach((meItem, index) => {
11146
- meItem.position.update();
11147
- });
11148
11288
  if (this.matrixAmmo) this.matrixAmmo.updatePhysics();
11149
-
11150
- // no cast WORKING
11151
- // this.mainRenderBundle.forEach((meItem, index) => {
11152
- // meItem.draw(commandEncoder);
11153
-
11154
- // shadowPass = commandEncoder.beginRenderPass(meItem.shadowPassDescriptor);
11155
- // shadowPass.setPipeline(meItem.shadowPipeline);
11156
- // meItem.drawShadows(shadowPass);
11157
- // shadowPass.end();
11158
- // })
11159
-
11160
- // cast!
11161
- const firstItem = this.mainRenderBundle[0];
11162
- shadowPass = commandEncoder.beginRenderPass(firstItem.shadowPassDescriptor);
11163
- shadowPass.setPipeline(firstItem.shadowPipeline);
11164
- for (const meItem of this.mainRenderBundle) {
11165
- // meItem.draw(commandEncoder);
11166
- meItem.drawShadows(shadowPass); // Draw ALL objects
11289
+ for (let i = 0; i < this.lightContainer.length; i++) {
11290
+ const light = this.lightContainer[i];
11291
+ let ViewPerLightRenderShadowPass = this.shadowTextureArray.createView({
11292
+ dimension: '2d',
11293
+ baseArrayLayer: i,
11294
+ arrayLayerCount: 1,
11295
+ // must be > 0
11296
+ baseMipLevel: 0,
11297
+ mipLevelCount: 1
11298
+ });
11299
+ const shadowPass = commandEncoder.beginRenderPass({
11300
+ label: "shadowPass",
11301
+ colorAttachments: [],
11302
+ depthStencilAttachment: {
11303
+ view: ViewPerLightRenderShadowPass,
11304
+ depthLoadOp: 'clear',
11305
+ depthStoreOp: 'store',
11306
+ depthClearValue: 1.0
11307
+ }
11308
+ });
11309
+ shadowPass.setPipeline(light.shadowPipeline);
11310
+ for (const [meshIndex, mesh] of this.mainRenderBundle.entries()) {
11311
+ if (mesh.videoIsReady == 'NONE') {
11312
+ shadowPass.setBindGroup(0, light.getShadowBindGroup(mesh, meshIndex));
11313
+ shadowPass.setBindGroup(1, mesh.modelBindGroup);
11314
+ mesh.drawShadows(shadowPass, light);
11315
+ }
11316
+ }
11317
+ shadowPass.end();
11167
11318
  }
11168
- shadowPass.end();
11169
- this.mainRenderBundle.forEach((meItem, index) => {
11170
- if (index == 0) {
11171
- meItem.draw(commandEncoder);
11172
- meItem.renderPassDescriptor.colorAttachments[0].view = this.context.getCurrentTexture().createView();
11173
- renderPass = commandEncoder.beginRenderPass(meItem.renderPassDescriptor);
11174
- renderPass.setPipeline(meItem.pipeline);
11175
- } else {
11176
- meItem.draw(commandEncoder);
11319
+ const currentTextureView = this.context.getCurrentTexture().createView();
11320
+ this.mainRenderPassDesc.colorAttachments[0].view = currentTextureView;
11321
+ let pass = commandEncoder.beginRenderPass(this.mainRenderPassDesc);
11322
+ // Loop over each mesh
11323
+ for (const mesh of this.mainRenderBundle) {
11324
+ pass.setPipeline(mesh.pipeline);
11325
+ if (!mesh.sceneBindGroupForRender || mesh.FINISH_VIDIO_INIT == false && mesh.isVideo == true) {
11326
+ for (const m of this.mainRenderBundle) {
11327
+ if (m.isVideo == true) {
11328
+ console.log('✅✅✅ set shadowVideoView', this.shadowVideoView);
11329
+ m.shadowDepthTextureView = this.shadowVideoView;
11330
+ m.FINISH_VIDIO_INIT = true;
11331
+ m.setupPipeline();
11332
+ } else {
11333
+ console.log('✅ NORMAL shadowArrayView');
11334
+ m.shadowDepthTextureView = this.shadowArrayView;
11335
+ }
11336
+ }
11177
11337
  }
11178
- });
11179
- this.mainRenderBundle.forEach((meItem, index) => {
11180
- meItem.drawElements(renderPass);
11181
- });
11182
- if (renderPass) renderPass.end();
11338
+ mesh.drawElements(pass, this.lightContainer);
11339
+ }
11340
+
11341
+ // End render pass
11342
+ pass.end();
11183
11343
  this.device.queue.submit([commandEncoder.finish()]);
11184
11344
  requestAnimationFrame(this.frame);
11185
11345
  } catch (err) {
11186
- console.log('%cDraw func (err):' + err, _utils.LOG_WARN);
11346
+ console.log('%cLoop(err):' + err, _utils.LOG_WARN);
11347
+ // if(pass) pass.end();
11348
+ // this.device.queue.submit([commandEncoder.finish()]);
11187
11349
  requestAnimationFrame(this.frame);
11350
+ } finally {
11351
+ //requestAnimationFrame(this.frame);
11188
11352
  }
11189
11353
  };
11190
11354
  framePassPerObject = () => {
11191
11355
  let commandEncoder = this.device.createCommandEncoder();
11192
- this.matrixAmmo.updatePhysics();
11356
+ if (this.matrixAmmo.rigidBodies.length > 0) this.matrixAmmo.updatePhysics();
11193
11357
  this.mainRenderBundle.forEach((meItem, index) => {
11194
11358
  if (index === 0) {
11195
11359
  if (meItem.renderPassDescriptor) meItem.renderPassDescriptor.colorAttachments[0].loadOp = 'clear';
@@ -11197,7 +11361,7 @@ class MatrixEngineWGPU {
11197
11361
  if (meItem.renderPassDescriptor) meItem.renderPassDescriptor.colorAttachments[0].loadOp = 'load';
11198
11362
  }
11199
11363
  // Update transforms, physics, etc. (optional)
11200
- meItem.draw(commandEncoder); // optional: if this does per-frame updates
11364
+ meItem.draw(commandEncoder);
11201
11365
  if (meItem.renderBundle) {
11202
11366
  // Set up view per object
11203
11367
  meItem.renderPassDescriptor.colorAttachments[0].view = this.context.getCurrentTexture().createView();
@@ -11205,7 +11369,7 @@ class MatrixEngineWGPU {
11205
11369
  passEncoder.executeBundles([meItem.renderBundle]); // ✅ Use only this bundle
11206
11370
  passEncoder.end();
11207
11371
  } else {
11208
- meItem.draw(commandEncoder); // fallback if no renderBundle
11372
+ meItem.draw(commandEncoder);
11209
11373
  }
11210
11374
  });
11211
11375
  this.device.queue.submit([commandEncoder.finish()]);