@threlte/extras
Version:
Utilities, abstractions and plugins for your Threlte apps
102 lines (101 loc) • 4.2 kB
JavaScript
import { isInstanceOf } from '@threlte/core';
import { Camera, Matrix4, Object3D, OrthographicCamera, PerspectiveCamera, Raycaster, Vector2, Vector3 } from 'three';
const v1 = new Vector3();
const v2 = new Vector3();
const v3 = new Vector3();
const vec2 = new Vector2();
export const defaultCalculatePosition = (obj, camera, size) => {
const objectPos = v1.setFromMatrixPosition(obj.matrixWorld);
objectPos.project(camera);
const widthHalf = size.width / 2;
const heightHalf = size.height / 2;
return [objectPos.x * widthHalf + widthHalf, -(objectPos.y * heightHalf) + heightHalf];
};
export const isObjectBehindCamera = (el, camera) => {
const objectPos = v1.setFromMatrixPosition(el.matrixWorld);
const cameraPos = v2.setFromMatrixPosition(camera.matrixWorld);
const deltaCamObj = objectPos.sub(cameraPos);
const camDir = camera.getWorldDirection(v3);
return deltaCamObj.dot(camDir) < 0;
};
export const isObjectVisible = (el, camera, raycaster, occlude) => {
const elPos = v1.setFromMatrixPosition(el.matrixWorld);
const screenPos = v2.copy(v1);
screenPos.project(camera);
raycaster.setFromCamera(vec2.set(screenPos.x, screenPos.y), camera);
const intersects = raycaster.intersectObjects(occlude, true);
if (intersects.length) {
const intersectionDistance = intersects[0].distance;
const pointDistance = elPos.distanceTo(raycaster.ray.origin);
return pointDistance < intersectionDistance;
}
return true;
};
export const objectScale = (el, camera) => {
if (isInstanceOf(camera, 'OrthographicCamera')) {
return camera.zoom;
}
else if (isInstanceOf(camera, 'PerspectiveCamera')) {
const objectPos = v1.setFromMatrixPosition(el.matrixWorld);
const cameraPos = v2.setFromMatrixPosition(camera.matrixWorld);
const vFOV = (camera.fov * Math.PI) / 180;
const dist = objectPos.distanceTo(cameraPos);
const scaleFOV = 2 * Math.tan(vFOV / 2) * dist;
return 1 / scaleFOV;
}
else {
return 1;
}
};
export const objectZIndex = (el, camera, zIndexRange) => {
const objectPos = v1.setFromMatrixPosition(el.matrixWorld);
const cameraPos = v2.setFromMatrixPosition(camera.matrixWorld);
const dist = objectPos.distanceTo(cameraPos);
const A = (zIndexRange[1] - zIndexRange[0]) / (camera.far - camera.near);
const B = zIndexRange[1] - A * camera.far;
return Math.round(A * dist + B);
};
export const epsilon = (value) => (Math.abs(value) < 1e-10 ? 0 : value);
export const getCSSMatrix = (mat4, m, prepend = '') => {
const e = mat4.elements;
return `${prepend}matrix3d(${epsilon(m[0] * e[0])},${epsilon(m[1] * e[1])},${epsilon(m[2] * e[2])},${epsilon(m[3] * e[3])},${epsilon(m[4] * e[4])},${epsilon(m[5] * e[5])},${epsilon(m[6] * e[6])},${epsilon(m[7] * e[7])},${epsilon(m[8] * e[8])},${epsilon(m[9] * e[9])},${epsilon(m[10] * e[10])},${epsilon(m[11] * e[11])},${epsilon(m[12] * e[12])},${epsilon(m[13] * e[13])},${epsilon(m[14] * e[14])},${epsilon(m[15] * e[15])})`;
};
export const getCameraCSSMatrix = ((multipliers) => {
return (matrix) => getCSSMatrix(matrix, multipliers);
})([1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1]);
export const getObjectCSSMatrix = ((scaleMultipliers) => {
return (matrix, factor) => getCSSMatrix(matrix, scaleMultipliers(factor), 'translate(-50%,-50%)');
})((f) => [
1 / f,
1 / f,
1 / f,
1,
-1 / f,
-1 / f,
-1 / f,
-1,
1 / f,
1 / f,
1 / f,
1,
1,
1,
1,
1
]);
export const getViewportFactor = (camera, target, size) => {
if (isInstanceOf(camera, 'OrthographicCamera')) {
return 1;
}
if (isInstanceOf(camera, 'PerspectiveCamera')) {
const { width, height } = size;
const distance = camera.getWorldPosition(v1).distanceTo(target);
// convert vertical fov to radians
const fov = (camera.fov * Math.PI) / 180;
// visible height
const h = 2 * Math.tan(fov / 2) * distance;
const w = h * (width / height);
return width / w;
}
throw new Error('getViewportFactor needs a Perspective or Orthographic Camera');
};