matrix-engine-wgpu 1.2.14 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/readme.md +72 -0
- package/src/engine/ball.js +1 -1
- package/src/engine/cube.js +1 -1
- package/src/engine/loader-obj.js +3 -2
- package/src/engine/mesh-obj.js +147 -28
- package/src/engine/mesh.js +0 -1
- package/src/engine/raycast.js +64 -73
- package/src/engine/utils.js +2 -0
- package/src/physics/matrix-ammo.js +1 -14
- package/src/shaders/fragment.wgsl.js +2 -3
- package/src/shaders/vertex.wgsl.js +1 -2
- package/src/shaders/vertexShadow.wgsl.js +1 -2
- package/src/world.js +43 -18
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matrix-engine-wgpu",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "+HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "obj sequence anim +HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
7
7
|
"./src"
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"keywords": [
|
|
19
19
|
"3dScene",
|
|
20
20
|
"webGPU",
|
|
21
|
+
"webgpu-obj-sequence",
|
|
22
|
+
"obj-sequence-animation",
|
|
21
23
|
"webGPU-hit-detect",
|
|
22
24
|
"webGPU-raycaster",
|
|
23
25
|
"webGPU-click-on-object",
|
package/readme.md
CHANGED
|
@@ -234,6 +234,77 @@ window.app = application;
|
|
|
234
234
|
```
|
|
235
235
|
|
|
236
236
|
|
|
237
|
+
## 🔁 Load OBJ Sequence Animation
|
|
238
|
+
|
|
239
|
+
This example shows how to load and animate a sequence of .obj files to simulate mesh-based animation (e.g. walking character).
|
|
240
|
+
|
|
241
|
+
js
|
|
242
|
+
Copy
|
|
243
|
+
Edit
|
|
244
|
+
import MatrixEngineWGPU from "../src/world.js";
|
|
245
|
+
import { downloadMeshes, makeObjSeqArg } from "../src/engine/loader-obj.js";
|
|
246
|
+
import { LOG_MATRIX } from "../src/engine/utils.js";
|
|
247
|
+
|
|
248
|
+
export var loadObjsSequence = function () {
|
|
249
|
+
let loadObjFile = new MatrixEngineWGPU({
|
|
250
|
+
useSingleRenderPass: true,
|
|
251
|
+
canvasSize: "fullscreen",
|
|
252
|
+
mainCameraParams: {
|
|
253
|
+
type: "WASD",
|
|
254
|
+
responseCoef: 1000,
|
|
255
|
+
},
|
|
256
|
+
}, () => {
|
|
257
|
+
|
|
258
|
+
addEventListener("AmmoReady", () => {
|
|
259
|
+
downloadMeshes(
|
|
260
|
+
makeObjSeqArg({
|
|
261
|
+
id: "swat-walk-pistol",
|
|
262
|
+
path: "res/meshes/objs-sequence/swat-walk-pistol",
|
|
263
|
+
from: 1,
|
|
264
|
+
to: 20,
|
|
265
|
+
}),
|
|
266
|
+
onLoadObj,
|
|
267
|
+
{ scale: [10, 10, 10] }
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
function onLoadObj(m) {
|
|
272
|
+
console.log(`%c Loaded objs: ${m} `, LOG_MATRIX);
|
|
273
|
+
var objAnim = {
|
|
274
|
+
id: "swat-walk-pistol",
|
|
275
|
+
meshList: m,
|
|
276
|
+
currentAni: 1,
|
|
277
|
+
animations: {
|
|
278
|
+
active: "walk",
|
|
279
|
+
walk: { from: 1, to: 20, speed: 3 },
|
|
280
|
+
walkPistol: { from: 36, to: 60, speed: 3 },
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
loadObjFile.addMeshObj({
|
|
285
|
+
position: { x: 0, y: 2, z: -10 },
|
|
286
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
287
|
+
rotationSpeed: { x: 0, y: 0, z: 0 },
|
|
288
|
+
scale: [100, 100, 100],
|
|
289
|
+
texturesPaths: ["./res/meshes/blender/cube.png"],
|
|
290
|
+
name: "swat",
|
|
291
|
+
mesh: m["swat-walk-pistol"],
|
|
292
|
+
physics: {
|
|
293
|
+
enabled: false,
|
|
294
|
+
geometry: "Cube",
|
|
295
|
+
},
|
|
296
|
+
objAnim: objAnim,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
app.mainRenderBundle[0].objAnim.play("walk");
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
window.app = loadObjFile;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
### 📽️ Preview
|
|
307
|
+
|
|
237
308
|
## @Note
|
|
238
309
|
If this happen less then 15 times (Loading procces) then it is ok probably...
|
|
239
310
|
```warn
|
|
@@ -278,6 +349,7 @@ Uses `watchify` to bundle JavaScript.
|
|
|
278
349
|
## Resources
|
|
279
350
|
|
|
280
351
|
All resources and output go into the `./public` folder — everything you need in one place.
|
|
352
|
+
This is static file storage.
|
|
281
353
|
|
|
282
354
|
---
|
|
283
355
|
|
package/src/engine/ball.js
CHANGED
package/src/engine/cube.js
CHANGED
package/src/engine/loader-obj.js
CHANGED
|
@@ -467,6 +467,7 @@ export const makeObjSeqArg = (arg) => {
|
|
|
467
467
|
* Switching obj seq animations frames range.
|
|
468
468
|
*/
|
|
469
469
|
export function play (nameAni) {
|
|
470
|
-
this.
|
|
471
|
-
this.
|
|
470
|
+
this.animations.active = nameAni;
|
|
471
|
+
this.currentAni = this.animations[this.animations.active].from;
|
|
472
|
+
this.playing = true;
|
|
472
473
|
}
|
package/src/engine/mesh-obj.js
CHANGED
|
@@ -30,6 +30,14 @@ export default class MEMeshObj {
|
|
|
30
30
|
this.mesh.uvs = this.mesh.textures;
|
|
31
31
|
console.log(`%c Mesh loaded: ${o.name}`, LOG_FUNNY_SMALL);
|
|
32
32
|
|
|
33
|
+
// TEST OBJ SEQ ANIM
|
|
34
|
+
//
|
|
35
|
+
if(typeof o.objAnim !== 'undefined' && o.objAnim != null) {
|
|
36
|
+
this.objAnim = o.objAnim;
|
|
37
|
+
console.log(`%c Mesh objAnim exist: ${o.objAnim}`, LOG_FUNNY_SMALL);
|
|
38
|
+
this.drawElements = this.drawElementsAnim;
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
this.inputHandler = createInputHandler(window, canvas);
|
|
34
42
|
this.cameras = o.cameras;
|
|
35
43
|
|
|
@@ -61,7 +69,7 @@ export default class MEMeshObj {
|
|
|
61
69
|
this.modelViewProjectionMatrix = mat4.create();
|
|
62
70
|
// console.log('cube added texturesPaths: ', this.texturesPaths)
|
|
63
71
|
this.loadTex0(this.texturesPaths, device).then(() => {
|
|
64
|
-
|
|
72
|
+
console.log('loaded tex buffer for mesh:', this.texture0)
|
|
65
73
|
resolve()
|
|
66
74
|
})
|
|
67
75
|
})
|
|
@@ -117,19 +125,18 @@ export default class MEMeshObj {
|
|
|
117
125
|
|
|
118
126
|
// Create the model index buffer.
|
|
119
127
|
this.indexCount = this.mesh.indices.length;
|
|
128
|
+
const indexCount = this.mesh.indices.length;
|
|
129
|
+
const size = Math.ceil(indexCount * Uint16Array.BYTES_PER_ELEMENT / 4) * 4;
|
|
130
|
+
|
|
120
131
|
this.indexBuffer = this.device.createBuffer({
|
|
121
|
-
size
|
|
122
|
-
usage: GPUBufferUsage.INDEX,
|
|
123
|
-
mappedAtCreation: true
|
|
132
|
+
size,
|
|
133
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
134
|
+
mappedAtCreation: true
|
|
124
135
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// }
|
|
130
|
-
new Uint16Array(this.indexBuffer.getMappedRange()).set(this.mesh.indices);
|
|
131
|
-
this.indexBuffer.unmap();
|
|
132
|
-
}
|
|
136
|
+
|
|
137
|
+
new Uint16Array(this.indexBuffer.getMappedRange()).set(this.mesh.indices);
|
|
138
|
+
this.indexBuffer.unmap();
|
|
139
|
+
this.indexCount = indexCount;
|
|
133
140
|
|
|
134
141
|
// Create the depth texture for rendering/sampling the shadow map.
|
|
135
142
|
this.shadowDepthTexture = this.device.createTexture({
|
|
@@ -179,7 +186,8 @@ export default class MEMeshObj {
|
|
|
179
186
|
|
|
180
187
|
const primitive = {
|
|
181
188
|
topology: 'triangle-list',
|
|
182
|
-
cullMode: 'back',
|
|
189
|
+
// cullMode: 'back', // ORI
|
|
190
|
+
cullMode: 'none', // ORI
|
|
183
191
|
};
|
|
184
192
|
|
|
185
193
|
this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
|
|
@@ -290,7 +298,7 @@ export default class MEMeshObj {
|
|
|
290
298
|
});
|
|
291
299
|
|
|
292
300
|
const depthTexture = this.device.createTexture({
|
|
293
|
-
size: [canvas.width, canvas.height
|
|
301
|
+
size: [canvas.width, canvas.height],
|
|
294
302
|
format: 'depth24plus-stencil8',
|
|
295
303
|
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
296
304
|
});
|
|
@@ -301,7 +309,7 @@ export default class MEMeshObj {
|
|
|
301
309
|
// view is acquired and set in render loop.
|
|
302
310
|
view: undefined,
|
|
303
311
|
clearValue: {r: 0.5, g: 0.5, b: 0.5, a: 1.0},
|
|
304
|
-
loadOp: 'clear',
|
|
312
|
+
loadOp: 'clear', // load old fix for FF
|
|
305
313
|
storeOp: 'store',
|
|
306
314
|
},
|
|
307
315
|
],
|
|
@@ -326,8 +334,7 @@ export default class MEMeshObj {
|
|
|
326
334
|
// one for the camera and one for the light.
|
|
327
335
|
// Then a vec3 for the light position.
|
|
328
336
|
// Rounded to the nearest multiple of 16.
|
|
329
|
-
|
|
330
|
-
size: 160,
|
|
337
|
+
size: 2 * 4 * 16 + 4 * 4,
|
|
331
338
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
332
339
|
});
|
|
333
340
|
|
|
@@ -393,16 +400,25 @@ export default class MEMeshObj {
|
|
|
393
400
|
// const this.viewMatrix = mat4.identity()
|
|
394
401
|
const camera = this.cameras[this.mainCameraParams.type];
|
|
395
402
|
this.viewMatrix = camera.update(deltaTime, this.inputHandler());
|
|
403
|
+
const scaleVec = [1, 1, 1]; // your desired scale OPTION 1
|
|
404
|
+
const scaleMatrix = mat4.scaling(scaleVec);
|
|
405
|
+
// Apply scaling
|
|
406
|
+
mat4.multiply(scaleMatrix, this.viewMatrix, this.viewMatrix);
|
|
407
|
+
|
|
396
408
|
mat4.translate(this.viewMatrix, vec3.fromValues(pos.x, pos.y, pos.z), this.viewMatrix);
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
409
|
+
|
|
410
|
+
if(this.itIsPhysicsBody == true) {
|
|
411
|
+
mat4.rotate(
|
|
412
|
+
this.viewMatrix,
|
|
413
|
+
vec3.fromValues(this.rotation.axis.x, this.rotation.axis.y, this.rotation.axis.z),
|
|
414
|
+
degToRad(this.rotation.angle), this.viewMatrix)
|
|
415
|
+
// console.info('angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
|
|
416
|
+
} else {
|
|
417
|
+
mat4.rotateX(this.viewMatrix, Math.PI * this.rotation.getRotX(), this.viewMatrix);
|
|
418
|
+
mat4.rotateY(this.viewMatrix, Math.PI * this.rotation.getRotY(), this.viewMatrix);
|
|
419
|
+
mat4.rotateZ(this.viewMatrix, Math.PI * this.rotation.getRotZ(), this.viewMatrix);
|
|
420
|
+
// console.info('NOT PHYSICS angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
|
|
421
|
+
}
|
|
406
422
|
mat4.multiply(this.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
|
|
407
423
|
return this.modelViewProjectionMatrix;
|
|
408
424
|
}
|
|
@@ -475,6 +491,11 @@ export default class MEMeshObj {
|
|
|
475
491
|
};
|
|
476
492
|
|
|
477
493
|
this.done = true;
|
|
494
|
+
}).then(() => {
|
|
495
|
+
if(typeof this.objAnim !== 'undefined' && this.objAnim !== null) {
|
|
496
|
+
console.log('after all load configutr mesh list buffers')
|
|
497
|
+
this.updateMeshListBuffers()
|
|
498
|
+
}
|
|
478
499
|
})
|
|
479
500
|
}
|
|
480
501
|
|
|
@@ -553,9 +574,17 @@ export default class MEMeshObj {
|
|
|
553
574
|
|
|
554
575
|
return new Promise(async (resolve) => {
|
|
555
576
|
const response = await fetch(texturesPaths[0]);
|
|
577
|
+
|
|
578
|
+
// const blob = await response.blob();
|
|
579
|
+
// if(!blob.type.startsWith('image/')) {
|
|
580
|
+
// console.error("Unexpected texture response type:", blob.type);
|
|
581
|
+
// return;
|
|
582
|
+
// }
|
|
583
|
+
|
|
584
|
+
// const imageBitmap = await createImageBitmap(blob);
|
|
556
585
|
const imageBitmap = await createImageBitmap(await response.blob());
|
|
557
586
|
this.texture0 = device.createTexture({
|
|
558
|
-
size: [imageBitmap.width, imageBitmap.height, 1],
|
|
587
|
+
size: [imageBitmap.width, imageBitmap.height, 1], // REMOVED 1
|
|
559
588
|
format: 'rgba8unorm',
|
|
560
589
|
usage:
|
|
561
590
|
GPUTextureUsage.TEXTURE_BINDING |
|
|
@@ -574,7 +603,6 @@ export default class MEMeshObj {
|
|
|
574
603
|
|
|
575
604
|
draw = (commandEncoder) => {
|
|
576
605
|
if(this.done == false) return;
|
|
577
|
-
// console.log('test draw for meshObj !')
|
|
578
606
|
const transformationMatrix = this.getTransformationMatrix(this.position);
|
|
579
607
|
this.device.queue.writeBuffer(
|
|
580
608
|
this.sceneUniformBuffer,
|
|
@@ -598,6 +626,97 @@ export default class MEMeshObj {
|
|
|
598
626
|
renderPass.drawIndexed(this.indexCount);
|
|
599
627
|
}
|
|
600
628
|
|
|
629
|
+
// test
|
|
630
|
+
createGPUBuffer(dataArray, usage) {
|
|
631
|
+
if(!dataArray || typeof dataArray.length !== 'number') {
|
|
632
|
+
throw new Error('Invalid data array passed to createGPUBuffer');
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const size = dataArray.length * dataArray.BYTES_PER_ELEMENT;
|
|
636
|
+
if(!Number.isFinite(size) || size <= 0) {
|
|
637
|
+
throw new Error(`Invalid buffer size: ${size}`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const buffer = this.device.createBuffer({
|
|
641
|
+
size,
|
|
642
|
+
usage,
|
|
643
|
+
mappedAtCreation: true,
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const writeArray = dataArray.constructor === Float32Array
|
|
647
|
+
? new Float32Array(buffer.getMappedRange())
|
|
648
|
+
: new Uint16Array(buffer.getMappedRange());
|
|
649
|
+
|
|
650
|
+
writeArray.set(dataArray);
|
|
651
|
+
buffer.unmap();
|
|
652
|
+
|
|
653
|
+
return buffer;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
updateMeshListBuffers() {
|
|
658
|
+
for(const key in this.objAnim.meshList) {
|
|
659
|
+
const mesh = this.objAnim.meshList[key];
|
|
660
|
+
|
|
661
|
+
mesh.vertexBuffer = this.device.createBuffer({
|
|
662
|
+
size: mesh.vertices.length * Float32Array.BYTES_PER_ELEMENT,
|
|
663
|
+
usage: GPUBufferUsage.VERTEX,
|
|
664
|
+
mappedAtCreation: true,
|
|
665
|
+
});
|
|
666
|
+
new Float32Array(mesh.vertexBuffer.getMappedRange()).set(mesh.vertices);
|
|
667
|
+
mesh.vertexBuffer.unmap();
|
|
668
|
+
|
|
669
|
+
// Normals
|
|
670
|
+
mesh.vertexNormalsBuffer = this.device.createBuffer({
|
|
671
|
+
size: mesh.vertexNormals.length * Float32Array.BYTES_PER_ELEMENT,
|
|
672
|
+
usage: GPUBufferUsage.VERTEX,
|
|
673
|
+
mappedAtCreation: true,
|
|
674
|
+
});
|
|
675
|
+
new Float32Array(mesh.vertexNormalsBuffer.getMappedRange()).set(mesh.vertexNormals);
|
|
676
|
+
mesh.vertexNormalsBuffer.unmap();
|
|
677
|
+
|
|
678
|
+
// UVs
|
|
679
|
+
mesh.vertexTexCoordsBuffer = this.device.createBuffer({
|
|
680
|
+
size: mesh.textures.length * Float32Array.BYTES_PER_ELEMENT,
|
|
681
|
+
usage: GPUBufferUsage.VERTEX,
|
|
682
|
+
mappedAtCreation: true,
|
|
683
|
+
});
|
|
684
|
+
new Float32Array(mesh.vertexTexCoordsBuffer.getMappedRange()).set(mesh.textures);
|
|
685
|
+
mesh.vertexTexCoordsBuffer.unmap();
|
|
686
|
+
|
|
687
|
+
// Indices
|
|
688
|
+
const indexCount = mesh.indices.length;
|
|
689
|
+
const indexSize = Math.ceil(indexCount * Uint16Array.BYTES_PER_ELEMENT / 4) * 4;
|
|
690
|
+
mesh.indexBuffer = this.device.createBuffer({
|
|
691
|
+
size: indexSize,
|
|
692
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
693
|
+
mappedAtCreation: true,
|
|
694
|
+
});
|
|
695
|
+
new Uint16Array(mesh.indexBuffer.getMappedRange()).set(mesh.indices);
|
|
696
|
+
mesh.indexBuffer.unmap();
|
|
697
|
+
mesh.indexCount = indexCount;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
drawElementsAnim = (renderPass) => {
|
|
702
|
+
console.log('render is for anim')
|
|
703
|
+
renderPass.setBindGroup(0, this.sceneBindGroupForRender);
|
|
704
|
+
renderPass.setBindGroup(1, this.modelBindGroup);
|
|
705
|
+
const mesh = this.objAnim.meshList[this.objAnim.id + this.objAnim.currentAni];
|
|
706
|
+
|
|
707
|
+
renderPass.setVertexBuffer(0, mesh.vertexBuffer);
|
|
708
|
+
renderPass.setVertexBuffer(1, mesh.vertexNormalsBuffer);
|
|
709
|
+
renderPass.setVertexBuffer(2, mesh.vertexTexCoordsBuffer);
|
|
710
|
+
renderPass.setIndexBuffer(mesh.indexBuffer, 'uint16');
|
|
711
|
+
renderPass.drawIndexed(mesh.indexCount);
|
|
712
|
+
if(this.objAnim.playing == true) {
|
|
713
|
+
this.objAnim.currentAni++;
|
|
714
|
+
if(this.objAnim.currentAni >= this.objAnim.animations[this.objAnim.animations.active].to) {
|
|
715
|
+
this.objAnim.currentAni = this.objAnim.animations[this.objAnim.animations.active].from;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
601
720
|
drawShadows = (shadowPass) => {
|
|
602
721
|
shadowPass.setBindGroup(0, this.sceneBindGroupForShadow);
|
|
603
722
|
shadowPass.setBindGroup(1, this.modelBindGroup);
|
package/src/engine/mesh.js
CHANGED
package/src/engine/raycast.js
CHANGED
|
@@ -11,91 +11,82 @@
|
|
|
11
11
|
* app is global - will be fixed in future
|
|
12
12
|
*/
|
|
13
13
|
import {mat4, vec3, vec4} from "wgpu-matrix";
|
|
14
|
-
|
|
15
14
|
let rayHitEvent;
|
|
16
15
|
|
|
17
16
|
export let touchCoordinate = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
enabled: false,
|
|
18
|
+
x: 0,
|
|
19
|
+
y: 0,
|
|
20
|
+
stopOnFirstDetectedHit: false
|
|
22
21
|
};
|
|
23
22
|
|
|
24
23
|
function multiplyMatrixVector(matrix, vector) {
|
|
25
|
-
|
|
24
|
+
return vec4.transformMat4(vector, matrix);
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
export function getRayFromMouse(event, canvas, camera) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
worldPos[1],
|
|
65
|
-
worldPos[2]
|
|
66
|
-
];
|
|
67
|
-
}
|
|
68
|
-
const rayOrigin = [camera.position[0], camera.position[1], camera.position[2]];
|
|
69
|
-
const rayDirection = vec3.normalize(vec3.subtract(world, rayOrigin));
|
|
70
|
-
|
|
71
|
-
rayDirection[2] = -rayDirection[2];
|
|
72
|
-
|
|
73
|
-
return {rayOrigin, rayDirection};
|
|
28
|
+
const rect = canvas.getBoundingClientRect();
|
|
29
|
+
let x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
30
|
+
let y = ((event.clientY - rect.top) / rect.height) * 2 - 1;
|
|
31
|
+
// simple invert
|
|
32
|
+
x = -x;
|
|
33
|
+
y = -y;
|
|
34
|
+
const fov = Math.PI / 4;
|
|
35
|
+
const aspect = canvas.width / canvas.height;
|
|
36
|
+
const near = 0.1;
|
|
37
|
+
const far = 1000;
|
|
38
|
+
camera.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 1000.0);
|
|
39
|
+
const invProjection = mat4.inverse(camera.projectionMatrix);
|
|
40
|
+
const invView = mat4.inverse(camera.view);
|
|
41
|
+
const ndc = [x, y, 1, 1];
|
|
42
|
+
let worldPos = multiplyMatrixVector(invProjection, ndc);
|
|
43
|
+
worldPos = multiplyMatrixVector(invView, worldPos);
|
|
44
|
+
let world;
|
|
45
|
+
if(worldPos[3] !== 0) {
|
|
46
|
+
world = [
|
|
47
|
+
worldPos[0] / worldPos[3],
|
|
48
|
+
worldPos[2] / worldPos[3],
|
|
49
|
+
worldPos[1] / worldPos[3]
|
|
50
|
+
];
|
|
51
|
+
} else {
|
|
52
|
+
console.log("[raycaster]special case 0.");
|
|
53
|
+
world = [
|
|
54
|
+
worldPos[0],
|
|
55
|
+
worldPos[1],
|
|
56
|
+
worldPos[2]
|
|
57
|
+
];
|
|
58
|
+
}
|
|
59
|
+
const rayOrigin = [camera.position[0], camera.position[1], camera.position[2]];
|
|
60
|
+
const rayDirection = vec3.normalize(vec3.subtract(world, rayOrigin));
|
|
61
|
+
rayDirection[2] = -rayDirection[2];
|
|
62
|
+
return {rayOrigin, rayDirection};
|
|
74
63
|
}
|
|
75
64
|
|
|
76
65
|
export function rayIntersectsSphere(rayOrigin, rayDirection, sphereCenter, sphereRadius) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
66
|
+
const pos = [sphereCenter.x, sphereCenter.y, sphereCenter.z];
|
|
67
|
+
const oc = vec3.subtract(rayOrigin, pos);
|
|
68
|
+
const a = vec3.dot(rayDirection, rayDirection);
|
|
69
|
+
const b = 2.0 * vec3.dot(oc, rayDirection);
|
|
70
|
+
const c = vec3.dot(oc, oc) - sphereRadius * sphereRadius;
|
|
71
|
+
const discriminant = b * b - 4 * a * c;
|
|
72
|
+
return discriminant > 0;
|
|
84
73
|
}
|
|
85
74
|
|
|
86
|
-
export function addRaycastListener
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
75
|
+
export function addRaycastListener() {
|
|
76
|
+
let canvasDom = document.getElementById("canvas1");
|
|
77
|
+
canvasDom.addEventListener('click', (event) => {
|
|
78
|
+
let camera = app.cameras.WASD;
|
|
79
|
+
const {rayOrigin, rayDirection} = getRayFromMouse(event, canvasDom, camera);
|
|
80
|
+
for(const object of app.mainRenderBundle) {
|
|
81
|
+
if(rayIntersectsSphere(rayOrigin, rayDirection, object.position, object.raycast.radius)) {
|
|
82
|
+
console.log('Object clicked:', object.name);
|
|
83
|
+
// Just like in matrix-engine webGL version "ray.hit.event"
|
|
84
|
+
dispatchEvent(new CustomEvent('ray.hit.event', {
|
|
85
|
+
detail: {
|
|
86
|
+
hitObject: object
|
|
87
|
+
}
|
|
88
|
+
}))
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
101
92
|
}
|
package/src/engine/utils.js
CHANGED
|
@@ -708,12 +708,14 @@ export let mb = {
|
|
|
708
708
|
mb.c++;
|
|
709
709
|
},
|
|
710
710
|
error: function(content) {
|
|
711
|
+
if (mb.root()== null) return;
|
|
711
712
|
mb.root().classList.remove("success")
|
|
712
713
|
mb.root().classList.add("error")
|
|
713
714
|
mb.root().classList.add("fadeInDown");
|
|
714
715
|
mb.show(content, 'err');
|
|
715
716
|
},
|
|
716
717
|
success: function(content) {
|
|
718
|
+
if (mb.root()== null) return;
|
|
717
719
|
mb.root().classList.remove("error")
|
|
718
720
|
mb.root().classList.add("success")
|
|
719
721
|
mb.root().classList.add("fadeInDown");
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// import {vec3} from "wgpu-matrix";
|
|
2
1
|
import {LOG_FUNNY, degToRad, quaternion_rotation_matrix, radToDeg, scriptManager} from "../engine/utils";
|
|
3
2
|
|
|
4
3
|
export default class MatrixAmmo {
|
|
@@ -17,7 +16,6 @@ export default class MatrixAmmo {
|
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
init = () => {
|
|
20
|
-
// console.log('pre ammo')
|
|
21
19
|
Ammo().then(Ammo => {
|
|
22
20
|
// Physics variables
|
|
23
21
|
this.dynamicsWorld = null;
|
|
@@ -58,8 +56,6 @@ export default class MatrixAmmo {
|
|
|
58
56
|
body.name = 'ground';
|
|
59
57
|
this.ground = body;
|
|
60
58
|
this.dynamicsWorld.addRigidBody(body);
|
|
61
|
-
// this.rigidBodies.push(body);
|
|
62
|
-
// add collide event
|
|
63
59
|
this.detectCollision()
|
|
64
60
|
}
|
|
65
61
|
|
|
@@ -79,15 +75,12 @@ export default class MatrixAmmo {
|
|
|
79
75
|
var mass = 1;
|
|
80
76
|
var localInertia = new Ammo.btVector3(0, 0, 0);
|
|
81
77
|
colShape.calculateLocalInertia(mass, localInertia);
|
|
82
|
-
|
|
83
78
|
startTransform.setOrigin(new Ammo.btVector3(pOptions.position.x, pOptions.position.y, pOptions.position.z));
|
|
84
79
|
|
|
85
80
|
var myMotionState = new Ammo.btDefaultMotionState(startTransform),
|
|
86
81
|
rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia),
|
|
87
82
|
body = new Ammo.btRigidBody(rbInfo);
|
|
88
83
|
|
|
89
|
-
console.log("TEST ADDING PHYSICS ");
|
|
90
|
-
|
|
91
84
|
body.MEObject = MEObject;
|
|
92
85
|
this.dynamicsWorld.addRigidBody(body);
|
|
93
86
|
this.rigidBodies.push(body);
|
|
@@ -95,7 +88,6 @@ export default class MatrixAmmo {
|
|
|
95
88
|
}
|
|
96
89
|
|
|
97
90
|
addPhysicsBox(MEObject, pOptions) {
|
|
98
|
-
|
|
99
91
|
const FLAGS = {
|
|
100
92
|
TEST_NIDZA: 3,
|
|
101
93
|
CF_KINEMATIC_OBJECT: 2
|
|
@@ -110,7 +102,6 @@ export default class MatrixAmmo {
|
|
|
110
102
|
var localInertia = new Ammo.btVector3(0, 0, 0);
|
|
111
103
|
colShape.calculateLocalInertia(mass, localInertia);
|
|
112
104
|
startTransform.setOrigin(new Ammo.btVector3(pOptions.position.x, pOptions.position.y, pOptions.position.z));
|
|
113
|
-
//rotation
|
|
114
105
|
// console.log('startTransform.setRotation', startTransform.setRotation)
|
|
115
106
|
var t = startTransform.getRotation()
|
|
116
107
|
t.setX(degToRad(pOptions.rotation.x))
|
|
@@ -118,8 +109,6 @@ export default class MatrixAmmo {
|
|
|
118
109
|
t.setZ(degToRad(pOptions.rotation.z))
|
|
119
110
|
startTransform.setRotation(t)
|
|
120
111
|
|
|
121
|
-
// startTransform.setRotation(pOptions.rotation.x, pOptions.rotation.y, pOptions.rotation.z);
|
|
122
|
-
|
|
123
112
|
var myMotionState = new Ammo.btDefaultMotionState(startTransform),
|
|
124
113
|
rbInfo = new Ammo.btRigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia),
|
|
125
114
|
body = new Ammo.btRigidBody(rbInfo);
|
|
@@ -135,8 +124,8 @@ export default class MatrixAmmo {
|
|
|
135
124
|
} else {
|
|
136
125
|
body.setActivationState(4)
|
|
137
126
|
}
|
|
138
|
-
// console.log('what is name.', pOptions.name)
|
|
139
127
|
body.name = pOptions.name;
|
|
128
|
+
MEObject.itIsPhysicsBody = true;
|
|
140
129
|
body.MEObject = MEObject;
|
|
141
130
|
this.dynamicsWorld.addRigidBody(body);
|
|
142
131
|
this.rigidBodies.push(body);
|
|
@@ -168,7 +157,6 @@ export default class MatrixAmmo {
|
|
|
168
157
|
let ms = physicsBody.getMotionState();
|
|
169
158
|
if(ms) {
|
|
170
159
|
var tmpTrans = new Ammo.btTransform();
|
|
171
|
-
// quat.setValue(quat.x(), quat.y(), quat.z(), quat.w());
|
|
172
160
|
tmpTrans.setIdentity();
|
|
173
161
|
tmpTrans.setOrigin(pos);
|
|
174
162
|
tmpTrans.setRotation(localRot);
|
|
@@ -222,7 +210,6 @@ export default class MatrixAmmo {
|
|
|
222
210
|
detectCollision() {
|
|
223
211
|
// console.log('override this')
|
|
224
212
|
return;
|
|
225
|
-
|
|
226
213
|
this.lastRoll = '';
|
|
227
214
|
this.presentScore = '';
|
|
228
215
|
|
|
@@ -3,8 +3,7 @@ export let fragmentWGSL = `override shadowDepthTextureSize: f32 = 1024.0;
|
|
|
3
3
|
struct Scene {
|
|
4
4
|
lightViewProjMatrix : mat4x4f,
|
|
5
5
|
cameraViewProjMatrix : mat4x4f,
|
|
6
|
-
lightPos :
|
|
7
|
-
// padding: f32, // 👈 fix alignment
|
|
6
|
+
lightPos : vec3f,
|
|
8
7
|
}
|
|
9
8
|
|
|
10
9
|
@group(0) @binding(0) var<uniform> scene : Scene;
|
|
@@ -40,7 +39,7 @@ fn main(input : FragmentInput) -> @location(0) vec4f {
|
|
|
40
39
|
}
|
|
41
40
|
}
|
|
42
41
|
visibility /= 9.0;
|
|
43
|
-
let lambertFactor = max(dot(normalize(scene.lightPos
|
|
42
|
+
let lambertFactor = max(dot(normalize(scene.lightPos - input.fragPos), normalize(input.fragNorm)), 0.0);
|
|
44
43
|
let lightingFactor = min(ambientFactor + visibility * lambertFactor, 1.0);
|
|
45
44
|
let textureColor = textureSample(meshTexture, meshSampler, input.uv);
|
|
46
45
|
|
package/src/world.js
CHANGED
|
@@ -8,6 +8,7 @@ import MatrixAmmo from "./physics/matrix-ammo.js";
|
|
|
8
8
|
import {LOG_WARN, genName, mb, scriptManager, urlQuery} from "./engine/utils.js";
|
|
9
9
|
import {MultiLang} from "./multilang/lang.js";
|
|
10
10
|
import {MatrixSounds} from "./sounds/sounds.js";
|
|
11
|
+
import {play} from "./engine/loader-obj.js";
|
|
11
12
|
|
|
12
13
|
export default class MatrixEngineWGPU {
|
|
13
14
|
|
|
@@ -34,6 +35,7 @@ export default class MatrixEngineWGPU {
|
|
|
34
35
|
this.options = {
|
|
35
36
|
useSingleRenderPass: true,
|
|
36
37
|
canvasSize: 'fullscreen',
|
|
38
|
+
canvasId: 'canvas1',
|
|
37
39
|
mainCameraParams: {
|
|
38
40
|
type: 'WASD',
|
|
39
41
|
responseCoef: 2000
|
|
@@ -41,6 +43,9 @@ export default class MatrixEngineWGPU {
|
|
|
41
43
|
}
|
|
42
44
|
callback = options;
|
|
43
45
|
}
|
|
46
|
+
if(typeof options.canvasId === 'undefined') {
|
|
47
|
+
options.canvasId = 'canvas1';
|
|
48
|
+
}
|
|
44
49
|
if(typeof options.mainCameraParams === 'undefined') {
|
|
45
50
|
options.mainCameraParams = {
|
|
46
51
|
type: 'WASD',
|
|
@@ -48,12 +53,11 @@ export default class MatrixEngineWGPU {
|
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
this.options = options;
|
|
51
|
-
|
|
52
56
|
this.mainCameraParams = options.mainCameraParams;
|
|
53
57
|
|
|
54
58
|
const target = this.options.appendTo || document.body;
|
|
55
|
-
|
|
56
59
|
var canvas = document.createElement('canvas')
|
|
60
|
+
canvas.id = this.options.canvasId;
|
|
57
61
|
if(this.options.canvasSize == 'fullscreen') {
|
|
58
62
|
canvas.width = window.innerWidth;
|
|
59
63
|
canvas.height = window.innerHeight;
|
|
@@ -255,28 +259,21 @@ export default class MatrixEngineWGPU {
|
|
|
255
259
|
if(typeof o.texturesPaths === 'undefined') {o.texturesPaths = ['./res/textures/default.png']}
|
|
256
260
|
if(typeof o.mainCameraParams === 'undefined') {o.mainCameraParams = this.mainCameraParams}
|
|
257
261
|
if(typeof o.scale === 'undefined') {o.scale = [1, 1, 1];}
|
|
258
|
-
if(typeof o.raycast === 'undefined') {o.raycast = {enabled: false}}
|
|
259
|
-
|
|
262
|
+
if(typeof o.raycast === 'undefined') {o.raycast = {enabled: false, radius: 2}}
|
|
260
263
|
o.entityArgPass = this.entityArgPass;
|
|
261
264
|
o.cameras = this.cameras;
|
|
262
|
-
// if(typeof o.name === 'undefined') {o.name = 'random' + Math.random();}
|
|
263
|
-
if(typeof o.mesh === 'undefined') {
|
|
264
|
-
mb.error('arg mesh is empty for ', o.name);
|
|
265
|
-
throw console.error('arg mesh is empty...');
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
265
|
if(typeof o.physics === 'undefined') {
|
|
269
266
|
o.physics = {
|
|
270
267
|
scale: [1, 1, 1],
|
|
271
268
|
enabled: true,
|
|
272
|
-
geometry: "Sphere"
|
|
273
|
-
radius: o.scale,
|
|
269
|
+
geometry: "Sphere",// must be fixed<<
|
|
270
|
+
radius: (typeof o.scale == Number ? o.scale : o.scale[0]),
|
|
274
271
|
name: o.name,
|
|
275
272
|
rotation: o.rotation
|
|
276
273
|
}
|
|
277
274
|
}
|
|
278
275
|
if(typeof o.physics.enabled === 'undefined') {o.physics.enabled = true}
|
|
279
|
-
if(typeof o.physics.geometry === 'undefined') {o.physics.geometry = "
|
|
276
|
+
if(typeof o.physics.geometry === 'undefined') {o.physics.geometry = "Cube"}
|
|
280
277
|
if(typeof o.physics.radius === 'undefined') {o.physics.radius = o.scale}
|
|
281
278
|
if(typeof o.physics.mass === 'undefined') {o.physics.mass = 1;}
|
|
282
279
|
if(typeof o.physics.name === 'undefined') {o.physics.name = o.name;}
|
|
@@ -284,6 +281,39 @@ export default class MatrixEngineWGPU {
|
|
|
284
281
|
if(typeof o.physics.rotation === 'undefined') {o.physics.rotation = o.rotation;}
|
|
285
282
|
o.physics.position = o.position;
|
|
286
283
|
// console.log('Mesh procedure', o)
|
|
284
|
+
// TEST OBJS SEQ ANIMS
|
|
285
|
+
if(typeof o.objAnim == 'undefined' || typeof o.objAnim == null) {
|
|
286
|
+
o.objAnim = null;
|
|
287
|
+
} else {
|
|
288
|
+
console.log('o.anim', o.objAnim)
|
|
289
|
+
// o.objAnim = {
|
|
290
|
+
// id: o.objAnim.id,
|
|
291
|
+
// sumOfAniFrames: o.objAnim.sumOfAniFrames,
|
|
292
|
+
// currentAni: o.objAnim.currentAni,
|
|
293
|
+
// speed: o.objAnim.speed,
|
|
294
|
+
// currentDraws: 0
|
|
295
|
+
// };
|
|
296
|
+
|
|
297
|
+
if(typeof o.objAnim.animations !== 'undefined') {
|
|
298
|
+
// o.objAnim.animation.anims = o.objAnim.animations;
|
|
299
|
+
console.log('o.o.objAnim.animations ', o.objAnim.animations )
|
|
300
|
+
o.objAnim.play = play;
|
|
301
|
+
}
|
|
302
|
+
// no need for single test it in future
|
|
303
|
+
o.objAnim.meshList = o.objAnim.meshList;
|
|
304
|
+
|
|
305
|
+
if(typeof o.mesh === 'undefined') {
|
|
306
|
+
o.mesh = o.objAnim.meshList[0];
|
|
307
|
+
console.info('objSeq animation is active.');
|
|
308
|
+
}
|
|
309
|
+
// scale for all second option!
|
|
310
|
+
o.objAnim.scaleAll = function(s) {
|
|
311
|
+
for(var k in this.meshList) {
|
|
312
|
+
console.log('SCALE');
|
|
313
|
+
this.meshList[k].setScale(s);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
287
317
|
let myMesh1 = new MEMeshObj(this.canvas, this.device, this.context, o)
|
|
288
318
|
if(o.physics.enabled == true) {
|
|
289
319
|
this.matrixAmmo.addPhysics(myMesh1, o.physics)
|
|
@@ -345,9 +375,7 @@ export default class MatrixEngineWGPU {
|
|
|
345
375
|
|
|
346
376
|
framePassPerObject = () => {
|
|
347
377
|
let commandEncoder = this.device.createCommandEncoder();
|
|
348
|
-
|
|
349
378
|
this.matrixAmmo.updatePhysics();
|
|
350
|
-
|
|
351
379
|
this.mainRenderBundle.forEach((meItem, index) => {
|
|
352
380
|
if(index === 0) {
|
|
353
381
|
if(meItem.renderPassDescriptor) meItem.renderPassDescriptor.colorAttachments[0].loadOp = 'clear';
|
|
@@ -360,7 +388,6 @@ export default class MatrixEngineWGPU {
|
|
|
360
388
|
// Set up view per object
|
|
361
389
|
meItem.renderPassDescriptor.colorAttachments[0].view =
|
|
362
390
|
this.context.getCurrentTexture().createView();
|
|
363
|
-
|
|
364
391
|
const passEncoder = commandEncoder.beginRenderPass(meItem.renderPassDescriptor);
|
|
365
392
|
passEncoder.executeBundles([meItem.renderBundle]); // ✅ Use only this bundle
|
|
366
393
|
passEncoder.end();
|
|
@@ -368,9 +395,7 @@ export default class MatrixEngineWGPU {
|
|
|
368
395
|
meItem.draw(commandEncoder); // fallback if no renderBundle
|
|
369
396
|
}
|
|
370
397
|
});
|
|
371
|
-
|
|
372
398
|
this.device.queue.submit([commandEncoder.finish()]);
|
|
373
399
|
requestAnimationFrame(this.frame);
|
|
374
400
|
}
|
|
375
|
-
|
|
376
401
|
}
|