matrix-engine-wgpu 1.3.5 → 1.3.7
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/index.js +16 -6
- package/package.json +1 -1
- package/readme.md +10 -2
- package/src/engine/mesh-obj.js +23 -3
- package/src/engine/raycast.js +89 -66
package/index.js
CHANGED
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
// import {degToRad, radToDeg} from "./utils";
|
|
7
7
|
import {downloadMeshes} from "./src/engine/loader-obj.js";
|
|
8
8
|
import MatrixEngineWGPU from "./src/world.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
addRaycastsAABBListener,
|
|
11
|
+
addRaycastListener, getRayFromMouse,
|
|
12
|
+
getRayFromMouse2, rayIntersectsSphere,
|
|
13
|
+
computeWorldVertsAndAABB, rayIntersectsAABB,
|
|
14
|
+
computeAABB
|
|
15
|
+
} from "./src/engine/raycast.js";
|
|
10
16
|
|
|
11
17
|
var about = () => {
|
|
12
18
|
console.log("Hi npm. matrix-engine for webgpu is ready...")
|
|
@@ -15,16 +21,20 @@ var about = () => {
|
|
|
15
21
|
console.log(" - Loading obj files with uvs")
|
|
16
22
|
console.log(" - Scene camera use -z front")
|
|
17
23
|
console.log(" - position, rotation - same like matrix-engine")
|
|
18
|
-
|
|
19
|
-
|
|
24
|
+
console.log(" - Physics used Ammo.js build")
|
|
25
|
+
console.log(" - Raycaster HIT/CLICK on object scene")
|
|
20
26
|
}
|
|
21
27
|
|
|
22
28
|
export {
|
|
23
29
|
MatrixEngineWGPU,
|
|
24
30
|
downloadMeshes,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
rayIntersectsSphere,
|
|
32
|
+
getRayFromMouse,
|
|
33
|
+
getRayFromMouse2,
|
|
34
|
+
addRaycastListener,
|
|
28
35
|
addRaycastsAABBListener,
|
|
36
|
+
rayIntersectsAABB,
|
|
37
|
+
computeAABB,
|
|
38
|
+
computeWorldVertsAndAABB,
|
|
29
39
|
about
|
|
30
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matrix-engine-wgpu",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
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": [
|
package/readme.md
CHANGED
|
@@ -71,6 +71,10 @@ mainCameraParams: {
|
|
|
71
71
|
|
|
72
72
|
### Object Position
|
|
73
73
|
|
|
74
|
+
Best way for access physics body object:
|
|
75
|
+
app.matrixAmmo.getBodyByName(name)
|
|
76
|
+
also app.matrixAmmo.getNameByBody
|
|
77
|
+
|
|
74
78
|
Control object position:
|
|
75
79
|
|
|
76
80
|
```js
|
|
@@ -164,13 +168,17 @@ window.addEventListener('click', (event) => {
|
|
|
164
168
|
Automatic raycast listener:
|
|
165
169
|
|
|
166
170
|
```js
|
|
167
|
-
addRaycastListener();
|
|
171
|
+
addRaycastListener();
|
|
168
172
|
|
|
169
173
|
window.addEventListener('ray.hit.event', (event) => {
|
|
170
174
|
console.log('Ray hit:', event.detail.hitObject);
|
|
171
175
|
});
|
|
172
176
|
```
|
|
173
|
-
|
|
177
|
+
Engine also exports (box):
|
|
178
|
+
- addRaycastsAABBListener
|
|
179
|
+
- rayIntersectsAABB,
|
|
180
|
+
- computeAABB,
|
|
181
|
+
- computeWorldVertsAndAABB,
|
|
174
182
|
---
|
|
175
183
|
|
|
176
184
|
### How to Load `.obj` Models
|
package/src/engine/mesh-obj.js
CHANGED
|
@@ -36,8 +36,8 @@ export default class MEMeshObj {
|
|
|
36
36
|
// ObjSequence animation
|
|
37
37
|
if(typeof o.objAnim !== 'undefined' && o.objAnim != null) {
|
|
38
38
|
this.objAnim = o.objAnim;
|
|
39
|
-
for
|
|
40
|
-
if
|
|
39
|
+
for(var key in this.objAnim.animations) {
|
|
40
|
+
if(key != 'active') this.objAnim.animations[key].speedCounter = 0;
|
|
41
41
|
}
|
|
42
42
|
console.log(`%c Mesh objAnim exist: ${o.objAnim}`, LOG_FUNNY_SMALL);
|
|
43
43
|
this.drawElements = this.drawElementsAnim;
|
|
@@ -428,6 +428,25 @@ export default class MEMeshObj {
|
|
|
428
428
|
return this.modelViewProjectionMatrix;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
this.getModelMatrix = (pos) => {
|
|
432
|
+
let modelMatrix = mat4.identity();
|
|
433
|
+
mat4.translate(modelMatrix, [pos.x, pos.y, pos.z], modelMatrix);
|
|
434
|
+
if(this.itIsPhysicsBody) {
|
|
435
|
+
mat4.rotate(modelMatrix,
|
|
436
|
+
[this.rotation.axis.x, this.rotation.axis.y, this.rotation.axis.z],
|
|
437
|
+
degToRad(this.rotation.angle),
|
|
438
|
+
modelMatrix
|
|
439
|
+
);
|
|
440
|
+
} else {
|
|
441
|
+
mat4.rotateX(modelMatrix, this.rotation.getRotX(), modelMatrix);
|
|
442
|
+
mat4.rotateY(modelMatrix, this.rotation.getRotY(), modelMatrix);
|
|
443
|
+
mat4.rotateZ(modelMatrix, this.rotation.getRotZ(), modelMatrix);
|
|
444
|
+
}
|
|
445
|
+
// Apply scale if you have it, e.g.:
|
|
446
|
+
// mat4.scale(modelMatrix, modelMatrix, [this.scale.x, this.scale.y, this.scale.z]);
|
|
447
|
+
return modelMatrix;
|
|
448
|
+
};
|
|
449
|
+
|
|
431
450
|
this.upVector = vec3.fromValues(0, 1, 0);
|
|
432
451
|
this.origin = vec3.fromValues(0, 0, 0);
|
|
433
452
|
|
|
@@ -609,6 +628,7 @@ export default class MEMeshObj {
|
|
|
609
628
|
draw = (commandEncoder) => {
|
|
610
629
|
if(this.done == false) return;
|
|
611
630
|
const transformationMatrix = this.getTransformationMatrix(this.position);
|
|
631
|
+
|
|
612
632
|
this.device.queue.writeBuffer(
|
|
613
633
|
this.sceneUniformBuffer,
|
|
614
634
|
64,
|
|
@@ -713,7 +733,7 @@ export default class MEMeshObj {
|
|
|
713
733
|
renderPass.setIndexBuffer(mesh.indexBuffer, 'uint16');
|
|
714
734
|
renderPass.drawIndexed(mesh.indexCount);
|
|
715
735
|
if(this.objAnim.playing == true) {
|
|
716
|
-
if
|
|
736
|
+
if(this.objAnim.animations[this.objAnim.animations.active].speedCounter >= this.objAnim.animations[this.objAnim.animations.active].speed) {
|
|
717
737
|
this.objAnim.currentAni++;
|
|
718
738
|
this.objAnim.animations[this.objAnim.animations.active].speedCounter = 0;
|
|
719
739
|
} else {
|
package/src/engine/raycast.js
CHANGED
|
@@ -62,6 +62,43 @@ export function getRayFromMouse(event, canvas, camera) {
|
|
|
62
62
|
return {rayOrigin, rayDirection};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export function getRayFromMouse2(event, canvas, camera) {
|
|
66
|
+
const rect = canvas.getBoundingClientRect();
|
|
67
|
+
let x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
68
|
+
let y = ((event.clientY - rect.top) / rect.height) * 2 - 1;
|
|
69
|
+
// simple invert
|
|
70
|
+
y = -y;
|
|
71
|
+
const fov = Math.PI / 4;
|
|
72
|
+
const aspect = canvas.width / canvas.height;
|
|
73
|
+
const near = 0.1;
|
|
74
|
+
const far = 1000;
|
|
75
|
+
camera.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 1000.0);
|
|
76
|
+
const invProjection = mat4.inverse(camera.projectionMatrix);
|
|
77
|
+
console.log("camera.view:" + camera.view)
|
|
78
|
+
const invView = mat4.inverse(camera.view);
|
|
79
|
+
const ndc = [x, y, 1, 1];
|
|
80
|
+
let worldPos = multiplyMatrixVector(invProjection, ndc);
|
|
81
|
+
worldPos = multiplyMatrixVector(invView, worldPos);
|
|
82
|
+
let world;
|
|
83
|
+
if(worldPos[3] !== 0) {
|
|
84
|
+
world = [
|
|
85
|
+
worldPos[0] / worldPos[3],
|
|
86
|
+
worldPos[1] / worldPos[3],
|
|
87
|
+
worldPos[2] / worldPos[3]
|
|
88
|
+
];
|
|
89
|
+
} else {
|
|
90
|
+
console.log("[raycaster]special case 0.");
|
|
91
|
+
world = [
|
|
92
|
+
worldPos[0],
|
|
93
|
+
worldPos[1],
|
|
94
|
+
worldPos[2]
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
const rayOrigin = [camera.position[0], camera.position[1], camera.position[2]];
|
|
98
|
+
const rayDirection = vec3.normalize(vec3.subtract(world, rayOrigin));
|
|
99
|
+
return {rayOrigin, rayDirection};
|
|
100
|
+
}
|
|
101
|
+
|
|
65
102
|
export function rayIntersectsSphere(rayOrigin, rayDirection, sphereCenter, sphereRadius) {
|
|
66
103
|
const pos = [sphereCenter.x, sphereCenter.y, sphereCenter.z];
|
|
67
104
|
const oc = vec3.subtract(rayOrigin, pos);
|
|
@@ -78,12 +115,15 @@ export function addRaycastListener(canvasId = "canvas1") {
|
|
|
78
115
|
let camera = app.cameras.WASD;
|
|
79
116
|
const {rayOrigin, rayDirection} = getRayFromMouse(event, canvasDom, camera);
|
|
80
117
|
for(const object of app.mainRenderBundle) {
|
|
118
|
+
if(object.raycast.enabled == false) return;
|
|
81
119
|
if(rayIntersectsSphere(rayOrigin, rayDirection, object.position, object.raycast.radius)) {
|
|
82
120
|
console.log('Object clicked:', object.name);
|
|
83
121
|
// Just like in matrix-engine webGL version "ray.hit.event"
|
|
84
122
|
dispatchEvent(new CustomEvent('ray.hit.event', {
|
|
85
123
|
detail: {
|
|
86
|
-
hitObject: object
|
|
124
|
+
hitObject: object,
|
|
125
|
+
rayOrigin: rayOrigin,
|
|
126
|
+
rayDirection: rayDirection
|
|
87
127
|
}
|
|
88
128
|
}))
|
|
89
129
|
}
|
|
@@ -91,12 +131,26 @@ export function addRaycastListener(canvasId = "canvas1") {
|
|
|
91
131
|
});
|
|
92
132
|
}
|
|
93
133
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
) {
|
|
134
|
+
// Compute AABB from flat vertices array [x,y,z, x,y,z, ...]
|
|
135
|
+
export function computeAABB(vertices) {
|
|
136
|
+
const min = [Infinity, Infinity, Infinity];
|
|
137
|
+
const max = [-Infinity, -Infinity, -Infinity];
|
|
138
|
+
|
|
139
|
+
for(let i = 0;i < vertices.length;i += 3) {
|
|
140
|
+
min[0] = Math.min(min[0], vertices[i]);
|
|
141
|
+
min[1] = Math.min(min[1], vertices[i + 1]);
|
|
142
|
+
min[2] = Math.min(min[2], vertices[i + 2]);
|
|
143
|
+
|
|
144
|
+
max[0] = Math.max(max[0], vertices[i]);
|
|
145
|
+
max[1] = Math.max(max[1], vertices[i + 1]);
|
|
146
|
+
max[2] = Math.max(max[2], vertices[i + 2]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return [min, max];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Ray-AABB intersection using slabs method
|
|
153
|
+
export function rayIntersectsAABB(rayOrigin, rayDirection, boxMin, boxMax) {
|
|
100
154
|
let tmin = (boxMin[0] - rayOrigin[0]) / rayDirection[0];
|
|
101
155
|
let tmax = (boxMax[0] - rayOrigin[0]) / rayDirection[0];
|
|
102
156
|
if(tmin > tmax) [tmin, tmax] = [tmax, tmin];
|
|
@@ -105,7 +159,7 @@ export function rayIntersectsAABB(
|
|
|
105
159
|
let tymax = (boxMax[1] - rayOrigin[1]) / rayDirection[1];
|
|
106
160
|
if(tymin > tymax) [tymin, tymax] = [tymax, tymin];
|
|
107
161
|
|
|
108
|
-
if(tmin > tymax || tymin > tmax) return false;
|
|
162
|
+
if((tmin > tymax) || (tymin > tmax)) return false;
|
|
109
163
|
if(tymin > tmin) tmin = tymin;
|
|
110
164
|
if(tymax < tmax) tmax = tymax;
|
|
111
165
|
|
|
@@ -113,30 +167,30 @@ export function rayIntersectsAABB(
|
|
|
113
167
|
let tzmax = (boxMax[2] - rayOrigin[2]) / rayDirection[2];
|
|
114
168
|
if(tzmin > tzmax) [tzmin, tzmax] = [tzmax, tzmin];
|
|
115
169
|
|
|
116
|
-
if(tmin > tzmax || tzmin > tmax) return false;
|
|
170
|
+
if((tmin > tzmax) || (tzmin > tmax)) return false;
|
|
117
171
|
|
|
118
172
|
return true;
|
|
119
173
|
}
|
|
120
174
|
|
|
121
|
-
export function
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
min[2] = Math.min(min[2], z);
|
|
133
|
-
|
|
134
|
-
max[0] = Math.max(max[0], x);
|
|
135
|
-
max[1] = Math.max(max[1], y);
|
|
136
|
-
max[2] = Math.max(max[2], z);
|
|
175
|
+
export function computeWorldVertsAndAABB(object) {
|
|
176
|
+
const modelMatrix = object.getModelMatrix(object.position);
|
|
177
|
+
const worldVerts = [];
|
|
178
|
+
for(let i = 0;i < object.mesh.vertices.length;i += 3) {
|
|
179
|
+
const local = vec3.fromValues(
|
|
180
|
+
object.mesh.vertices[i],
|
|
181
|
+
object.mesh.vertices[i + 1],
|
|
182
|
+
object.mesh.vertices[i + 2]
|
|
183
|
+
);
|
|
184
|
+
const world = vec3.transformMat4(local, modelMatrix); // OK
|
|
185
|
+
worldVerts.push(world[0], world[1], world[2]);
|
|
137
186
|
}
|
|
138
|
-
|
|
139
|
-
return
|
|
187
|
+
const [boxMin, boxMax] = computeAABB(worldVerts);
|
|
188
|
+
return {
|
|
189
|
+
modelMatrix,
|
|
190
|
+
worldVerts,
|
|
191
|
+
boxMin,
|
|
192
|
+
boxMax,
|
|
193
|
+
};
|
|
140
194
|
}
|
|
141
195
|
|
|
142
196
|
export function addRaycastsAABBListener(canvasId = "canvas1") {
|
|
@@ -148,49 +202,18 @@ export function addRaycastsAABBListener(canvasId = "canvas1") {
|
|
|
148
202
|
|
|
149
203
|
canvasDom.addEventListener('click', (event) => {
|
|
150
204
|
const camera = app.cameras.WASD;
|
|
151
|
-
const {rayOrigin, rayDirection} =
|
|
152
|
-
|
|
205
|
+
const {rayOrigin, rayDirection} = getRayFromMouse2(event, canvasDom, camera);
|
|
153
206
|
for(const object of app.mainRenderBundle) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
////////////
|
|
158
|
-
const [boxMinLocal, boxMaxLocal] = computeAABBFromVertices(object.mesh.vertices);
|
|
159
|
-
// Optionally transform to world space using object.position
|
|
160
|
-
// const pos = object.position;
|
|
161
|
-
|
|
162
|
-
const boxMin = [
|
|
163
|
-
boxMinLocal[0] + pos[0],
|
|
164
|
-
boxMinLocal[1] + pos[1],
|
|
165
|
-
boxMinLocal[2] + pos[2]
|
|
166
|
-
];
|
|
167
|
-
|
|
168
|
-
const boxMax = [
|
|
169
|
-
boxMaxLocal[0] + pos[0],
|
|
170
|
-
boxMaxLocal[1] + pos[1],
|
|
171
|
-
boxMaxLocal[2] + pos[2]
|
|
172
|
-
];
|
|
173
|
-
//////////////
|
|
174
|
-
// const size = object.size || [1, 1, 1]; // Replace with actual object size or default 1x1x1
|
|
175
|
-
|
|
176
|
-
// const boxMin = [
|
|
177
|
-
// pos[0] - size[0] / 2,
|
|
178
|
-
// pos[1] - size[1] / 2,
|
|
179
|
-
// pos[2] - size[2] / 2
|
|
180
|
-
// ];
|
|
181
|
-
// const boxMax = [
|
|
182
|
-
// pos[0] + size[0] / 2,
|
|
183
|
-
// pos[1] + size[1] / 2,
|
|
184
|
-
// pos[2] + size[2] / 2
|
|
185
|
-
// ];
|
|
186
|
-
|
|
207
|
+
const {boxMin, boxMax} = computeWorldVertsAndAABB(object);
|
|
208
|
+
if(object.raycast.enabled == false) return;
|
|
187
209
|
if(rayIntersectsAABB(rayOrigin, rayDirection, boxMin, boxMax)) {
|
|
188
|
-
console.log('AABB hit:', object.name);
|
|
189
|
-
|
|
210
|
+
// console.log('AABB hit:', object.name);
|
|
190
211
|
canvasDom.dispatchEvent(new CustomEvent('ray.hit.event', {
|
|
191
|
-
detail: {hitObject: object}
|
|
212
|
+
detail: {hitObject: object},
|
|
213
|
+
rayOrigin: rayOrigin,
|
|
214
|
+
rayDirection: rayDirection
|
|
192
215
|
}));
|
|
193
216
|
}
|
|
194
217
|
}
|
|
195
218
|
});
|
|
196
|
-
}
|
|
219
|
+
}
|