UNPKG

@react-three/drei

Version:

useful add-ons for react-three-fiber

130 lines (123 loc) 4.38 kB
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 };