Skip to content

Instantly share code, notes, and snippets.

@drcmda
Created August 3, 2020 16:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drcmda/61c404faf645d0636dd3ea1ef7d8cee9 to your computer and use it in GitHub Desktop.
Save drcmda/61c404faf645d0636dd3ea1ef7d8cee9 to your computer and use it in GitHub Desktop.
Orbit contols using react-spring and react-use-gesture
// https://github.com/react-spring/react-three-fiber/discussions/440#discussioncomment-20655
// https://github.com/youroff/planet_disco
import React, { useRef, useEffect } from 'react'
import { Vector3, Matrix4, Quaternion, MathUtils } from 'three'
import { useThree, useFrame } from 'react-three-fiber'
import { useGesture } from 'react-use-gesture'
import { useSpring, a } from '@react-spring/three'
function Controls({ speed = 5, maxDist = 4, minDist = 1.5, distance, phi = Math.PI / 2, theta = 2 * Math.PI }) {
const camera = useRef()
const { gl, setDefaultCamera } = useThree()
const [{ props }, set] = useSpring(() => ({ props: [distance || maxDist, phi, theta] }))
const bind = useGesture(
{
onDrag: ({ dragging, velocities: [x, y] }) => {
if (dragging) {
const [distance, phi, theta] = props.get()
const k = Math.sqrt(distance + 100) / speed
const p = MathUtils.clamp(phi - y / k, 0.1, Math.PI - 0.1)
const t = theta - x / k
set({ props: [distance, p, t] })
}
},
onPinch: ({ movement: [y] }) => {
const [distance, phi, theta] = props.get()
const k = 1 + (Math.sign(y) * Math.min(8 * Math.abs(y), 20)) / (20 + 10)
const d = MathUtils.clamp(k * distance, minDist, maxDist)
set({ props: [d, phi, theta] })
},
onWheel: ({ velocities: [, y] }) => {
const [distance, phi, theta] = props.get()
const k = 1 + (Math.sign(y) * Math.min(8 * Math.abs(y), 20)) / (20 + 10)
const d = MathUtils.clamp(k * distance, minDist, maxDist)
set({ props: [d, phi, theta] })
},
},
{ domTarget: gl.domElement },
)
useEffect(bind, [bind])
useEffect(() => void setDefaultCamera(camera.current), [])
useFrame(() => camera.current.updateMatrixWorld())
useEffect(() => void set({ props: [distance, phi, theta], delay: 400 }), [distance, phi, theta])
return (
<a.perspectiveCamera
ref={camera}
position={props.to(calcPosition)}
quaternion={props.to((distance, phi, theta) => {
const poi = getPoi(phi, theta)
const pos = new Vector3(...calcPosition(distance, phi, theta))
const up = pos.clone().sub(poi).dot(new Vector3(poi.x, 0, poi.z))
const m = new Matrix4().lookAt(pos, poi, new Vector3(0, Math.sign(up), 0))
return new Quaternion().setFromRotationMatrix(m).toArray()
})}
/>
)
}
const getPoi = (phi, theta) => new Vector3().setFromSphericalCoords(1, phi, theta)
const calcPosition = (d, phi, theta) => {
const ext = new Vector3().setFromSphericalCoords(
d - 1,
phi + ((1 - (d - minDist) / (maxDist - minDist)) * Math.PI) / 3,
theta,
)
return getPoi(phi, theta).add(ext).toArray()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment