matrix-engine-wgpu 1.4.0 → 1.4.2

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",
@@ -186,7 +187,7 @@ var loadObjFile = function () {
186
187
  (0, _loaderObj.downloadMeshes)({
187
188
  cube: "./res/meshes/blender/cube.obj"
188
189
  }, onGround, {
189
- scale: [25, 0.1, 25]
190
+ scale: [20, 1, 20]
190
191
  });
191
192
 
192
193
  // loadObjFile.addLight();
@@ -228,7 +229,7 @@ var loadObjFile = function () {
228
229
  position: {
229
230
  x: 0,
230
231
  y: 2,
231
- z: -10
232
+ z: -20
232
233
  },
233
234
  rotation: {
234
235
  x: 0,
@@ -244,16 +245,16 @@ var loadObjFile = function () {
244
245
  name: 'cube1',
245
246
  mesh: m.cube,
246
247
  physics: {
247
- enabled: true,
248
+ enabled: false,
248
249
  geometry: "Cube"
249
250
  }
250
251
  // raycast: { enabled: true , radius: 2 }
251
252
  });
252
253
  loadObjFile.addMeshObj({
253
254
  position: {
254
- x: 3,
255
- y: 2,
256
- z: -15
255
+ x: 0,
256
+ y: -1,
257
+ z: -20
257
258
  },
258
259
  rotation: {
259
260
  x: 0,
@@ -269,7 +270,7 @@ var loadObjFile = function () {
269
270
  name: 'ball1',
270
271
  mesh: m.ball,
271
272
  physics: {
272
- enabled: true,
273
+ enabled: false,
273
274
  geometry: "Sphere"
274
275
  }
275
276
  // raycast: { enabled: true , radius: 2 }
@@ -277,7 +278,7 @@ var loadObjFile = function () {
277
278
  var TEST = loadObjFile.getSceneObjectByName('cube2');
278
279
  console.log(`%c Test access scene ${TEST} object.`, _utils.LOG_MATRIX);
279
280
  loadObjFile.addLight();
280
- loadObjFile.addLight();
281
+ // loadObjFile.addLight();
281
282
  }
282
283
  });
283
284
  // just for dev
@@ -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,18 +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));
7149
7167
  this.intensity = 1.0;
7150
7168
  this.color = _wgpuMatrix.vec3.create(1.0, 1.0, 1.0); // white
7151
7169
 
7152
7170
  this.viewMatrix = _wgpuMatrix.mat4.lookAt(position, target, this.up);
7153
- 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;
7154
7179
  this.viewProjMatrix = _wgpuMatrix.mat4.multiply(this.projectionMatrix, this.viewMatrix);
7155
7180
  this.fov = fov;
7156
7181
  this.aspect = aspect;
@@ -7159,63 +7184,144 @@ class SpotLight {
7159
7184
  this.innerCutoff = Math.cos(Math.PI / 180 * 12.5);
7160
7185
  this.outerCutoff = Math.cos(Math.PI / 180 * 17.5);
7161
7186
  this.ambientFactor = 0.5;
7162
- this.range = 200.0; // example max distance
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
+ };
7163
7305
  }
7164
7306
  update() {
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
- }
7188
-
7189
- // DEPLACED
7190
- prepareBuffer(device) {
7191
- if (!this.device) this.device = device;
7192
- this.spotlightUniformBuffer = this.device.createBuffer({
7193
- label: 'spotlightUniformBuffer',
7194
- size: 80,
7195
- usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
7196
- });
7197
- const spotlightData = this.getLightDataBuffer();
7198
- this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, spotlightData.buffer, spotlightData.byteOffset, spotlightData.byteLength);
7199
- }
7200
-
7201
- // DEPLACED
7202
- updateLightBuffer() {
7203
- if (!this.device || !this.spotlightUniformBuffer) {
7204
- return;
7205
- }
7206
- const spotlightData = this.getLightDataBuffer();
7207
- this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, spotlightData.buffer, spotlightData.byteOffset, spotlightData.byteLength);
7208
- }
7209
7313
  getLightDataBuffer() {
7210
- 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,
7211
- // new
7212
- 0.0, 0.0 // padding
7213
- ]);
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]);
7214
7320
  }
7215
7321
  }
7216
7322
  exports.SpotLight = SpotLight;
7217
7323
 
7218
- },{"wgpu-matrix":7}],12:[function(require,module,exports){
7324
+ },{"../shaders/vertexShadow.wgsl":24,"wgpu-matrix":7}],12:[function(require,module,exports){
7219
7325
  "use strict";
7220
7326
 
7221
7327
  Object.defineProperty(exports, "__esModule", {
@@ -7702,8 +7808,17 @@ class Materials {
7702
7808
  constructor(device) {
7703
7809
  this.device = device;
7704
7810
  this.isVideo = false;
7811
+ this.videoIsReady = 'NONE';
7812
+ // this.compareSampler = this.device.createSampler({compare: 'less'});
7705
7813
  this.compareSampler = this.device.createSampler({
7706
- 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'
7707
7822
  });
7708
7823
  // For image textures (standard sampler)
7709
7824
  this.imageSampler = this.device.createSampler({
@@ -7758,7 +7873,8 @@ class Materials {
7758
7873
  });
7759
7874
  }
7760
7875
  async loadVideoTexture(arg) {
7761
- this.isVideo = true;
7876
+ // this.isVideo = true;
7877
+ this.videoIsReady = 'MAYBE';
7762
7878
  if (arg.type === 'video') {
7763
7879
  this.video = document.createElement('video');
7764
7880
  this.video.src = arg.src || 'res/videos/tunel.mp4';
@@ -7767,7 +7883,11 @@ class Materials {
7767
7883
  this.video.loop = true;
7768
7884
  document.body.append(this.video);
7769
7885
  this.video.style.display = 'none';
7886
+ this.video.style.position = 'absolute';
7887
+ this.video.style.top = '50px';
7888
+ this.video.style.left = '50px';
7770
7889
  await this.video.play();
7890
+ this.isVideo = true;
7771
7891
  } else if (arg.type === 'videoElement') {
7772
7892
  this.video = arg.el;
7773
7893
  await this.video.play();
@@ -7792,6 +7912,7 @@ class Materials {
7792
7912
  });
7793
7913
  this.video.srcObject = stream;
7794
7914
  await this.video.play();
7915
+ this.isVideo = true;
7795
7916
  } catch (err) {
7796
7917
  console.error("❌ Failed to access camera:", err);
7797
7918
  return;
@@ -7811,6 +7932,7 @@ class Materials {
7811
7932
  }
7812
7933
  this.video.srcObject = stream;
7813
7934
  await this.video.play();
7935
+ this.isVideo = true;
7814
7936
  } else if (arg.type === 'canvas2d-inline') {
7815
7937
  // Miniature inline-drawn canvas created dynamically
7816
7938
  const canvas = document.createElement('canvas');
@@ -7831,6 +7953,7 @@ class Materials {
7831
7953
  this.video.playsInline = true;
7832
7954
  this.video.style.display = 'none';
7833
7955
  document.body.append(this.video);
7956
+ this.isVideo = true;
7834
7957
  const stream = canvas.captureStream?.() || canvas.mozCaptureStream?.();
7835
7958
  if (!stream) {
7836
7959
  console.error('❌ Cannot capture stream from inline canvas');
@@ -7844,25 +7967,37 @@ class Materials {
7844
7967
  minFilter: 'linear'
7845
7968
  });
7846
7969
 
7847
- // ✅ Now
7970
+ // ✅ Now - maybe noT
7848
7971
  this.createLayoutForRender();
7849
- this.setupPipeline();
7850
7972
  }
7851
7973
  updateVideoTexture() {
7852
7974
  if (!this.video || this.video.readyState < 2) return;
7853
- this.externalTexture = this.device.importExternalTexture({
7854
- source: this.video
7855
- });
7856
- 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
+ }
7857
7989
  }
7858
7990
  createBindGroupForRender() {
7859
- const textureResource = this.isVideo ? this.externalTexture // must be set via updateVideoTexture
7860
- : this.texture0.createView();
7861
- if (!textureResource || !this.sceneUniformBuffer || !this.shadowDepthTextureView || !this.sampler) {
7862
- 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();
7863
7997
  return;
7864
- }
7998
+ } else {}
7865
7999
  if (this.isVideo == true) {
8000
+ console.info("✅ video sceneBindGroupForRender ");
7866
8001
  this.sceneBindGroupForRender = this.device.createBindGroup({
7867
8002
  layout: this.bglForRender,
7868
8003
  entries: [{
@@ -7889,6 +8024,10 @@ class Materials {
7889
8024
  }
7890
8025
  }]
7891
8026
  });
8027
+
8028
+ // special case for video meybe better solution exist
8029
+ // this.setupPipeline();
8030
+ this.video.play();
7892
8031
  } else {
7893
8032
  this.sceneBindGroupForRender = this.device.createBindGroup({
7894
8033
  layout: this.bglForRender,
@@ -7919,66 +8058,87 @@ class Materials {
7919
8058
  }
7920
8059
  }
7921
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);
7922
8135
  this.bglForRender = this.device.createBindGroupLayout({
7923
- entries: [{
7924
- binding: 0,
7925
- visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
7926
- buffer: {
7927
- type: 'uniform'
7928
- }
7929
- }, {
7930
- binding: 1,
7931
- visibility: GPUShaderStage.FRAGMENT,
7932
- texture: {
7933
- sampleType: 'depth'
7934
- }
7935
- }, {
7936
- binding: 2,
7937
- visibility: GPUShaderStage.FRAGMENT,
7938
- sampler: {
7939
- type: 'comparison'
7940
- }
7941
- }, ...(this.isVideo ? [
7942
- // VIDEO
7943
- {
7944
- binding: 3,
7945
- visibility: GPUShaderStage.FRAGMENT,
7946
- externalTexture: {}
7947
- }, {
7948
- binding: 4,
7949
- visibility: GPUShaderStage.FRAGMENT,
7950
- sampler: {
7951
- type: 'filtering'
7952
- } // for video sampling
7953
- }, {
7954
- binding: 5,
7955
- visibility: GPUShaderStage.FRAGMENT,
7956
- buffer: {
7957
- type: 'uniform'
7958
- }
7959
- }] : [
7960
- // IMAGE
7961
- {
7962
- binding: 3,
7963
- visibility: GPUShaderStage.FRAGMENT,
7964
- texture: {
7965
- sampleType: 'float',
7966
- viewDimension: '2d'
7967
- }
7968
- }, {
7969
- binding: 4,
7970
- visibility: GPUShaderStage.FRAGMENT,
7971
- sampler: {
7972
- type: 'filtering'
7973
- }
7974
- }, {
7975
- binding: 5,
7976
- visibility: GPUShaderStage.FRAGMENT,
7977
- buffer: {
7978
- type: 'uniform'
7979
- }
7980
- }])]
8136
+ label: 'bglForRender',
8137
+ entries: e
7981
8138
  });
8139
+ if (this.isVideo == true) {
8140
+ this.createBindGroupForRender();
8141
+ }
7982
8142
  }
7983
8143
  }
7984
8144
  exports.default = Materials;
@@ -8236,7 +8396,7 @@ var _materials = _interopRequireDefault(require("./materials"));
8236
8396
  var _fragmentVideo = require("../shaders/fragment.video.wgsl");
8237
8397
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
8238
8398
  class MEMeshObj extends _materials.default {
8239
- constructor(canvas, device, context, o, sceneUniformBuffer) {
8399
+ constructor(canvas, device, context, o, inputHandler) {
8240
8400
  super(device);
8241
8401
  if (typeof o.name === 'undefined') o.name = (0, _utils.genName)(3);
8242
8402
  if (typeof o.raycast === 'undefined') {
@@ -8254,6 +8414,7 @@ class MEMeshObj extends _materials.default {
8254
8414
  this.entityArgPass = o.entityArgPass;
8255
8415
  this.clearColor = "red";
8256
8416
  this.video = null;
8417
+ this.FINISH_VIDIO_INIT = false;
8257
8418
 
8258
8419
  // Mesh stuff - for single mesh or t-posed (fiktive-first in loading order)
8259
8420
  this.mesh = o.mesh;
@@ -8269,7 +8430,7 @@ class MEMeshObj extends _materials.default {
8269
8430
  console.log(`%c Mesh objAnim exist: ${o.objAnim}`, _utils.LOG_FUNNY_SMALL);
8270
8431
  this.drawElements = this.drawElementsAnim;
8271
8432
  }
8272
- this.inputHandler = null;
8433
+ this.inputHandler = inputHandler;
8273
8434
  this.cameras = o.cameras;
8274
8435
  this.mainCameraParams = {
8275
8436
  type: o.mainCameraParams.type,
@@ -8353,14 +8514,6 @@ class MEMeshObj extends _materials.default {
8353
8514
  this.indexBuffer.unmap();
8354
8515
  this.indexCount = indexCount;
8355
8516
 
8356
- // Create the depth texture for rendering/sampling the shadow map.
8357
- this.shadowDepthTexture = this.device.createTexture({
8358
- size: [this.shadowDepthTextureSize, this.shadowDepthTextureSize, 1],
8359
- usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
8360
- format: 'depth32float'
8361
- });
8362
- this.shadowDepthTextureView = this.shadowDepthTexture.createView();
8363
-
8364
8517
  // Create some common descriptors used for both the shadow pipeline
8365
8518
  // and the color rendering pipeline.
8366
8519
  this.vertexBuffers = [{
@@ -8390,91 +8543,37 @@ class MEMeshObj extends _materials.default {
8390
8543
  }];
8391
8544
  this.primitive = {
8392
8545
  topology: 'triangle-list',
8393
- // cullMode: 'back',
8394
- cullMode: 'none'
8546
+ cullMode: 'back',
8547
+ // typical for shadow passes
8548
+ frontFace: 'ccw'
8395
8549
  };
8396
- this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
8397
- entries: [{
8398
- binding: 0,
8399
- visibility: GPUShaderStage.VERTEX,
8400
- buffer: {
8401
- type: 'uniform'
8402
- }
8403
- }]
8404
- });
8405
- this.shadowPipeline = this.device.createRenderPipeline({
8406
- layout: this.device.createPipelineLayout({
8407
- bindGroupLayouts: [this.uniformBufferBindGroupLayout, this.uniformBufferBindGroupLayout]
8408
- }),
8409
- vertex: {
8410
- module: this.device.createShaderModule({
8411
- code: _vertexShadow.vertexShadowWGSL
8412
- }),
8413
- buffers: this.vertexBuffers
8414
- },
8415
- depthStencil: {
8416
- depthWriteEnabled: true,
8417
- depthCompare: 'less',
8418
- format: 'depth32float'
8419
- },
8420
- primitive: this.primitive
8421
- });
8422
8550
 
8423
8551
  // Create a bind group layout which holds the scene uniforms and
8424
8552
  // the texture+sampler for depth. We create it manually because the WebPU
8425
8553
  // implementation doesn't infer this from the shader (yet).
8426
8554
  this.createLayoutForRender();
8427
- this.setupPipeline();
8428
- const depthTexture = this.device.createTexture({
8429
- size: [canvas.width, canvas.height],
8430
- // format: 'depth24plus-stencil8',
8431
- format: 'depth24plus',
8432
- usage: GPUTextureUsage.RENDER_ATTACHMENT
8433
- });
8434
- this.renderPassDescriptor = {
8435
- colorAttachments: [{
8436
- // view is acquired and set in render loop.
8437
- view: undefined,
8438
- clearValue: this.clearColor,
8439
- loadOp: 'clear',
8440
- // load -> clear = fix for FF
8441
- storeOp: 'store'
8442
- }],
8443
- depthStencilAttachment: {
8444
- view: depthTexture.createView(),
8445
- depthClearValue: 1.0,
8446
- depthLoadOp: 'clear',
8447
- depthStoreOp: 'store'
8448
- // stencilClearValue: 0,
8449
- // stencilLoadOp: 'clear',
8450
- // stencilStoreOp: 'store',
8451
- }
8452
- };
8453
8555
  this.modelUniformBuffer = this.device.createBuffer({
8454
8556
  size: 4 * 16,
8455
8557
  // 4x4 matrix
8456
8558
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
8457
8559
  });
8458
8560
  this.sceneUniformBuffer = this.device.createBuffer({
8459
- // Two 4x4 viewProj matrices,
8460
- // one for the camera and one for the light.
8461
- // Then a vec3 for the light position.
8462
- // Rounded to the nearest multiple of 16.
8463
- size: 2 * 4 * 16 + 4 * 4,
8561
+ label: 'sceneUniformBuffer per mesh',
8562
+ size: 160,
8464
8563
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
8465
8564
  });
8466
- console.log('test buffer sceneUniformBuffer ', this.sceneUniformBuffer);
8467
- this.sceneBindGroupForShadow = this.device.createBindGroup({
8468
- layout: this.uniformBufferBindGroupLayout,
8565
+ this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
8566
+ label: 'uniformBufferBindGroupLayout in mesh',
8469
8567
  entries: [{
8470
8568
  binding: 0,
8471
- resource: {
8472
- buffer: this.sceneUniformBuffer
8569
+ visibility: GPUShaderStage.VERTEX,
8570
+ buffer: {
8571
+ type: 'uniform'
8473
8572
  }
8474
8573
  }]
8475
8574
  });
8476
- this.createBindGroupForRender();
8477
8575
  this.modelBindGroup = this.device.createBindGroup({
8576
+ label: 'modelBindGroup in mesh',
8478
8577
  layout: this.uniformBufferBindGroupLayout,
8479
8578
  entries: [{
8480
8579
  binding: 0,
@@ -8483,49 +8582,49 @@ class MEMeshObj extends _materials.default {
8483
8582
  }
8484
8583
  }]
8485
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
+ });
8486
8600
 
8487
8601
  // Rotates the camera around the origin based on time.
8488
- this.getTransformationMatrix = pos => {
8602
+ this.getTransformationMatrix = (mainRenderBundle, spotLight) => {
8489
8603
  const now = Date.now();
8490
- const deltaTime = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
8604
+ const dt = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
8491
8605
  this.lastFrameMS = now;
8492
- // const this.viewMatrix = mat4.identity()
8493
8606
  const camera = this.cameras[this.mainCameraParams.type];
8494
-
8495
- // engine frame
8496
8607
  camera.update(dt, inputHandler());
8497
8608
  const camVP = _wgpuMatrix.mat4.multiply(camera.projectionMatrix, camera.view);
8498
8609
  for (const mesh of mainRenderBundle) {
8499
- // Light’s viewProj should come from your SpotLight
8500
- // If you have multiple lights, you’ll need an array UBO or multiple passes.
8501
- 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
8502
8614
  sceneData.set(spotLight.viewProjMatrix, 0);
8615
+
8616
+ // Camera VP
8503
8617
  sceneData.set(camVP, 16);
8504
- 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);
8505
8624
  device.queue.writeBuffer(mesh.sceneUniformBuffer,
8506
- // or a shared one if/when you centralize it
8625
+ // or shared buffer
8507
8626
  0, sceneData.buffer, sceneData.byteOffset, sceneData.byteLength);
8508
8627
  }
8509
- // this.viewMatrix = camera.update(deltaTime, this.inputHandler());
8510
- // const scaleVec = [1, 1, 1]; // your desired scale OPTION 1
8511
- // const scaleMatrix = mat4.scaling(scaleVec);
8512
- // // Apply scaling
8513
- // mat4.multiply(scaleMatrix, this.viewMatrix, this.viewMatrix);
8514
- // mat4.translate(this.viewMatrix, vec3.fromValues(pos.x, pos.y, pos.z), this.viewMatrix);
8515
-
8516
- // if(this.itIsPhysicsBody == true) {
8517
- // mat4.rotate(
8518
- // this.viewMatrix,
8519
- // vec3.fromValues(this.rotation.axis.x, this.rotation.axis.y, this.rotation.axis.z),
8520
- // degToRad(this.rotation.angle), this.viewMatrix)
8521
- // } else {
8522
- // mat4.rotateX(this.viewMatrix, Math.PI * this.rotation.getRotX(), this.viewMatrix);
8523
- // mat4.rotateY(this.viewMatrix, Math.PI * this.rotation.getRotY(), this.viewMatrix);
8524
- // mat4.rotateZ(this.viewMatrix, Math.PI * this.rotation.getRotZ(), this.viewMatrix);
8525
- // // console.info('NOT PHYSICS angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
8526
- // }
8527
- // mat4.multiply(camera.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
8528
- // return this.modelViewProjectionMatrix;
8529
8628
  };
8530
8629
  this.getModelMatrix = pos => {
8531
8630
  let modelMatrix = _wgpuMatrix.mat4.identity();
@@ -8546,28 +8645,25 @@ class MEMeshObj extends _materials.default {
8546
8645
  const modelMatrix = _wgpuMatrix.mat4.translation([0, 0, 0]);
8547
8646
  const modelData = modelMatrix;
8548
8647
  this.device.queue.writeBuffer(this.modelUniformBuffer, 0, modelData.buffer, modelData.byteOffset, modelData.byteLength);
8549
- this.shadowPassDescriptor = {
8550
- colorAttachments: [],
8551
- depthStencilAttachment: {
8552
- view: this.shadowDepthTextureView,
8553
- depthClearValue: 1.0,
8554
- depthLoadOp: 'clear',
8555
- depthStoreOp: 'store'
8556
- }
8557
- };
8558
8648
  this.done = true;
8649
+ try {
8650
+ this.setupPipeline();
8651
+ } catch (err) {
8652
+ console.log('err in create pipeline in init ', err);
8653
+ }
8559
8654
  }).then(() => {
8560
8655
  if (typeof this.objAnim !== 'undefined' && this.objAnim !== null) {
8561
- console.log('after all load configutr mesh list buffers');
8656
+ console.log('after all updateMeshListBuffers...');
8562
8657
  this.updateMeshListBuffers();
8563
8658
  }
8564
8659
  });
8565
8660
  }
8566
8661
  setupPipeline = () => {
8567
- console.log('Set Pipeline✅');
8662
+ this.createBindGroupForRender();
8568
8663
  this.pipeline = this.device.createRenderPipeline({
8569
8664
  label: 'Mesh Pipeline ✅',
8570
8665
  layout: this.device.createPipelineLayout({
8666
+ label: 'createPipelineLayout Mesh',
8571
8667
  bindGroupLayouts: [this.bglForRender, this.uniformBufferBindGroupLayout]
8572
8668
  }),
8573
8669
  vertex: {
@@ -8596,26 +8692,13 @@ class MEMeshObj extends _materials.default {
8596
8692
  },
8597
8693
  primitive: this.primitive
8598
8694
  });
8695
+ console.log('✅Set Pipeline done');
8599
8696
  };
8600
- draw = () => {
8697
+ updateModelUniformBuffer = () => {
8601
8698
  if (this.done == false) return;
8602
8699
  // Per-object model matrix only
8603
8700
  const modelMatrix = this.getModelMatrix(this.position);
8604
8701
  this.device.queue.writeBuffer(this.modelUniformBuffer, 0, modelMatrix.buffer, modelMatrix.byteOffset, modelMatrix.byteLength);
8605
- // Acquire swapchain view for the pass
8606
- this.renderPassDescriptor.colorAttachments[0].view = this.context.getCurrentTexture().createView();
8607
- };
8608
- drawElements = renderPass => {
8609
- if (this.isVideo) {
8610
- this.updateVideoTexture();
8611
- }
8612
- renderPass.setBindGroup(0, this.sceneBindGroupForRender);
8613
- renderPass.setBindGroup(1, this.modelBindGroup);
8614
- renderPass.setVertexBuffer(0, this.vertexBuffer);
8615
- renderPass.setVertexBuffer(1, this.vertexNormalsBuffer);
8616
- renderPass.setVertexBuffer(2, this.vertexTexCoordsBuffer);
8617
- renderPass.setIndexBuffer(this.indexBuffer, 'uint16');
8618
- renderPass.drawIndexed(this.indexCount);
8619
8702
  };
8620
8703
  createGPUBuffer(dataArray, usage) {
8621
8704
  if (!dataArray || typeof dataArray.length !== 'number') {
@@ -8674,7 +8757,35 @@ class MEMeshObj extends _materials.default {
8674
8757
  mesh.indexCount = indexCount;
8675
8758
  }
8676
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
+ };
8677
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
+ }
8678
8789
  renderPass.setBindGroup(0, this.sceneBindGroupForRender);
8679
8790
  renderPass.setBindGroup(1, this.modelBindGroup);
8680
8791
  const mesh = this.objAnim.meshList[this.objAnim.id + this.objAnim.currentAni];
@@ -8695,9 +8806,7 @@ class MEMeshObj extends _materials.default {
8695
8806
  }
8696
8807
  }
8697
8808
  };
8698
- drawShadows = shadowPass => {
8699
- shadowPass.setBindGroup(0, this.sceneBindGroupForShadow);
8700
- shadowPass.setBindGroup(1, this.modelBindGroup);
8809
+ drawShadows = (shadowPass, light) => {
8701
8810
  shadowPass.setVertexBuffer(0, this.vertexBuffer);
8702
8811
  shadowPass.setVertexBuffer(1, this.vertexNormalsBuffer);
8703
8812
  shadowPass.setVertexBuffer(2, this.vertexTexCoordsBuffer);
@@ -10152,18 +10261,19 @@ struct Scene {
10152
10261
  @group(0) @binding(2) var shadowSampler: sampler_comparison;
10153
10262
  @group(0) @binding(3) var meshTexture: texture_external;
10154
10263
  @group(0) @binding(4) var meshSampler: sampler;
10155
-
10156
10264
  @group(0) @binding(5) var<uniform> postFXMode: u32;
10157
10265
 
10158
10266
  // ❌ No binding(4) here!
10159
10267
 
10160
10268
  struct FragmentInput {
10161
- @location(0) shadowPos : vec3f,
10269
+ @location(0) shadowPos : vec4f,
10162
10270
  @location(1) fragPos : vec3f,
10163
10271
  @location(2) fragNorm : vec3f,
10164
10272
  @location(3) uv : vec2f,
10165
10273
  }
10166
10274
 
10275
+
10276
+
10167
10277
  const albedo = vec3f(0.9);
10168
10278
  const ambientFactor = 0.7;
10169
10279
 
@@ -10232,52 +10342,57 @@ Object.defineProperty(exports, "__esModule", {
10232
10342
  exports.fragmentWGSL = void 0;
10233
10343
  let fragmentWGSL = exports.fragmentWGSL = `override shadowDepthTextureSize: f32 = 1024.0;
10234
10344
 
10345
+ // Created by Nikola Lukic with chatgtp assist.
10346
+
10235
10347
  struct Scene {
10236
- lightViewProjMatrix : mat4x4f,
10348
+ lightViewProjMatrix : mat4x4f,
10237
10349
  cameraViewProjMatrix : mat4x4f,
10238
- lightPos : vec3f,
10239
- padding : f32, // Required for alignment
10240
- }
10350
+ cameraPos : vec3f,
10351
+ padding2 : f32, // align to 16 bytes
10352
+ lightPos : vec3f,
10353
+ padding : f32, // align to 16 bytes
10354
+ };
10241
10355
 
10242
10356
  struct SpotLight {
10243
- position : vec3f,
10244
- _pad1 : f32,
10357
+ position : vec3f,
10358
+ _pad1 : f32,
10359
+
10360
+ direction : vec3f,
10361
+ _pad2 : f32,
10245
10362
 
10246
- direction : vec3f,
10247
- _pad2 : f32,
10363
+ innerCutoff : f32,
10364
+ outerCutoff : f32,
10365
+ intensity : f32,
10366
+ _pad3 : f32,
10248
10367
 
10249
- innerCutoff : f32,
10250
- outerCutoff : f32,
10251
- intensity : f32,
10252
- _pad3 : f32,
10368
+ color : vec3f,
10369
+ _pad4 : f32,
10253
10370
 
10254
- color : vec3f,
10255
- _pad4 : f32,
10371
+ range : f32,
10372
+ ambientFactor : f32,
10373
+ shadowBias : f32,
10374
+ _pad5 : f32,
10256
10375
 
10257
- range : f32,
10258
- ambientFactor: f32, // new
10259
- _pad5 : vec2f, // padding to align to 16 bytes
10376
+ lightViewProj : mat4x4<f32>,
10260
10377
  };
10261
10378
 
10262
- const MAX_SPOTLIGHTS = 20u; // adjust as needed
10379
+ const MAX_SPOTLIGHTS = 20u;
10263
10380
 
10264
10381
  @group(0) @binding(0) var<uniform> scene : Scene;
10265
- @group(0) @binding(1) var shadowMap: texture_depth_2d;
10382
+ @group(0) @binding(1) var shadowMapArray: texture_depth_2d_array;
10266
10383
  @group(0) @binding(2) var shadowSampler: sampler_comparison;
10267
10384
  @group(0) @binding(3) var meshTexture: texture_2d<f32>;
10268
10385
  @group(0) @binding(4) var meshSampler: sampler;
10269
10386
  @group(0) @binding(5) var<uniform> spotlights: array<SpotLight, MAX_SPOTLIGHTS>;
10270
- // @group(0) @binding(6) var<uniform> spotlight1: SpotLight;
10271
10387
 
10272
10388
  struct FragmentInput {
10273
- @location(0) shadowPos : vec3f,
10274
- @location(1) fragPos : vec3f,
10275
- @location(2) fragNorm : vec3f,
10276
- @location(3) uv : vec2f,
10389
+ @location(0) shadowPos : vec4f,
10390
+ @location(1) fragPos : vec3f,
10391
+ @location(2) fragNorm : vec3f,
10392
+ @location(3) uv : vec2f,
10277
10393
  }
10278
10394
 
10279
10395
  const albedo = vec3f(0.9);
10280
- // const ambientFactor = 0.7;
10281
10396
 
10282
10397
  fn calculateSpotlightFactor(light: SpotLight, fragPos: vec3f) -> f32 {
10283
10398
  let L = normalize(light.position - fragPos);
@@ -10304,39 +10419,64 @@ fn computeSpotLight(light: SpotLight, normal: vec3f, fragPos: vec3f, viewDir: ve
10304
10419
  return (diffuse + specular) * spotFactor;
10305
10420
  }
10306
10421
 
10307
- @fragment
10308
- fn main(input : FragmentInput) -> @location(0) vec4f {
10309
- // Shadow PCF
10310
- var visibility = 0.0;
10311
- let oneOverSize = 1.0 / shadowDepthTextureSize;
10312
- for (var y = -1; y <= 1; y++) {
10313
- for (var x = -1; x <= 1; x++) {
10314
- let offset = vec2f(vec2(x, y)) * oneOverSize;
10315
- visibility += textureSampleCompare(
10316
- shadowMap, shadowSampler,
10317
- input.shadowPos.xy + offset, input.shadowPos.z - 0.007
10318
- );
10319
- }
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
+ );
10320
10447
  }
10321
- visibility /= 9.0;
10448
+ return visibility / 9.0;
10449
+ }
10450
+
10451
+ @fragment
10452
+ fn main(input: FragmentInput) -> @location(0) vec4f {
10322
10453
  let norm = normalize(input.fragNorm);
10323
- let viewDir = normalize(scene.cameraViewProjMatrix[3].xyz - input.fragPos);
10324
10454
 
10325
- // Spotlight contribution (diffuse + specular + cone + distance)
10455
+ let viewDir = normalize(scene.cameraPos - input.fragPos);
10456
+ // let viewDir = normalize(scene.cameraViewProjMatrix[3].xyz - input.fragPos);
10457
+
10326
10458
  var lightContribution = vec3f(0.0);
10327
10459
  var ambient = vec3f(0.0);
10328
10460
 
10329
- for (var i = 0u; i < MAX_SPOTLIGHTS; i++) {
10330
- lightContribution += computeSpotLight(spotlights[i], norm, input.fragPos, viewDir);
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;
10331
10473
  ambient += spotlights[i].ambientFactor * spotlights[i].color;
10332
10474
  }
10333
10475
 
10334
10476
  let texColor = textureSample(meshTexture, meshSampler, input.uv);
10335
- let finalColor = texColor.rgb * (ambient + lightContribution * visibility) * albedo;
10336
-
10477
+ let finalColor = texColor.rgb * (ambient + lightContribution); // * albedo;
10337
10478
  return vec4f(finalColor, 1.0);
10338
- }
10339
- `;
10479
+ }`;
10340
10480
 
10341
10481
  },{}],22:[function(require,module,exports){
10342
10482
  "use strict";
@@ -10417,11 +10557,10 @@ struct Model {
10417
10557
  @group(1) @binding(0) var<uniform> model : Model;
10418
10558
 
10419
10559
  struct VertexOutput {
10420
- @location(0) shadowPos: vec3f,
10560
+ @location(0) shadowPos: vec4f, // now vec4
10421
10561
  @location(1) fragPos: vec3f,
10422
10562
  @location(2) fragNorm: vec3f,
10423
- @location(3) uv : vec2f,
10424
-
10563
+ @location(3) uv: vec2f,
10425
10564
  @builtin(position) Position: vec4f,
10426
10565
  }
10427
10566
 
@@ -10429,34 +10568,21 @@ struct VertexOutput {
10429
10568
  fn main(
10430
10569
  @location(0) position: vec3f,
10431
10570
  @location(1) normal: vec3f,
10432
- @location(2) uv : vec2f
10571
+ @location(2) uv: vec2f
10433
10572
  ) -> VertexOutput {
10434
10573
  var output : VertexOutput;
10435
10574
 
10436
- // XY is in (-1, 1) space, Z is in (0, 1) space
10437
10575
  let posFromLight = scene.lightViewProjMatrix * model.modelMatrix * vec4(position, 1.0);
10438
-
10439
- // Convert XY to (0, 1)
10440
- // Y is flipped because texture coords are Y-down.
10441
- output.shadowPos = vec3(
10442
- posFromLight.xy * vec2(0.5, -0.5) + vec2(0.5),
10443
- posFromLight.z
10444
- );
10445
-
10446
- // follewed camera code
10447
- // output.Position = scene.cameraViewProjMatrix * model.modelMatrix * vec4(position, 1.0);
10448
- // output.fragPos = output.Position.xyz;
10449
- // output.fragNorm = normal;
10576
+ output.shadowPos = posFromLight; // pass full vec4 for perspective divide
10450
10577
 
10451
10578
  let worldPos = model.modelMatrix * vec4(position, 1.0);
10452
10579
  output.Position = scene.cameraViewProjMatrix * worldPos;
10453
- output.fragPos = worldPos.xyz; // ✅ world space
10580
+ output.fragPos = worldPos.xyz;
10454
10581
 
10455
10582
  output.fragNorm = normalize((model.modelMatrix * vec4(normal, 0.0)).xyz);
10456
10583
  output.uv = uv;
10457
10584
  return output;
10458
- }
10459
- `;
10585
+ }`;
10460
10586
 
10461
10587
  },{}],24:[function(require,module,exports){
10462
10588
  "use strict";
@@ -10584,7 +10710,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
10584
10710
  * @github zlatnaspirala
10585
10711
  */
10586
10712
  class MatrixEngineWGPU {
10587
- lightContainer;
10588
10713
  mainRenderBundle = [];
10589
10714
  lightContainer = [];
10590
10715
  frame = () => {};
@@ -10649,7 +10774,6 @@ class MatrixEngineWGPU {
10649
10774
 
10650
10775
  // The camera types
10651
10776
  const initialCameraPosition = _wgpuMatrix.vec3.create(0, 0, 0);
10652
- // console.log('passed : o.mainCameraParams.responseCoef ', o.mainCameraParams.responseCoef)
10653
10777
  this.mainCameraParams = {
10654
10778
  type: this.options.mainCameraParams.type,
10655
10779
  responseCoef: this.options.mainCameraParams.responseCoef
@@ -10709,9 +10833,78 @@ class MatrixEngineWGPU {
10709
10833
  createGlobalStuff() {
10710
10834
  this.spotlightUniformBuffer = this.device.createBuffer({
10711
10835
  label: 'spotlightUniformBufferGLOBAL',
10712
- size: this.MAX_SPOTLIGHTS * 80,
10836
+ size: this.MAX_SPOTLIGHTS * 144,
10713
10837
  usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
10714
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();
10715
10908
  }
10716
10909
  getSceneObjectByName(name) {
10717
10910
  return this.mainRenderBundle.find(sceneObject => sceneObject.name === name);
@@ -10918,9 +11111,9 @@ class MatrixEngineWGPU {
10918
11111
  };
10919
11112
  addLight(o) {
10920
11113
  const camera = this.cameras[this.mainCameraParams.type];
10921
- let newLight = new _lights.SpotLight(camera, this.inputHandler);
10922
- newLight.prepareBuffer(this.device);
11114
+ let newLight = new _lights.SpotLight(camera, this.inputHandler, this.device);
10923
11115
  this.lightContainer.push(newLight);
11116
+ this.createTexArrayForShadows();
10924
11117
  console.log(`%cAdd light: ${newLight}`, _utils.LOG_FUNNY_SMALL);
10925
11118
  }
10926
11119
  addMeshObj = (o, clearColor = this.options.clearColor) => {
@@ -11013,14 +11206,13 @@ class MatrixEngineWGPU {
11013
11206
  // scale for all second option!
11014
11207
  o.objAnim.scaleAll = function (s) {
11015
11208
  for (var k in this.meshList) {
11016
- console.log('SCALE');
11209
+ console.log('SCALE meshList');
11017
11210
  this.meshList[k].setScale(s);
11018
11211
  }
11019
11212
  };
11020
11213
  }
11021
- let myMesh1 = new _meshObj.default(this.canvas, this.device, this.context, o);
11214
+ let myMesh1 = new _meshObj.default(this.canvas, this.device, this.context, o, this.inputHandler);
11022
11215
  myMesh1.spotlightUniformBuffer = this.spotlightUniformBuffer;
11023
- myMesh1.inputHandler = this.inputHandler;
11024
11216
  myMesh1.clearColor = clearColor;
11025
11217
  if (o.physics.enabled == true) {
11026
11218
  this.matrixAmmo.addPhysics(myMesh1, o.physics);
@@ -11039,33 +11231,15 @@ class MatrixEngineWGPU {
11039
11231
  this.mainRenderBundle = [];
11040
11232
  this.canvas.remove();
11041
11233
  };
11042
- test = () => {
11043
- const now = Date.now();
11044
- // First frame safety
11045
- let dt = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
11046
- if (!this.lastFrameMS) {
11047
- dt = 16;
11048
- }
11049
- this.lastFrameMS = now;
11050
- const camera = this.cameras[this.mainCameraParams.type];
11051
- camera.update(dt, this.inputHandler());
11052
- const camVP = _wgpuMatrix.mat4.multiply(camera.projectionMatrix, camera.view); // P * V
11053
-
11054
- for (const mesh of this.mainRenderBundle) {
11055
- // scene buffer layout = 0..63 lightVP, 64..127 camVP, 128..143 lightPos(+pad)
11056
- this.device.queue.writeBuffer(mesh.sceneUniformBuffer, 64,
11057
- // cameraViewProjMatrix offset
11058
- camVP.buffer, camVP.byteOffset, camVP.byteLength);
11059
- }
11060
- };
11061
11234
  updateLights() {
11062
- // Update buffer every frame
11063
- const data = new Float32Array(this.MAX_SPOTLIGHTS * 20);
11235
+ const floatsPerLight = 36; // not 20 anymore
11236
+ const data = new Float32Array(this.MAX_SPOTLIGHTS * floatsPerLight);
11064
11237
  for (let i = 0; i < this.MAX_SPOTLIGHTS; i++) {
11065
11238
  if (i < this.lightContainer.length) {
11066
- data.set(this.lightContainer[i].getLightDataBuffer(), i * 20);
11239
+ const buf = this.lightContainer[i].getLightDataBuffer();
11240
+ data.set(buf, i * floatsPerLight);
11067
11241
  } else {
11068
- data.set(new Float32Array(20), i * 20);
11242
+ data.set(new Float32Array(floatsPerLight), i * floatsPerLight);
11069
11243
  }
11070
11244
  }
11071
11245
  this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, data.buffer);
@@ -11077,54 +11251,103 @@ class MatrixEngineWGPU {
11077
11251
  }, 200);
11078
11252
  return;
11079
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;
11080
11273
  try {
11081
- let shadowPass = null;
11082
- let renderPass;
11083
11274
  let commandEncoder = this.device.createCommandEncoder();
11084
11275
  this.updateLights();
11085
- this.test();
11086
-
11087
11276
  // 1️⃣ Update light data (position, direction, uniforms)
11088
11277
  for (const light of this.lightContainer) {
11278
+ light.update();
11279
+ // light.updateSceneUniforms(this.mainRenderBundle, this.cameras.WASD);
11089
11280
  this.mainRenderBundle.forEach((meItem, index) => {
11090
- 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
+ // }
11091
11286
  });
11092
11287
  }
11093
- this.mainRenderBundle.forEach((meItem, index) => {
11094
- meItem.position.update();
11095
- });
11096
11288
  if (this.matrixAmmo) this.matrixAmmo.updatePhysics();
11097
- const firstItem = this.mainRenderBundle[0];
11098
- shadowPass = commandEncoder.beginRenderPass(firstItem.shadowPassDescriptor);
11099
- shadowPass.setPipeline(firstItem.shadowPipeline);
11100
- for (const meItem of this.mainRenderBundle) {
11101
- meItem.drawShadows(shadowPass);
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();
11102
11318
  }
11103
- shadowPass.end();
11104
- this.mainRenderBundle.forEach((meItem, index) => {
11105
- if (index == 0) {
11106
- meItem.draw(commandEncoder);
11107
- meItem.renderPassDescriptor.colorAttachments[0].view = this.context.getCurrentTexture().createView();
11108
- renderPass = commandEncoder.beginRenderPass(meItem.renderPassDescriptor);
11109
- renderPass.setPipeline(meItem.pipeline);
11110
- } else {
11111
- 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('✅shadowVideoView', this.shadowVideoView);
11329
+ m.shadowDepthTextureView = this.shadowVideoView;
11330
+ m.FINISH_VIDIO_INIT = true;
11331
+ m.setupPipeline();
11332
+ } else {
11333
+ m.shadowDepthTextureView = this.shadowArrayView;
11334
+ m.setupPipeline();
11335
+ }
11336
+ }
11112
11337
  }
11113
- });
11114
- this.mainRenderBundle.forEach((meItem, index) => {
11115
- meItem.drawElements(renderPass);
11116
- });
11117
- if (renderPass) renderPass.end();
11338
+ mesh.drawElements(pass, this.lightContainer);
11339
+ }
11340
+ pass.end();
11118
11341
  this.device.queue.submit([commandEncoder.finish()]);
11119
11342
  requestAnimationFrame(this.frame);
11120
11343
  } catch (err) {
11121
- console.log('%cLoop (err):' + err, _utils.LOG_WARN);
11344
+ console.log('%cLoop(err):' + err, _utils.LOG_WARN);
11122
11345
  requestAnimationFrame(this.frame);
11123
11346
  }
11124
11347
  };
11125
11348
  framePassPerObject = () => {
11126
11349
  let commandEncoder = this.device.createCommandEncoder();
11127
- this.matrixAmmo.updatePhysics();
11350
+ if (this.matrixAmmo.rigidBodies.length > 0) this.matrixAmmo.updatePhysics();
11128
11351
  this.mainRenderBundle.forEach((meItem, index) => {
11129
11352
  if (index === 0) {
11130
11353
  if (meItem.renderPassDescriptor) meItem.renderPassDescriptor.colorAttachments[0].loadOp = 'clear';