matrix-engine-wgpu 1.2.14 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/readme.md +74 -4
- 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 +153 -29
- 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.1",
|
|
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
|
@@ -69,7 +69,7 @@ mainCameraParams: {
|
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
72
|
-
### Object
|
|
72
|
+
### Object Position
|
|
73
73
|
|
|
74
74
|
Control object position:
|
|
75
75
|
|
|
@@ -134,7 +134,7 @@ app.cameras.WASD.pitch = 0.2;
|
|
|
134
134
|
|
|
135
135
|
---
|
|
136
136
|
|
|
137
|
-
|
|
137
|
+
### Object Interaction (Raycasting)
|
|
138
138
|
|
|
139
139
|
The raycast returns:
|
|
140
140
|
|
|
@@ -173,7 +173,7 @@ window.addEventListener('ray.hit.event', (event) => {
|
|
|
173
173
|
|
|
174
174
|
---
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
### How to Load `.obj` Models
|
|
177
177
|
|
|
178
178
|
```js
|
|
179
179
|
import MatrixEngineWGPU from "./src/world.js";
|
|
@@ -234,6 +234,75 @@ 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
|
+
import MatrixEngineWGPU from "../src/world.js";
|
|
243
|
+
import { downloadMeshes, makeObjSeqArg } from "../src/engine/loader-obj.js";
|
|
244
|
+
import { LOG_MATRIX } from "../src/engine/utils.js";
|
|
245
|
+
|
|
246
|
+
export var loadObjsSequence = function () {
|
|
247
|
+
let loadObjFile = new MatrixEngineWGPU({
|
|
248
|
+
useSingleRenderPass: true,
|
|
249
|
+
canvasSize: "fullscreen",
|
|
250
|
+
mainCameraParams: {
|
|
251
|
+
type: "WASD",
|
|
252
|
+
responseCoef: 1000,
|
|
253
|
+
},
|
|
254
|
+
}, () => {
|
|
255
|
+
|
|
256
|
+
addEventListener("AmmoReady", () => {
|
|
257
|
+
downloadMeshes(
|
|
258
|
+
makeObjSeqArg({
|
|
259
|
+
id: "swat-walk-pistol",
|
|
260
|
+
path: "res/meshes/objs-sequence/swat-walk-pistol",
|
|
261
|
+
from: 1,
|
|
262
|
+
to: 20,
|
|
263
|
+
}),
|
|
264
|
+
onLoadObj,
|
|
265
|
+
{ scale: [10, 10, 10] }
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
function onLoadObj(m) {
|
|
270
|
+
console.log(`%c Loaded objs: ${m} `, LOG_MATRIX);
|
|
271
|
+
var objAnim = {
|
|
272
|
+
id: "swat-walk-pistol",
|
|
273
|
+
meshList: m,
|
|
274
|
+
currentAni: 1,
|
|
275
|
+
animations: {
|
|
276
|
+
active: "walk",
|
|
277
|
+
walk: { from: 1, to: 20, speed: 3 },
|
|
278
|
+
walkPistol: { from: 36, to: 60, speed: 3 },
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
loadObjFile.addMeshObj({
|
|
283
|
+
position: { x: 0, y: 2, z: -10 },
|
|
284
|
+
rotation: { x: 0, y: 0, z: 0 },
|
|
285
|
+
rotationSpeed: { x: 0, y: 0, z: 0 },
|
|
286
|
+
scale: [100, 100, 100],
|
|
287
|
+
texturesPaths: ["./res/meshes/blender/cube.png"],
|
|
288
|
+
name: "swat",
|
|
289
|
+
mesh: m["swat-walk-pistol"],
|
|
290
|
+
physics: {
|
|
291
|
+
enabled: false,
|
|
292
|
+
geometry: "Cube",
|
|
293
|
+
},
|
|
294
|
+
objAnim: objAnim,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
app.mainRenderBundle[0].objAnim.play("walk");
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
window.app = loadObjFile;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
### 📽️ Preview
|
|
305
|
+
|
|
237
306
|
## @Note
|
|
238
307
|
If this happen less then 15 times (Loading procces) then it is ok probably...
|
|
239
308
|
```warn
|
|
@@ -278,6 +347,7 @@ Uses `watchify` to bundle JavaScript.
|
|
|
278
347
|
## Resources
|
|
279
348
|
|
|
280
349
|
All resources and output go into the `./public` folder — everything you need in one place.
|
|
350
|
+
This is static file storage.
|
|
281
351
|
|
|
282
352
|
---
|
|
283
353
|
|
|
@@ -324,4 +394,4 @@ You may use, modify, and sell projects based on this code — just keep this not
|
|
|
324
394
|
|
|
325
395
|
Top level main.js instance (Ultimate Yahtzee)
|
|
326
396
|
|
|
327
|
-
---
|
|
397
|
+
---
|
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
|
@@ -25,11 +25,21 @@ export default class MEMeshObj {
|
|
|
25
25
|
this.context = context;
|
|
26
26
|
this.entityArgPass = o.entityArgPass;
|
|
27
27
|
|
|
28
|
-
// Mesh stuff
|
|
28
|
+
// Mesh stuff - for single mesh or t-posed (fiktive-first in loading order)
|
|
29
29
|
this.mesh = o.mesh;
|
|
30
30
|
this.mesh.uvs = this.mesh.textures;
|
|
31
31
|
console.log(`%c Mesh loaded: ${o.name}`, LOG_FUNNY_SMALL);
|
|
32
32
|
|
|
33
|
+
// ObjSequence animation
|
|
34
|
+
if(typeof o.objAnim !== 'undefined' && o.objAnim != null) {
|
|
35
|
+
this.objAnim = o.objAnim;
|
|
36
|
+
for (var key in this.objAnim.animations){
|
|
37
|
+
if (key != 'active') this.objAnim.animations[key].speedCounter = 0;
|
|
38
|
+
}
|
|
39
|
+
console.log(`%c Mesh objAnim exist: ${o.objAnim}`, LOG_FUNNY_SMALL);
|
|
40
|
+
this.drawElements = this.drawElementsAnim;
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
this.inputHandler = createInputHandler(window, canvas);
|
|
34
44
|
this.cameras = o.cameras;
|
|
35
45
|
|
|
@@ -61,7 +71,7 @@ export default class MEMeshObj {
|
|
|
61
71
|
this.modelViewProjectionMatrix = mat4.create();
|
|
62
72
|
// console.log('cube added texturesPaths: ', this.texturesPaths)
|
|
63
73
|
this.loadTex0(this.texturesPaths, device).then(() => {
|
|
64
|
-
|
|
74
|
+
console.log('loaded tex buffer for mesh:', this.texture0)
|
|
65
75
|
resolve()
|
|
66
76
|
})
|
|
67
77
|
})
|
|
@@ -117,19 +127,18 @@ export default class MEMeshObj {
|
|
|
117
127
|
|
|
118
128
|
// Create the model index buffer.
|
|
119
129
|
this.indexCount = this.mesh.indices.length;
|
|
130
|
+
const indexCount = this.mesh.indices.length;
|
|
131
|
+
const size = Math.ceil(indexCount * Uint16Array.BYTES_PER_ELEMENT / 4) * 4;
|
|
132
|
+
|
|
120
133
|
this.indexBuffer = this.device.createBuffer({
|
|
121
|
-
size
|
|
122
|
-
usage: GPUBufferUsage.INDEX,
|
|
123
|
-
mappedAtCreation: true
|
|
134
|
+
size,
|
|
135
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
136
|
+
mappedAtCreation: true
|
|
124
137
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// }
|
|
130
|
-
new Uint16Array(this.indexBuffer.getMappedRange()).set(this.mesh.indices);
|
|
131
|
-
this.indexBuffer.unmap();
|
|
132
|
-
}
|
|
138
|
+
|
|
139
|
+
new Uint16Array(this.indexBuffer.getMappedRange()).set(this.mesh.indices);
|
|
140
|
+
this.indexBuffer.unmap();
|
|
141
|
+
this.indexCount = indexCount;
|
|
133
142
|
|
|
134
143
|
// Create the depth texture for rendering/sampling the shadow map.
|
|
135
144
|
this.shadowDepthTexture = this.device.createTexture({
|
|
@@ -179,7 +188,8 @@ export default class MEMeshObj {
|
|
|
179
188
|
|
|
180
189
|
const primitive = {
|
|
181
190
|
topology: 'triangle-list',
|
|
182
|
-
cullMode: 'back',
|
|
191
|
+
// cullMode: 'back', // ORI
|
|
192
|
+
cullMode: 'none', // ORI
|
|
183
193
|
};
|
|
184
194
|
|
|
185
195
|
this.uniformBufferBindGroupLayout = this.device.createBindGroupLayout({
|
|
@@ -290,7 +300,7 @@ export default class MEMeshObj {
|
|
|
290
300
|
});
|
|
291
301
|
|
|
292
302
|
const depthTexture = this.device.createTexture({
|
|
293
|
-
size: [canvas.width, canvas.height
|
|
303
|
+
size: [canvas.width, canvas.height],
|
|
294
304
|
format: 'depth24plus-stencil8',
|
|
295
305
|
usage: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
296
306
|
});
|
|
@@ -301,7 +311,7 @@ export default class MEMeshObj {
|
|
|
301
311
|
// view is acquired and set in render loop.
|
|
302
312
|
view: undefined,
|
|
303
313
|
clearValue: {r: 0.5, g: 0.5, b: 0.5, a: 1.0},
|
|
304
|
-
loadOp: 'clear',
|
|
314
|
+
loadOp: 'clear', // load old fix for FF
|
|
305
315
|
storeOp: 'store',
|
|
306
316
|
},
|
|
307
317
|
],
|
|
@@ -326,8 +336,7 @@ export default class MEMeshObj {
|
|
|
326
336
|
// one for the camera and one for the light.
|
|
327
337
|
// Then a vec3 for the light position.
|
|
328
338
|
// Rounded to the nearest multiple of 16.
|
|
329
|
-
|
|
330
|
-
size: 160,
|
|
339
|
+
size: 2 * 4 * 16 + 4 * 4,
|
|
331
340
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
332
341
|
});
|
|
333
342
|
|
|
@@ -393,16 +402,25 @@ export default class MEMeshObj {
|
|
|
393
402
|
// const this.viewMatrix = mat4.identity()
|
|
394
403
|
const camera = this.cameras[this.mainCameraParams.type];
|
|
395
404
|
this.viewMatrix = camera.update(deltaTime, this.inputHandler());
|
|
405
|
+
const scaleVec = [1, 1, 1]; // your desired scale OPTION 1
|
|
406
|
+
const scaleMatrix = mat4.scaling(scaleVec);
|
|
407
|
+
// Apply scaling
|
|
408
|
+
mat4.multiply(scaleMatrix, this.viewMatrix, this.viewMatrix);
|
|
409
|
+
|
|
396
410
|
mat4.translate(this.viewMatrix, vec3.fromValues(pos.x, pos.y, pos.z), this.viewMatrix);
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
411
|
+
|
|
412
|
+
if(this.itIsPhysicsBody == true) {
|
|
413
|
+
mat4.rotate(
|
|
414
|
+
this.viewMatrix,
|
|
415
|
+
vec3.fromValues(this.rotation.axis.x, this.rotation.axis.y, this.rotation.axis.z),
|
|
416
|
+
degToRad(this.rotation.angle), this.viewMatrix)
|
|
417
|
+
// console.info('angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
|
|
418
|
+
} else {
|
|
419
|
+
mat4.rotateX(this.viewMatrix, Math.PI * this.rotation.getRotX(), this.viewMatrix);
|
|
420
|
+
mat4.rotateY(this.viewMatrix, Math.PI * this.rotation.getRotY(), this.viewMatrix);
|
|
421
|
+
mat4.rotateZ(this.viewMatrix, Math.PI * this.rotation.getRotZ(), this.viewMatrix);
|
|
422
|
+
// console.info('NOT PHYSICS angle: ', this.rotation.angle, ' axis ', this.rotation.axis.x, ' , ', this.rotation.axis.y, ' , ', this.rotation.axis.z)
|
|
423
|
+
}
|
|
406
424
|
mat4.multiply(this.projectionMatrix, this.viewMatrix, this.modelViewProjectionMatrix);
|
|
407
425
|
return this.modelViewProjectionMatrix;
|
|
408
426
|
}
|
|
@@ -475,6 +493,11 @@ export default class MEMeshObj {
|
|
|
475
493
|
};
|
|
476
494
|
|
|
477
495
|
this.done = true;
|
|
496
|
+
}).then(() => {
|
|
497
|
+
if(typeof this.objAnim !== 'undefined' && this.objAnim !== null) {
|
|
498
|
+
console.log('after all load configutr mesh list buffers')
|
|
499
|
+
this.updateMeshListBuffers()
|
|
500
|
+
}
|
|
478
501
|
})
|
|
479
502
|
}
|
|
480
503
|
|
|
@@ -553,9 +576,17 @@ export default class MEMeshObj {
|
|
|
553
576
|
|
|
554
577
|
return new Promise(async (resolve) => {
|
|
555
578
|
const response = await fetch(texturesPaths[0]);
|
|
579
|
+
|
|
580
|
+
// const blob = await response.blob();
|
|
581
|
+
// if(!blob.type.startsWith('image/')) {
|
|
582
|
+
// console.error("Unexpected texture response type:", blob.type);
|
|
583
|
+
// return;
|
|
584
|
+
// }
|
|
585
|
+
|
|
586
|
+
// const imageBitmap = await createImageBitmap(blob);
|
|
556
587
|
const imageBitmap = await createImageBitmap(await response.blob());
|
|
557
588
|
this.texture0 = device.createTexture({
|
|
558
|
-
size: [imageBitmap.width, imageBitmap.height, 1],
|
|
589
|
+
size: [imageBitmap.width, imageBitmap.height, 1], // REMOVED 1
|
|
559
590
|
format: 'rgba8unorm',
|
|
560
591
|
usage:
|
|
561
592
|
GPUTextureUsage.TEXTURE_BINDING |
|
|
@@ -574,7 +605,6 @@ export default class MEMeshObj {
|
|
|
574
605
|
|
|
575
606
|
draw = (commandEncoder) => {
|
|
576
607
|
if(this.done == false) return;
|
|
577
|
-
// console.log('test draw for meshObj !')
|
|
578
608
|
const transformationMatrix = this.getTransformationMatrix(this.position);
|
|
579
609
|
this.device.queue.writeBuffer(
|
|
580
610
|
this.sceneUniformBuffer,
|
|
@@ -598,6 +628,100 @@ export default class MEMeshObj {
|
|
|
598
628
|
renderPass.drawIndexed(this.indexCount);
|
|
599
629
|
}
|
|
600
630
|
|
|
631
|
+
// test
|
|
632
|
+
createGPUBuffer(dataArray, usage) {
|
|
633
|
+
if(!dataArray || typeof dataArray.length !== 'number') {
|
|
634
|
+
throw new Error('Invalid data array passed to createGPUBuffer');
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
const size = dataArray.length * dataArray.BYTES_PER_ELEMENT;
|
|
638
|
+
if(!Number.isFinite(size) || size <= 0) {
|
|
639
|
+
throw new Error(`Invalid buffer size: ${size}`);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const buffer = this.device.createBuffer({
|
|
643
|
+
size,
|
|
644
|
+
usage,
|
|
645
|
+
mappedAtCreation: true,
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
const writeArray = dataArray.constructor === Float32Array
|
|
649
|
+
? new Float32Array(buffer.getMappedRange())
|
|
650
|
+
: new Uint16Array(buffer.getMappedRange());
|
|
651
|
+
|
|
652
|
+
writeArray.set(dataArray);
|
|
653
|
+
buffer.unmap();
|
|
654
|
+
|
|
655
|
+
return buffer;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
updateMeshListBuffers() {
|
|
660
|
+
for(const key in this.objAnim.meshList) {
|
|
661
|
+
const mesh = this.objAnim.meshList[key];
|
|
662
|
+
|
|
663
|
+
mesh.vertexBuffer = this.device.createBuffer({
|
|
664
|
+
size: mesh.vertices.length * Float32Array.BYTES_PER_ELEMENT,
|
|
665
|
+
usage: GPUBufferUsage.VERTEX,
|
|
666
|
+
mappedAtCreation: true,
|
|
667
|
+
});
|
|
668
|
+
new Float32Array(mesh.vertexBuffer.getMappedRange()).set(mesh.vertices);
|
|
669
|
+
mesh.vertexBuffer.unmap();
|
|
670
|
+
|
|
671
|
+
// Normals
|
|
672
|
+
mesh.vertexNormalsBuffer = this.device.createBuffer({
|
|
673
|
+
size: mesh.vertexNormals.length * Float32Array.BYTES_PER_ELEMENT,
|
|
674
|
+
usage: GPUBufferUsage.VERTEX,
|
|
675
|
+
mappedAtCreation: true,
|
|
676
|
+
});
|
|
677
|
+
new Float32Array(mesh.vertexNormalsBuffer.getMappedRange()).set(mesh.vertexNormals);
|
|
678
|
+
mesh.vertexNormalsBuffer.unmap();
|
|
679
|
+
|
|
680
|
+
// UVs
|
|
681
|
+
mesh.vertexTexCoordsBuffer = this.device.createBuffer({
|
|
682
|
+
size: mesh.textures.length * Float32Array.BYTES_PER_ELEMENT,
|
|
683
|
+
usage: GPUBufferUsage.VERTEX,
|
|
684
|
+
mappedAtCreation: true,
|
|
685
|
+
});
|
|
686
|
+
new Float32Array(mesh.vertexTexCoordsBuffer.getMappedRange()).set(mesh.textures);
|
|
687
|
+
mesh.vertexTexCoordsBuffer.unmap();
|
|
688
|
+
|
|
689
|
+
// Indices
|
|
690
|
+
const indexCount = mesh.indices.length;
|
|
691
|
+
const indexSize = Math.ceil(indexCount * Uint16Array.BYTES_PER_ELEMENT / 4) * 4;
|
|
692
|
+
mesh.indexBuffer = this.device.createBuffer({
|
|
693
|
+
size: indexSize,
|
|
694
|
+
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
|
|
695
|
+
mappedAtCreation: true,
|
|
696
|
+
});
|
|
697
|
+
new Uint16Array(mesh.indexBuffer.getMappedRange()).set(mesh.indices);
|
|
698
|
+
mesh.indexBuffer.unmap();
|
|
699
|
+
mesh.indexCount = indexCount;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
drawElementsAnim = (renderPass) => {
|
|
704
|
+
renderPass.setBindGroup(0, this.sceneBindGroupForRender);
|
|
705
|
+
renderPass.setBindGroup(1, this.modelBindGroup);
|
|
706
|
+
const mesh = this.objAnim.meshList[this.objAnim.id + this.objAnim.currentAni];
|
|
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
|
+
if (this.objAnim.animations[this.objAnim.animations.active].speedCounter >= this.objAnim.animations[this.objAnim.animations.active].speed) {
|
|
714
|
+
this.objAnim.currentAni++;
|
|
715
|
+
this.objAnim.animations[this.objAnim.animations.active].speedCounter = 0;
|
|
716
|
+
} else {
|
|
717
|
+
this.objAnim.animations[this.objAnim.animations.active].speedCounter++;
|
|
718
|
+
}
|
|
719
|
+
if(this.objAnim.currentAni >= this.objAnim.animations[this.objAnim.animations.active].to) {
|
|
720
|
+
this.objAnim.currentAni = this.objAnim.animations[this.objAnim.animations.active].from;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
601
725
|
drawShadows = (shadowPass) => {
|
|
602
726
|
shadowPass.setBindGroup(0, this.sceneBindGroupForShadow);
|
|
603
727
|
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
|
}
|