155 lines
3.9 KiB
TypeScript
155 lines
3.9 KiB
TypeScript
import {editable as e, bindToCanvas} from '@theatre/plugin-r3f'
|
|
import {PerspectiveCamera} from '@react-three/drei'
|
|
import {getProject} from '@theatre/core'
|
|
import * as THREE from 'three'
|
|
import React, {useState, useEffect, useRef} from 'react'
|
|
import type {Color} from '@react-three/fiber'
|
|
import {Canvas, useFrame} from '@react-three/fiber'
|
|
import {softShadows, Shadow} from '@react-three/drei'
|
|
import type {DirectionalLight} from 'three'
|
|
import type {$FixMe} from '../../../plugin-r3f/src/types'
|
|
|
|
const ECamera = e(PerspectiveCamera, 'perspectiveCamera')
|
|
|
|
// Soft shadows are expensive, comment and refresh when it's too slow
|
|
softShadows()
|
|
|
|
// credit: https://codesandbox.io/s/camera-pan-nsb7f
|
|
|
|
function Button() {
|
|
const vec = new THREE.Vector3()
|
|
const light = useRef<DirectionalLight>(undefined as any)
|
|
const [active, setActive] = useState(false)
|
|
const [zoom, set] = useState(true)
|
|
useEffect(
|
|
() => void (document.body.style.cursor = active ? 'pointer' : 'auto'),
|
|
[active],
|
|
)
|
|
|
|
useFrame((state) => {
|
|
const step = 0.1
|
|
const camera = state.camera as THREE.PerspectiveCamera
|
|
camera.fov = (THREE as $FixMe).MathUtils.lerp(
|
|
camera.fov,
|
|
zoom ? 10 : 42,
|
|
step,
|
|
)
|
|
camera.position.lerp(
|
|
vec.set(zoom ? 25 : 10, zoom ? 1 : 5, zoom ? 0 : 10),
|
|
step,
|
|
)
|
|
state.camera.lookAt(0, 0, 0)
|
|
state.camera.updateProjectionMatrix()
|
|
|
|
light.current.position.lerp(
|
|
vec.set(zoom ? 4 : 0, zoom ? 3 : 8, zoom ? 3 : 5),
|
|
step,
|
|
)
|
|
})
|
|
|
|
return (
|
|
<e.mesh
|
|
receiveShadow
|
|
castShadow
|
|
onClick={() => set(!zoom)}
|
|
onPointerOver={() => setActive(true)}
|
|
onPointerOut={() => setActive(false)}
|
|
uniqueName="The Button"
|
|
>
|
|
<sphereBufferGeometry args={[0.75, 64, 64]} />
|
|
<meshPhysicalMaterial
|
|
color={active ? 'purple' : '#e7b056'}
|
|
clearcoat={1}
|
|
clearcoatRoughness={0}
|
|
/>
|
|
<Shadow
|
|
position-y={-0.79}
|
|
rotation-x={-Math.PI / 2}
|
|
opacity={0.6}
|
|
scale={[0.8, 0.8, 1]}
|
|
/>
|
|
<directionalLight
|
|
ref={light}
|
|
castShadow
|
|
intensity={1.5}
|
|
shadow-camera-far={70}
|
|
/>
|
|
</e.mesh>
|
|
)
|
|
}
|
|
|
|
function Plane({
|
|
color,
|
|
uniqueName,
|
|
...props
|
|
}: {color: Color; uniqueName: string} & Parameters<typeof e.mesh>[0]) {
|
|
return (
|
|
<e.mesh receiveShadow castShadow {...props} uniqueName={uniqueName}>
|
|
<boxBufferGeometry />
|
|
<meshStandardMaterial color={color as any} />
|
|
</e.mesh>
|
|
)
|
|
}
|
|
|
|
function App() {
|
|
return (
|
|
<div>
|
|
<Canvas
|
|
// @ts-ignore
|
|
shadowMap
|
|
onCreated={bindToCanvas({
|
|
sheet: getProject('Example project').sheet('R3F-Canvas'),
|
|
})}
|
|
>
|
|
<ECamera makeDefault uniqueName="Camera" />
|
|
<ambientLight intensity={0.4} />
|
|
<e.pointLight
|
|
position={[-10, -10, 5]}
|
|
intensity={2}
|
|
color="#ff20f0"
|
|
uniqueName="Light 1"
|
|
/>
|
|
<e.pointLight
|
|
position={[0, 0.5, -1]}
|
|
distance={1}
|
|
intensity={2}
|
|
color="#e4be00"
|
|
uniqueName="Light 2"
|
|
/>
|
|
<group position={[0, -0.9, -3]}>
|
|
<Plane
|
|
color="hotpink"
|
|
rotation-x={-Math.PI / 2}
|
|
position-z={2}
|
|
scale={[4, 20, 0.2]}
|
|
uniqueName="plane1"
|
|
/>
|
|
<Plane
|
|
color="#e4be00"
|
|
rotation-x={-Math.PI / 2}
|
|
position-y={1}
|
|
scale={[4.2, 0.2, 4]}
|
|
uniqueName="plane2"
|
|
/>
|
|
<Plane
|
|
color="#736fbd"
|
|
rotation-x={-Math.PI / 2}
|
|
position={[-1.7, 1, 3.5]}
|
|
scale={[0.5, 4, 4]}
|
|
uniqueName="plane3"
|
|
/>
|
|
<Plane
|
|
color="white"
|
|
rotation-x={-Math.PI / 2}
|
|
position={[0, 4.5, 3]}
|
|
scale={[2, 0.03, 4]}
|
|
uniqueName="plane4"
|
|
/>
|
|
</group>
|
|
<Button />
|
|
</Canvas>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default App
|