@react-three/drei
Version:
useful add-ons for react-three-fiber
130 lines (123 loc) • 4.38 kB
JavaScript
import _extends from '@babel/runtime/helpers/esm/extends';
import * as React from 'react';
import * as THREE from 'three';
import { applyProps } from '@react-three/fiber';
import { DecalGeometry } from 'three-stdlib';
function isArray(vec) {
return Array.isArray(vec);
}
function vecToArray(vec = [0, 0, 0]) {
if (isArray(vec)) {
return vec;
} else if (vec instanceof THREE.Vector3 || vec instanceof THREE.Euler) {
return [vec.x, vec.y, vec.z];
} else {
return [vec, vec, vec];
}
}
const Decal = /* @__PURE__ */React.forwardRef(function Decal({
debug,
depthTest = false,
polygonOffsetFactor = -10,
map,
mesh,
children,
position,
rotation,
scale,
...props
}, forwardRef) {
const ref = React.useRef(null);
React.useImperativeHandle(forwardRef, () => ref.current);
const helper = React.useRef(null);
const state = React.useRef({
position: new THREE.Vector3(),
rotation: new THREE.Euler(),
scale: new THREE.Vector3(1, 1, 1)
});
React.useLayoutEffect(() => {
const parent = (mesh == null ? void 0 : mesh.current) || ref.current.parent;
const target = ref.current;
if (!(parent instanceof THREE.Mesh)) {
throw new Error('Decal must have a Mesh as parent or specify its "mesh" prop');
}
if (parent) {
applyProps(state.current, {
position,
scale
});
// Zero out the parents matrix world for this operation
const matrixWorld = parent.matrixWorld.clone();
parent.matrixWorld.identity();
if (!rotation || typeof rotation === 'number') {
const o = new THREE.Object3D();
o.position.copy(state.current.position);
// Thanks https://x.com/N8Programs !
const vertices = parent.geometry.attributes.position.array;
if (parent.geometry.attributes.normal === undefined) parent.geometry.computeVertexNormals();
const normal = parent.geometry.attributes.normal.array;
let distance = Infinity;
new THREE.Vector3();
let closestNormal = new THREE.Vector3();
const ox = o.position.x;
const oy = o.position.y;
const oz = o.position.z;
const vLength = vertices.length;
let chosenIdx = -1;
for (let i = 0; i < vLength; i += 3) {
const x = vertices[i];
const y = vertices[i + 1];
const z = vertices[i + 2];
const xDiff = x - ox;
const yDiff = y - oy;
const zDiff = z - oz;
const distSquared = xDiff * xDiff + yDiff * yDiff + zDiff * zDiff;
if (distSquared < distance) {
distance = distSquared;
chosenIdx = i;
}
}
closestNormal.fromArray(normal, chosenIdx);
// Get vector tangent to normal
o.lookAt(o.position.clone().add(closestNormal));
o.rotateZ(Math.PI);
o.rotateY(Math.PI);
if (typeof rotation === 'number') o.rotateZ(rotation);
applyProps(state.current, {
rotation: o.rotation
});
} else {
applyProps(state.current, {
rotation
});
}
target.geometry = new DecalGeometry(parent, state.current.position, state.current.rotation, state.current.scale);
// Reset parents matix-world
parent.matrixWorld = matrixWorld;
return () => {
target.geometry.dispose();
};
}
}, [mesh, ...vecToArray(position), ...vecToArray(scale), ...vecToArray(rotation)]);
React.useLayoutEffect(() => {
if (helper.current) {
applyProps(helper.current, state.current);
// Prevent the helpers from blocking rays
helper.current.traverse(child => child.raycast = () => null);
}
}, [debug]);
// <meshStandardMaterial transparent polygonOffset polygonOffsetFactor={-10} {...props} />}
return /*#__PURE__*/React.createElement("mesh", _extends({
ref: ref,
"material-transparent": true,
"material-polygonOffset": true,
"material-polygonOffsetFactor": polygonOffsetFactor,
"material-depthTest": depthTest,
"material-map": map
}, props), children, debug && /*#__PURE__*/React.createElement("mesh", {
ref: helper
}, /*#__PURE__*/React.createElement("boxGeometry", null), /*#__PURE__*/React.createElement("meshNormalMaterial", {
wireframe: true
}), /*#__PURE__*/React.createElement("axesHelper", null)));
});
export { Decal };