Initial project import
This commit is contained in:
375
node_modules/@react-three/drei/web/Html.js
generated
vendored
Normal file
375
node_modules/@react-three/drei/web/Html.js
generated
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
import _extends from '@babel/runtime/helpers/esm/extends';
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
import { Vector3, DoubleSide, OrthographicCamera, PerspectiveCamera, Vector2 } from 'three';
|
||||
import { useThree, useFrame } from '@react-three/fiber';
|
||||
|
||||
const v1 = /* @__PURE__ */new Vector3();
|
||||
const v2 = /* @__PURE__ */new Vector3();
|
||||
const v3 = /* @__PURE__ */new Vector3();
|
||||
const v4 = /* @__PURE__ */new Vector2();
|
||||
function defaultCalculatePosition(el, camera, size) {
|
||||
const objectPos = v1.setFromMatrixPosition(el.matrixWorld);
|
||||
objectPos.project(camera);
|
||||
const widthHalf = size.width / 2;
|
||||
const heightHalf = size.height / 2;
|
||||
return [objectPos.x * widthHalf + widthHalf, -(objectPos.y * heightHalf) + heightHalf];
|
||||
}
|
||||
function 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.angleTo(camDir) > Math.PI / 2;
|
||||
}
|
||||
function isObjectVisible(el, camera, raycaster, occlude) {
|
||||
const elPos = v1.setFromMatrixPosition(el.matrixWorld);
|
||||
const screenPos = elPos.clone();
|
||||
screenPos.project(camera);
|
||||
v4.set(screenPos.x, screenPos.y);
|
||||
raycaster.setFromCamera(v4, 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;
|
||||
}
|
||||
function objectScale(el, camera) {
|
||||
if (camera instanceof OrthographicCamera) {
|
||||
return camera.zoom;
|
||||
} else if (camera instanceof 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;
|
||||
}
|
||||
}
|
||||
function objectZIndex(el, camera, zIndexRange) {
|
||||
if (camera instanceof PerspectiveCamera || camera instanceof OrthographicCamera) {
|
||||
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);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
const epsilon = value => Math.abs(value) < 1e-10 ? 0 : value;
|
||||
function getCSSMatrix(matrix, multipliers, prepend = '') {
|
||||
let matrix3d = 'matrix3d(';
|
||||
for (let i = 0; i !== 16; i++) {
|
||||
matrix3d += epsilon(multipliers[i] * matrix.elements[i]) + (i !== 15 ? ',' : ')');
|
||||
}
|
||||
return prepend + matrix3d;
|
||||
}
|
||||
const getCameraCSSMatrix = (multipliers => {
|
||||
return matrix => getCSSMatrix(matrix, multipliers);
|
||||
})([1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1, 1, -1, 1, 1]);
|
||||
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]);
|
||||
function isRefObject(ref) {
|
||||
return ref && typeof ref === 'object' && 'current' in ref;
|
||||
}
|
||||
const Html = /* @__PURE__ */React.forwardRef(({
|
||||
children,
|
||||
eps = 0.001,
|
||||
style,
|
||||
className,
|
||||
prepend,
|
||||
center,
|
||||
fullscreen,
|
||||
portal,
|
||||
distanceFactor,
|
||||
sprite = false,
|
||||
transform = false,
|
||||
occlude,
|
||||
onOcclude,
|
||||
castShadow,
|
||||
receiveShadow,
|
||||
material,
|
||||
geometry,
|
||||
zIndexRange = [16777271, 0],
|
||||
calculatePosition = defaultCalculatePosition,
|
||||
as = 'div',
|
||||
wrapperClass,
|
||||
pointerEvents = 'auto',
|
||||
...props
|
||||
}, ref) => {
|
||||
const {
|
||||
gl,
|
||||
camera,
|
||||
scene,
|
||||
size,
|
||||
raycaster,
|
||||
events,
|
||||
viewport
|
||||
} = useThree();
|
||||
const [el] = React.useState(() => document.createElement(as));
|
||||
const root = React.useRef(null);
|
||||
const group = React.useRef(null);
|
||||
const oldZoom = React.useRef(0);
|
||||
const oldPosition = React.useRef([0, 0]);
|
||||
const transformOuterRef = React.useRef(null);
|
||||
const transformInnerRef = React.useRef(null);
|
||||
// Append to the connected element, which makes HTML work with views
|
||||
const target = (portal == null ? void 0 : portal.current) || events.connected || gl.domElement.parentNode;
|
||||
const occlusionMeshRef = React.useRef(null);
|
||||
const isMeshSizeSet = React.useRef(false);
|
||||
const isRayCastOcclusion = React.useMemo(() => {
|
||||
return occlude && occlude !== 'blending' || Array.isArray(occlude) && occlude.length && isRefObject(occlude[0]);
|
||||
}, [occlude]);
|
||||
React.useLayoutEffect(() => {
|
||||
const el = gl.domElement;
|
||||
if (occlude && occlude === 'blending') {
|
||||
el.style.zIndex = `${Math.floor(zIndexRange[0] / 2)}`;
|
||||
el.style.position = 'absolute';
|
||||
el.style.pointerEvents = 'none';
|
||||
} else {
|
||||
el.style.zIndex = null;
|
||||
el.style.position = null;
|
||||
el.style.pointerEvents = null;
|
||||
}
|
||||
}, [occlude]);
|
||||
React.useLayoutEffect(() => {
|
||||
if (group.current) {
|
||||
const currentRoot = root.current = ReactDOM.createRoot(el);
|
||||
scene.updateMatrixWorld();
|
||||
if (transform) {
|
||||
el.style.cssText = `position:absolute;top:0;left:0;pointer-events:none;overflow:hidden;`;
|
||||
} else {
|
||||
const vec = calculatePosition(group.current, camera, size);
|
||||
el.style.cssText = `position:absolute;top:0;left:0;transform:translate3d(${vec[0]}px,${vec[1]}px,0);transform-origin:0 0;`;
|
||||
}
|
||||
if (target) {
|
||||
if (prepend) target.prepend(el);else target.appendChild(el);
|
||||
}
|
||||
return () => {
|
||||
if (target) target.removeChild(el);
|
||||
currentRoot.unmount();
|
||||
};
|
||||
}
|
||||
}, [target, transform]);
|
||||
React.useLayoutEffect(() => {
|
||||
if (wrapperClass) el.className = wrapperClass;
|
||||
}, [wrapperClass]);
|
||||
const styles = React.useMemo(() => {
|
||||
if (transform) {
|
||||
return {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
transformStyle: 'preserve-3d',
|
||||
pointerEvents: 'none'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
position: 'absolute',
|
||||
transform: center ? 'translate3d(-50%,-50%,0)' : 'none',
|
||||
...(fullscreen && {
|
||||
top: -size.height / 2,
|
||||
left: -size.width / 2,
|
||||
width: size.width,
|
||||
height: size.height
|
||||
}),
|
||||
...style
|
||||
};
|
||||
}
|
||||
}, [style, center, fullscreen, size, transform]);
|
||||
const transformInnerStyles = React.useMemo(() => ({
|
||||
position: 'absolute',
|
||||
pointerEvents
|
||||
}), [pointerEvents]);
|
||||
React.useLayoutEffect(() => {
|
||||
isMeshSizeSet.current = false;
|
||||
if (transform) {
|
||||
var _root$current;
|
||||
(_root$current = root.current) == null || _root$current.render(/*#__PURE__*/React.createElement("div", {
|
||||
ref: transformOuterRef,
|
||||
style: styles
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
ref: transformInnerRef,
|
||||
style: transformInnerStyles
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
ref: ref,
|
||||
className: className,
|
||||
style: style,
|
||||
children: children
|
||||
}))));
|
||||
} else {
|
||||
var _root$current2;
|
||||
(_root$current2 = root.current) == null || _root$current2.render(/*#__PURE__*/React.createElement("div", {
|
||||
ref: ref,
|
||||
style: styles,
|
||||
className: className,
|
||||
children: children
|
||||
}));
|
||||
}
|
||||
});
|
||||
const visible = React.useRef(true);
|
||||
useFrame(gl => {
|
||||
if (group.current) {
|
||||
camera.updateMatrixWorld();
|
||||
group.current.updateWorldMatrix(true, false);
|
||||
const vec = transform ? oldPosition.current : calculatePosition(group.current, camera, size);
|
||||
if (transform || Math.abs(oldZoom.current - camera.zoom) > eps || Math.abs(oldPosition.current[0] - vec[0]) > eps || Math.abs(oldPosition.current[1] - vec[1]) > eps) {
|
||||
const isBehindCamera = isObjectBehindCamera(group.current, camera);
|
||||
let raytraceTarget = false;
|
||||
if (isRayCastOcclusion) {
|
||||
if (Array.isArray(occlude)) {
|
||||
raytraceTarget = occlude.map(item => item.current);
|
||||
} else if (occlude !== 'blending') {
|
||||
raytraceTarget = [scene];
|
||||
}
|
||||
}
|
||||
const previouslyVisible = visible.current;
|
||||
if (raytraceTarget) {
|
||||
const isvisible = isObjectVisible(group.current, camera, raycaster, raytraceTarget);
|
||||
visible.current = isvisible && !isBehindCamera;
|
||||
} else {
|
||||
visible.current = !isBehindCamera;
|
||||
}
|
||||
if (previouslyVisible !== visible.current) {
|
||||
if (onOcclude) onOcclude(!visible.current);else el.style.display = visible.current ? 'block' : 'none';
|
||||
}
|
||||
const halfRange = Math.floor(zIndexRange[0] / 2);
|
||||
const zRange = occlude ? isRayCastOcclusion //
|
||||
? [zIndexRange[0], halfRange] : [halfRange - 1, 0] : zIndexRange;
|
||||
el.style.zIndex = `${objectZIndex(group.current, camera, zRange)}`;
|
||||
if (transform) {
|
||||
const [widthHalf, heightHalf] = [size.width / 2, size.height / 2];
|
||||
const fov = camera.projectionMatrix.elements[5] * heightHalf;
|
||||
const {
|
||||
isOrthographicCamera,
|
||||
top,
|
||||
left,
|
||||
bottom,
|
||||
right
|
||||
} = camera;
|
||||
const cameraMatrix = getCameraCSSMatrix(camera.matrixWorldInverse);
|
||||
const cameraTransform = isOrthographicCamera ? `scale(${fov})translate(${epsilon(-(right + left) / 2)}px,${epsilon((top + bottom) / 2)}px)` : `translateZ(${fov}px)`;
|
||||
let matrix = group.current.matrixWorld;
|
||||
if (sprite) {
|
||||
matrix = camera.matrixWorldInverse.clone().transpose().copyPosition(matrix).scale(group.current.scale);
|
||||
matrix.elements[3] = matrix.elements[7] = matrix.elements[11] = 0;
|
||||
matrix.elements[15] = 1;
|
||||
}
|
||||
el.style.width = size.width + 'px';
|
||||
el.style.height = size.height + 'px';
|
||||
el.style.perspective = isOrthographicCamera ? '' : `${fov}px`;
|
||||
if (transformOuterRef.current && transformInnerRef.current) {
|
||||
transformOuterRef.current.style.transform = `${cameraTransform}${cameraMatrix}translate(${widthHalf}px,${heightHalf}px)`;
|
||||
transformInnerRef.current.style.transform = getObjectCSSMatrix(matrix, 1 / ((distanceFactor || 10) / 400));
|
||||
}
|
||||
} else {
|
||||
const scale = distanceFactor === undefined ? 1 : objectScale(group.current, camera) * distanceFactor;
|
||||
el.style.transform = `translate3d(${vec[0]}px,${vec[1]}px,0) scale(${scale})`;
|
||||
}
|
||||
oldPosition.current = vec;
|
||||
oldZoom.current = camera.zoom;
|
||||
}
|
||||
}
|
||||
if (!isRayCastOcclusion && occlusionMeshRef.current && !isMeshSizeSet.current) {
|
||||
if (transform) {
|
||||
if (transformOuterRef.current) {
|
||||
const el = transformOuterRef.current.children[0];
|
||||
if (el != null && el.clientWidth && el != null && el.clientHeight) {
|
||||
const {
|
||||
isOrthographicCamera
|
||||
} = camera;
|
||||
if (isOrthographicCamera || geometry) {
|
||||
if (props.scale) {
|
||||
if (!Array.isArray(props.scale)) {
|
||||
occlusionMeshRef.current.scale.setScalar(1 / props.scale);
|
||||
} else if (props.scale instanceof Vector3) {
|
||||
occlusionMeshRef.current.scale.copy(props.scale.clone().divideScalar(1));
|
||||
} else {
|
||||
occlusionMeshRef.current.scale.set(1 / props.scale[0], 1 / props.scale[1], 1 / props.scale[2]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const ratio = (distanceFactor || 10) / 400;
|
||||
const w = el.clientWidth * ratio;
|
||||
const h = el.clientHeight * ratio;
|
||||
occlusionMeshRef.current.scale.set(w, h, 1);
|
||||
}
|
||||
isMeshSizeSet.current = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const ele = el.children[0];
|
||||
if (ele != null && ele.clientWidth && ele != null && ele.clientHeight) {
|
||||
const ratio = 1 / viewport.factor;
|
||||
const w = ele.clientWidth * ratio;
|
||||
const h = ele.clientHeight * ratio;
|
||||
occlusionMeshRef.current.scale.set(w, h, 1);
|
||||
isMeshSizeSet.current = true;
|
||||
}
|
||||
occlusionMeshRef.current.lookAt(gl.camera.position);
|
||||
}
|
||||
}
|
||||
});
|
||||
const shaders = React.useMemo(() => ({
|
||||
vertexShader: !transform ? /* glsl */`
|
||||
/*
|
||||
This shader is from the THREE's SpriteMaterial.
|
||||
We need to turn the backing plane into a Sprite
|
||||
(make it always face the camera) if "transfrom"
|
||||
is false.
|
||||
*/
|
||||
#include <common>
|
||||
|
||||
void main() {
|
||||
vec2 center = vec2(0., 1.);
|
||||
float rotation = 0.0;
|
||||
|
||||
// This is somewhat arbitrary, but it seems to work well
|
||||
// Need to figure out how to derive this dynamically if it even matters
|
||||
float size = 0.03;
|
||||
|
||||
vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
|
||||
vec2 scale;
|
||||
scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
|
||||
scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
|
||||
|
||||
bool isPerspective = isPerspectiveMatrix( projectionMatrix );
|
||||
if ( isPerspective ) scale *= - mvPosition.z;
|
||||
|
||||
vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale * size;
|
||||
vec2 rotatedPosition;
|
||||
rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
|
||||
rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
|
||||
mvPosition.xy += rotatedPosition;
|
||||
|
||||
gl_Position = projectionMatrix * mvPosition;
|
||||
}
|
||||
` : undefined,
|
||||
fragmentShader: /* glsl */`
|
||||
void main() {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
`
|
||||
}), [transform]);
|
||||
return /*#__PURE__*/React.createElement("group", _extends({}, props, {
|
||||
ref: group
|
||||
}), occlude && !isRayCastOcclusion && /*#__PURE__*/React.createElement("mesh", {
|
||||
castShadow: castShadow,
|
||||
receiveShadow: receiveShadow,
|
||||
ref: occlusionMeshRef
|
||||
}, geometry || /*#__PURE__*/React.createElement("planeGeometry", null), material || /*#__PURE__*/React.createElement("shaderMaterial", {
|
||||
side: DoubleSide,
|
||||
vertexShader: shaders.vertexShader,
|
||||
fragmentShader: shaders.fragmentShader
|
||||
})));
|
||||
});
|
||||
|
||||
export { Html };
|
||||
Reference in New Issue
Block a user