React Three Fiber: Toggling between cameras unexpected behaviour

Hello legends, @drcmda this might be your wheelhouse.

I’m battling with the task of toggling between cameras in R3F. There is a sandbox provided below.

I have a standard <OrbitControls> that lets me rotate around a skateboard

I also have a perspective camera that I can render that zooms in on a detail

It appears that I can safely jump TO the perspective camera, but the OrbitControls are messed up when I jump back. I know this is something to do with the correct recipe of makeDefault and the fact that:

All controls react to the default camera. If you have a <PerspectiveCamera makeDefault /> in your scene, they will control it. If you need to inject an imperative camera or one that isn’t the default, use the camera prop: <OrbitControls camera={MyCamera} />

Here’s a distilled sandbox that I might be able to get some clarification on? I feel like I’ve tried a few approaches. Thanks!

it behaved so strange … sure enough this is a bug. pc is supposed to set itself as the default and restore the old camera on unmount. it’s fixed, should come live in a few minutes: fix: perspective & orthographic camera have confused side effects · pmndrs/drei@0f44e35 · GitHub

ps. makeDefault on the controls … when you set it it will write these controls into “state.controls” that’s all. there are a few components that will either change the camera (camera-shake, …) or conflict with event handlers (for instance transform-controls and orbit-controls at the same time). if you have two conflicting camera-controls, or more, then makeDefault allows them to “know” one another. for instance transform-controls can then disable orbit-controls temporarily when you drag one of its handles, or camera-shake can call “controls.update()” after it applied a shake.

1 Like

it’s up, seems OK now R3F Camera Toggling (forked) - CodeSandbox

1 Like

ps, just in case, we added setAzimuthalAngle and setPolarAngle to orbitcontrols so that you can animate to a certain degree by code. Controlling OrbitControls - CodeSandbox

2 Likes

Thanks a bunch mate, excellent!

In regards to controlling the angles, do you see any obvious re-rendering issues mixing useSpring and useThree like this? It does work really well though.

Ignore [distance, setDistance] for now, have a seperate question below the code block

const ActiveCamera: React.FC<{ cameraCoordinates: Camera | null }> = ({
  cameraCoordinates,
}) => {
  const {controls} = useThree();
  const [limitedControl, setLimitedControl] = useState<boolean>(false);
  const [distance, setDistance] = useState<number>(1);

  useSpring({
    azimuthal: cameraCoordinates ? cameraCoordinates.azimuthal : 0,
    polar: cameraCoordinates ? cameraCoordinates.polar : 1.5708,
    distance: cameraCoordinates ? 0.5 : 1,
    onChange: (state) => {
      const { azimuthal, polar, distance: animDistance } = state.value;
      (controls as IOrbitControls)?.setAzimuthalAngle(azimuthal);
      (controls as IOrbitControls)?.setPolarAngle(polar);
      setDistance(animDistance);
    },
    config: cameraCoordinates ? config.stiff : config.wobbly,
    onStart: () => {setLimitedControl(false)},
    onRest: () => {if (cameraCoordinates) setLimitedControl(true)},
  });

  return (
    <OrbitControls
      minDistance={distance}
      maxDistance={distance}
      makeDefault
      enableZoom={false}
      rotateSpeed={limitedControl ? 0.1 : 1}
    />
  );
};

So yeah, about the [distance, setDistance], absolutely illegal to call useState that often in a component, but can’t for the life of me figure out how else to animate the distance of the camera to it’s target? I tried animating target itself, but there were some issues

You might find this sansdbox useful as well, I set it up to switch between orthographic and perspective but it should work fine with two perspective cameras. I found OrbitControls a bit problematic when doing complex transitions like this so I’m using CameraControls instead.

1 Like
const r = useRef()

onChange: state => {
  ...
  r.current.minDistance = animDistance
  r.current.maxDistance = animDistance
}

<OrbitControls ref={r} ... />

like @looeee said, probably better to look into camera controls if it gets too complex, orbit controls isn’t the most friendly thing to extend.

1 Like

Thanks @drcmda @looeee , I was thinking of using a ref for a number but didn’t think to add OrbitControls as a ref itself, was having trouble with this and types and realised that I’d already solved that.

I’d love to play around with more in-depth camera options, I’m familiar with orbit controls and for this project it seems to be what I want, here’s a small run through, you can see why I need to animate the min/max distance, and this is actually running the useState, which is surprisingly performant but I also need to be able to live with myself…

setState is by far not as slow as it’s sometimes made out to be, just a bit of diffing, but if you can avoid it somehow you’ll for sure have more headroom on older mobile devices and weaker platforms in general. looks great btw.

Is the skateboard visualization open source?

its not showing any codeSandbox, is it removed?
i am also having the same prob…