Files
cadhub/app/web/src/components/Hero/AssetWithGooey.tsx
2021-09-12 17:47:59 -04:00

114 lines
3.9 KiB
TypeScript

import React, { useRef, useMemo } from 'react'
import * as THREE from 'three'
import { useLoader, useThree, useFrame } from '@react-three/fiber'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
import { useEdgeSplit } from 'src/helpers/hooks/useEdgeSplit'
import texture from 'src/components/IdeViewer/dullFrontLitMetal.png'
import { useTexture, MeshDistortMaterial, Sphere } from '@react-three/drei'
const thresholdAngle = 10
export default function AssetWithGooey({
assetUrl,
offset,
scale,
}: {
assetUrl: string
offset: number[]
scale: number
}) {
const geo = useLoader(STLLoader, assetUrl)
const edgeRef = useRef(null)
const coffeeRef = useRef(null)
const mesh = useEdgeSplit((thresholdAngle * Math.PI) / 180, true, geo)
const colorMap = useTexture(texture)
const edges = React.useMemo(() => new THREE.EdgesGeometry(geo, 12), [geo])
const position = [offset[0], offset[1], 5]
const scaleArr = Array.from({ length: 3 }).map(() => scale)
const { mouse } = useThree()
const [rEuler, rQuaternion] = useMemo(
() => [new THREE.Euler(), new THREE.Quaternion()],
[]
)
useFrame((state, delta) => {
if (edgeRef.current) {
edgeRef.current.rotation.y += 0.01
}
if (coffeeRef.current) {
rEuler.set((-mouse.y * Math.PI) / 4, (mouse.x * Math.PI) / 2, 0)
coffeeRef.current.quaternion.slerp(rQuaternion.setFromEuler(rEuler), 0.1)
}
})
return (
<group dispose={null} ref={edgeRef} position={position}>
<group ref={coffeeRef}>
<mesh ref={mesh} scale={scaleArr} geometry={geo}>
<meshPhysicalMaterial
color="#FF6EBD"
map={colorMap}
clearcoat={0.5}
clearcoatRoughness={0.01}
roughness={0}
metalness={0.7}
smoothShading
/>
</mesh>
<lineSegments scale={scale} geometry={edges} renderOrder={100}>
<lineBasicMaterial color="#aaaaff" />
</lineSegments>
</group>
<ambientLight intensity={2} />
<Gooey />
<ambientLight intensity={1.8} />
</group>
)
}
function randomSign(num: number): number {
return Math.random() > 0.5 ? num : -num
}
function Gooey() {
const blobsData = useMemo(() => {
const firstSet = Array.from({ length: 5 }).map((_, index) => {
const dist = Math.random() * 3 + 2.5
const x = randomSign(Math.random() * dist)
const y = randomSign(Math.sqrt(dist * dist - x * x))
const z = randomSign(Math.random() * 2)
const position: [number, number, number] = [x, z, y]
const size = Math.random() * 0.8 + 0.1
const distort = (size > .1) ? Math.random() * .6 * size + 0.2 : 0
const speed = (size > .1) ? (Math.random() * 0.8) / size / size + 0.1 : 0
return { position, size, distort, speed }
})
const secondSet = Array.from({ length: 5 }).map((_, index) => {
const dist = Math.random() * 3 + 1.5
const x = randomSign(Math.random() * dist)
const y = randomSign(Math.sqrt(dist * dist - x * x))
const z = randomSign(Math.random() * 2)
const position: [number, number, number] = [x, z, y]
const size = Math.random() * 0.2 + 0.05
const distort = (size > .1) ? Math.random() * .8 * size + 0.2 : 0
const speed = (size > .1) ? (Math.random() * 0.5) / size / size + 0.1 : 0
return { position, size, distort, speed }
})
return [...firstSet, ...secondSet]
}, [])
return (
<>
{blobsData.map(({ position, size, distort, speed }, index) => (
<Sphere key={index} visible position={position} args={[size, 16, 200]}>
<MeshDistortMaterial
color="#173E6F"
attach="material"
distort={distort} // Strength, 0 disables the effect (default=1)
speed={speed} // Speed (default=1)
roughness={0.2}
opacity={0.6}
transparent
/>
</Sphere>
))}
</>
)
}