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.
- package/examples/camera-texture.js +2 -0
- package/examples/load-obj-file.js +6 -6
- package/examples/load-objs-sequence.js +8 -0
- package/examples/video-texture.js +13 -7
- package/package.json +4 -2
- package/public/examples.js +625 -402
- package/readme.md +19 -8
- package/src/engine/engine.js +1 -1
- package/src/engine/lights.js +177 -77
- package/src/engine/materials.js +125 -63
- package/src/engine/mesh-obj.js +91 -151
- package/src/shaders/fragment.video.wgsl.js +3 -2
- package/src/shaders/fragment.wgsl.js +77 -47
- package/src/shaders/shaders.js +0 -1
- package/src/shaders/vertex.wgsl.js +6 -20
- package/src/world.js +175 -71
package/src/world.js
CHANGED
|
@@ -20,12 +20,10 @@ import {SpotLight} from "./engine/lights.js";
|
|
|
20
20
|
* @github zlatnaspirala
|
|
21
21
|
*/
|
|
22
22
|
export default class MatrixEngineWGPU {
|
|
23
|
-
lightContainer
|
|
24
23
|
|
|
25
24
|
mainRenderBundle = [];
|
|
26
25
|
lightContainer = [];
|
|
27
26
|
frame = () => {};
|
|
28
|
-
|
|
29
27
|
entityHolder = [];
|
|
30
28
|
|
|
31
29
|
entityArgPass = {
|
|
@@ -68,7 +66,7 @@ export default class MatrixEngineWGPU {
|
|
|
68
66
|
this.mainCameraParams = options.mainCameraParams;
|
|
69
67
|
|
|
70
68
|
const target = this.options.appendTo || document.body;
|
|
71
|
-
var canvas = document.createElement('canvas')
|
|
69
|
+
var canvas = document.createElement('canvas');
|
|
72
70
|
canvas.id = this.options.canvasId;
|
|
73
71
|
if(this.options.canvasSize == 'fullscreen') {
|
|
74
72
|
canvas.width = window.innerWidth;
|
|
@@ -81,11 +79,10 @@ export default class MatrixEngineWGPU {
|
|
|
81
79
|
|
|
82
80
|
// The camera types
|
|
83
81
|
const initialCameraPosition = vec3.create(0, 0, 0);
|
|
84
|
-
// console.log('passed : o.mainCameraParams.responseCoef ', o.mainCameraParams.responseCoef)
|
|
85
82
|
this.mainCameraParams = {
|
|
86
83
|
type: this.options.mainCameraParams.type,
|
|
87
84
|
responseCoef: this.options.mainCameraParams.responseCoef
|
|
88
|
-
}
|
|
85
|
+
};
|
|
89
86
|
|
|
90
87
|
this.cameras = {
|
|
91
88
|
arcball: new ArcballCamera({position: initialCameraPosition}),
|
|
@@ -139,9 +136,86 @@ export default class MatrixEngineWGPU {
|
|
|
139
136
|
createGlobalStuff() {
|
|
140
137
|
this.spotlightUniformBuffer = this.device.createBuffer({
|
|
141
138
|
label: 'spotlightUniformBufferGLOBAL',
|
|
142
|
-
size: this.MAX_SPOTLIGHTS *
|
|
139
|
+
size: this.MAX_SPOTLIGHTS * 144,
|
|
143
140
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
144
141
|
});
|
|
142
|
+
|
|
143
|
+
this.SHADOW_RES = 1024;
|
|
144
|
+
this.createTexArrayForShadows()
|
|
145
|
+
|
|
146
|
+
this.mainDepthTexture = this.device.createTexture({
|
|
147
|
+
size: [this.canvas.width, this.canvas.height],
|
|
148
|
+
format: 'depth24plus',
|
|
149
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
this.mainDepthView = this.mainDepthTexture.createView();
|
|
153
|
+
|
|
154
|
+
this.mainRenderPassDesc = {
|
|
155
|
+
label: 'mainRenderPassDesc',
|
|
156
|
+
colorAttachments: [{
|
|
157
|
+
view: undefined, // set each frame
|
|
158
|
+
loadOp: 'clear',
|
|
159
|
+
storeOp: 'store',
|
|
160
|
+
clearValue: [0.02, 0.02, 0.02, 1],
|
|
161
|
+
}],
|
|
162
|
+
depthStencilAttachment: {
|
|
163
|
+
view: this.mainDepthView, // fixed
|
|
164
|
+
depthLoadOp: 'clear',
|
|
165
|
+
depthStoreOp: 'store',
|
|
166
|
+
depthClearValue: 1.0,
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
createTexArrayForShadows() {
|
|
171
|
+
let numberOfLights = this.lightContainer.length;
|
|
172
|
+
if(this.lightContainer.length == 0) {
|
|
173
|
+
// console.warn('Wait for init light instance')
|
|
174
|
+
setTimeout(() => {
|
|
175
|
+
// console.info('Test light again...')
|
|
176
|
+
this.createMe();
|
|
177
|
+
}, 800);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
this.createMe = () => {
|
|
181
|
+
Math.max(1, this.lightContainer.length);
|
|
182
|
+
if(this.lightContainer.length == 0) {
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
console.warn('Create now test...')
|
|
185
|
+
this.createMe();
|
|
186
|
+
}, 800);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.warn('Create this.shadowTextureArray...')
|
|
191
|
+
this.shadowTextureArray = this.device.createTexture({
|
|
192
|
+
label: `shadowTextureArray[GLOBAL] num of light ${numberOfLights}`,
|
|
193
|
+
size: {
|
|
194
|
+
width: 1024,
|
|
195
|
+
height: 1024,
|
|
196
|
+
depthOrArrayLayers: numberOfLights, // at least 1
|
|
197
|
+
},
|
|
198
|
+
dimension: '2d',
|
|
199
|
+
format: 'depth32float',
|
|
200
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
this.shadowArrayView = this.shadowTextureArray.createView({
|
|
204
|
+
dimension: '2d-array'
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
this.shadowVideoTexture = this.device.createTexture({
|
|
208
|
+
size: [1024, 1024],
|
|
209
|
+
format: "depth32float",
|
|
210
|
+
usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
this.shadowVideoView = this.shadowVideoTexture.createView({
|
|
214
|
+
dimension: "2d",
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.createMe();
|
|
145
219
|
}
|
|
146
220
|
|
|
147
221
|
getSceneObjectByName(name) {
|
|
@@ -250,9 +324,9 @@ export default class MatrixEngineWGPU {
|
|
|
250
324
|
|
|
251
325
|
addLight(o) {
|
|
252
326
|
const camera = this.cameras[this.mainCameraParams.type];
|
|
253
|
-
let newLight = new SpotLight(camera, this.inputHandler);
|
|
254
|
-
newLight.prepareBuffer(this.device);
|
|
327
|
+
let newLight = new SpotLight(camera, this.inputHandler, this.device);
|
|
255
328
|
this.lightContainer.push(newLight);
|
|
329
|
+
this.createTexArrayForShadows();
|
|
256
330
|
console.log(`%cAdd light: ${newLight}`, LOG_FUNNY_SMALL);
|
|
257
331
|
}
|
|
258
332
|
|
|
@@ -300,14 +374,13 @@ export default class MatrixEngineWGPU {
|
|
|
300
374
|
// scale for all second option!
|
|
301
375
|
o.objAnim.scaleAll = function(s) {
|
|
302
376
|
for(var k in this.meshList) {
|
|
303
|
-
console.log('SCALE');
|
|
377
|
+
console.log('SCALE meshList');
|
|
304
378
|
this.meshList[k].setScale(s);
|
|
305
379
|
}
|
|
306
380
|
}
|
|
307
381
|
}
|
|
308
|
-
let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o);
|
|
382
|
+
let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o, this.inputHandler);
|
|
309
383
|
myMesh1.spotlightUniformBuffer = this.spotlightUniformBuffer;
|
|
310
|
-
myMesh1.inputHandler = this.inputHandler;
|
|
311
384
|
myMesh1.clearColor = clearColor;
|
|
312
385
|
if(o.physics.enabled == true) {
|
|
313
386
|
this.matrixAmmo.addPhysics(myMesh1, o.physics)
|
|
@@ -325,38 +398,19 @@ export default class MatrixEngineWGPU {
|
|
|
325
398
|
this.canvas.remove();
|
|
326
399
|
}
|
|
327
400
|
|
|
328
|
-
test = () => {
|
|
329
|
-
const now = Date.now();
|
|
330
|
-
// First frame safety
|
|
331
|
-
let dt = (now - this.lastFrameMS) / this.mainCameraParams.responseCoef;
|
|
332
|
-
if(!this.lastFrameMS) {dt = 16;}
|
|
333
|
-
this.lastFrameMS = now;
|
|
334
|
-
const camera = this.cameras[this.mainCameraParams.type];
|
|
335
|
-
camera.update(dt, this.inputHandler());
|
|
336
|
-
const camVP = mat4.multiply(camera.projectionMatrix, camera.view); // P * V
|
|
337
|
-
|
|
338
|
-
for(const mesh of this.mainRenderBundle) {
|
|
339
|
-
// scene buffer layout = 0..63 lightVP, 64..127 camVP, 128..143 lightPos(+pad)
|
|
340
|
-
this.device.queue.writeBuffer(
|
|
341
|
-
mesh.sceneUniformBuffer,
|
|
342
|
-
64, // cameraViewProjMatrix offset
|
|
343
|
-
camVP.buffer,
|
|
344
|
-
camVP.byteOffset,
|
|
345
|
-
camVP.byteLength
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
401
|
updateLights() {
|
|
351
|
-
//
|
|
352
|
-
const data = new Float32Array(this.MAX_SPOTLIGHTS *
|
|
402
|
+
const floatsPerLight = 36; // not 20 anymore
|
|
403
|
+
const data = new Float32Array(this.MAX_SPOTLIGHTS * floatsPerLight);
|
|
404
|
+
|
|
353
405
|
for(let i = 0;i < this.MAX_SPOTLIGHTS;i++) {
|
|
354
406
|
if(i < this.lightContainer.length) {
|
|
355
|
-
|
|
407
|
+
const buf = this.lightContainer[i].getLightDataBuffer();
|
|
408
|
+
data.set(buf, i * floatsPerLight);
|
|
356
409
|
} else {
|
|
357
|
-
data.set(new Float32Array(
|
|
410
|
+
data.set(new Float32Array(floatsPerLight), i * floatsPerLight);
|
|
358
411
|
}
|
|
359
412
|
}
|
|
413
|
+
|
|
360
414
|
this.device.queue.writeBuffer(this.spotlightUniformBuffer, 0, data.buffer);
|
|
361
415
|
}
|
|
362
416
|
|
|
@@ -365,60 +419,110 @@ export default class MatrixEngineWGPU {
|
|
|
365
419
|
setTimeout(() => {requestAnimationFrame(this.frame)}, 200);
|
|
366
420
|
return;
|
|
367
421
|
}
|
|
422
|
+
|
|
423
|
+
let noPass = false;
|
|
424
|
+
|
|
425
|
+
this.mainRenderBundle.forEach((meItem, index) => {
|
|
426
|
+
if(meItem.isVideo == true) {
|
|
427
|
+
if(!meItem.externalTexture || meItem.video.readyState < 2) {
|
|
428
|
+
console.log('no rendere for video not ready')
|
|
429
|
+
// this.externalTexture = this.device.importExternalTexture({source: this.video});
|
|
430
|
+
noPass = true;
|
|
431
|
+
setTimeout(() => requestAnimationFrame(this.frame), 1500)
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
})
|
|
436
|
+
|
|
437
|
+
if(noPass == true) {
|
|
438
|
+
console.log('no rendere for video not ready !!!!')
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// let pass;
|
|
443
|
+
// let commandEncoder;
|
|
368
444
|
try {
|
|
369
|
-
let shadowPass = null;
|
|
370
|
-
let renderPass;
|
|
371
445
|
let commandEncoder = this.device.createCommandEncoder();
|
|
372
|
-
|
|
373
446
|
this.updateLights()
|
|
374
|
-
this.test()
|
|
375
|
-
|
|
376
447
|
// 1️⃣ Update light data (position, direction, uniforms)
|
|
377
448
|
for(const light of this.lightContainer) {
|
|
449
|
+
light.update()
|
|
450
|
+
// light.updateSceneUniforms(this.mainRenderBundle, this.cameras.WASD);
|
|
378
451
|
this.mainRenderBundle.forEach((meItem, index) => {
|
|
379
|
-
|
|
380
|
-
|
|
452
|
+
meItem.position.update()
|
|
453
|
+
meItem.updateModelUniformBuffer()
|
|
454
|
+
// if(meItem.isVideo != true) {
|
|
455
|
+
meItem.getTransformationMatrix(this.mainRenderBundle, light)
|
|
456
|
+
// }
|
|
381
457
|
})
|
|
382
458
|
}
|
|
383
|
-
|
|
384
|
-
this.mainRenderBundle.forEach((meItem, index) => {meItem.position.update()})
|
|
385
459
|
if(this.matrixAmmo) this.matrixAmmo.updatePhysics();
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
460
|
+
|
|
461
|
+
for(let i = 0;i < this.lightContainer.length;i++) {
|
|
462
|
+
const light = this.lightContainer[i];
|
|
463
|
+
|
|
464
|
+
let ViewPerLightRenderShadowPass = this.shadowTextureArray.createView({
|
|
465
|
+
dimension: '2d',
|
|
466
|
+
baseArrayLayer: i,
|
|
467
|
+
arrayLayerCount: 1, // must be > 0
|
|
468
|
+
baseMipLevel: 0,
|
|
469
|
+
mipLevelCount: 1,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const shadowPass = commandEncoder.beginRenderPass({
|
|
473
|
+
label: "shadowPass",
|
|
474
|
+
colorAttachments: [],
|
|
475
|
+
depthStencilAttachment: {
|
|
476
|
+
view: ViewPerLightRenderShadowPass,
|
|
477
|
+
depthLoadOp: 'clear',
|
|
478
|
+
depthStoreOp: 'store',
|
|
479
|
+
depthClearValue: 1.0,
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
shadowPass.setPipeline(light.shadowPipeline);
|
|
484
|
+
for(const [meshIndex, mesh] of this.mainRenderBundle.entries()) {
|
|
485
|
+
if(mesh.videoIsReady == 'NONE') {
|
|
486
|
+
shadowPass.setBindGroup(0, light.getShadowBindGroup(mesh, meshIndex));
|
|
487
|
+
shadowPass.setBindGroup(1, mesh.modelBindGroup);
|
|
488
|
+
mesh.drawShadows(shadowPass, light);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
shadowPass.end();
|
|
391
492
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
this.
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
493
|
+
const currentTextureView = this.context.getCurrentTexture().createView();
|
|
494
|
+
this.mainRenderPassDesc.colorAttachments[0].view = currentTextureView;
|
|
495
|
+
let pass = commandEncoder.beginRenderPass(this.mainRenderPassDesc);
|
|
496
|
+
// Loop over each mesh
|
|
497
|
+
for(const mesh of this.mainRenderBundle) {
|
|
498
|
+
pass.setPipeline(mesh.pipeline);
|
|
499
|
+
if(!mesh.sceneBindGroupForRender || (mesh.FINISH_VIDIO_INIT == false && mesh.isVideo == true)) {
|
|
500
|
+
for(const m of this.mainRenderBundle) {
|
|
501
|
+
if(m.isVideo == true) {
|
|
502
|
+
console.log('✅shadowVideoView', this.shadowVideoView)
|
|
503
|
+
m.shadowDepthTextureView = this.shadowVideoView;
|
|
504
|
+
m.FINISH_VIDIO_INIT = true;
|
|
505
|
+
m.setupPipeline();
|
|
506
|
+
} else {
|
|
507
|
+
m.shadowDepthTextureView = this.shadowArrayView;
|
|
508
|
+
m.setupPipeline();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
403
511
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
meItem.drawElements(renderPass);
|
|
408
|
-
})
|
|
409
|
-
if(renderPass) renderPass.end();
|
|
410
|
-
|
|
512
|
+
mesh.drawElements(pass, this.lightContainer);
|
|
513
|
+
}
|
|
514
|
+
pass.end();
|
|
411
515
|
this.device.queue.submit([commandEncoder.finish()]);
|
|
412
516
|
requestAnimationFrame(this.frame);
|
|
413
517
|
} catch(err) {
|
|
414
|
-
console.log('%cLoop
|
|
518
|
+
console.log('%cLoop(err):' + err, LOG_WARN)
|
|
415
519
|
requestAnimationFrame(this.frame);
|
|
416
520
|
}
|
|
417
521
|
}
|
|
418
522
|
|
|
419
523
|
framePassPerObject = () => {
|
|
420
524
|
let commandEncoder = this.device.createCommandEncoder();
|
|
421
|
-
this.matrixAmmo.updatePhysics();
|
|
525
|
+
if(this.matrixAmmo.rigidBodies.length > 0) this.matrixAmmo.updatePhysics();
|
|
422
526
|
this.mainRenderBundle.forEach((meItem, index) => {
|
|
423
527
|
if(index === 0) {
|
|
424
528
|
if(meItem.renderPassDescriptor) meItem.renderPassDescriptor.colorAttachments[0].loadOp = 'clear';
|