matrix-engine-wgpu
Version:
+HOTFIX raycast, webGPU powered pwa application. Crazy fast rendering with AmmoJS physics support. Simple raycaster hit object added.
101 lines (90 loc) • 3.13 kB
JavaScript
/**
* @author Nikola Lukic
* @email zlatnaspirala@gmail.com
* @site https://maximumroulette.com
* @Licence GPL v3
* @credits chatgpt used for this script adaptation.
* @Note matrix-engine-wgpu adaptation test
* default for now:
* app.cameras['WASD']
* Only tested for WASD type of camera.
* app is global - will be fixed in future
*/
import {mat4, vec3, vec4} from "wgpu-matrix";
let rayHitEvent;
export let touchCoordinate = {
enabled: false,
x: 0,
y: 0,
stopOnFirstDetectedHit: false
};
function multiplyMatrixVector(matrix, vector) {
return vec4.transformMat4(vector, matrix);
}
export function getRayFromMouse(event, canvas, camera) {
const rect = canvas.getBoundingClientRect();
let x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
let y = ((event.clientY - rect.top) / rect.height) * 2 - 1;
// simple invert
x = -x;
y = -y;
const fov = Math.PI / 4;
const aspect = canvas.width / canvas.height;
const near = 0.1;
const far = 1000;
camera.projectionMatrix = mat4.perspective((2 * Math.PI) / 5, aspect, 1, 1000.0);
const invProjection = mat4.inverse(camera.projectionMatrix);
// const correctedView = mat4.clone(camera.view_);
// correctedView[2] *= -1;
// correctedView[6] *= -1;
// correctedView[10] *= -1;
// const invView = mat4.inverse(correctedView);
const invView = mat4.inverse(camera.view);
const ndc = [x, y, 1, 1];
let worldPos = multiplyMatrixVector(invProjection, ndc);
worldPos = multiplyMatrixVector(invView, worldPos);
let world;
if (worldPos[3] !== 0) {
world = [
worldPos[0] / worldPos[3],
worldPos[2] / worldPos[3],
worldPos[1] / worldPos[3]
];
} else {
console.log("[raycaster]special case 0.")
world = [
worldPos[0],
worldPos[1],
worldPos[2]
];
}
const rayOrigin = [camera.position[0], camera.position[1], camera.position[2]];
const rayDirection = vec3.normalize(vec3.subtract(world, rayOrigin));
rayDirection[2] = -rayDirection[2];
return {rayOrigin, rayDirection};
}
export function rayIntersectsSphere(rayOrigin, rayDirection, sphereCenter, sphereRadius) {
const pos = [sphereCenter.x, sphereCenter.y, sphereCenter.z];
const oc = vec3.subtract(rayOrigin, pos);
const a = vec3.dot(rayDirection, rayDirection);
const b = 2.0 * vec3.dot(oc, rayDirection);
const c = vec3.dot(oc, oc) - sphereRadius * sphereRadius;
const discriminant = b * b - 4 * a * c;
return discriminant > 0;
}
export function addRaycastListener () {
window.addEventListener('click', (event) => {
let canvas = document.getElementsByTagName('canvas')[0];
let camera = app.cameras.WASD;
const { rayOrigin, rayDirection } = getRayFromMouse(event, canvas, camera);
for (const object of app.mainRenderBundle) {
if (rayIntersectsSphere(rayOrigin, rayDirection, object.position, object.raycast.radius)) {
// console.log('Object clicked:', object.name);
// Just like in matrix-engine webGL version "ray.hit.event"
dispatchEvent(new CustomEvent('ray.hit.event', {detail: {
hitObject: object
}}))
}
}
});
}