Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import { useCallback, useContext, useMemo } from 'react';
import { useApp } from '@playcanvas/react/hooks';
import { Entity, GraphNode, Vec3 } from 'playcanvas';
import { CameraEntityContext } from './cameraEntityContext';
/** The camera-driving scripts this hook talks to. */
interface CameraDriver {
reset: (focus: Vec3, position: Vec3, horizontalNdcBias?: number) => void;
focusPoint: Vec3;
}
/** Scripts are added to entities dynamically, so they aren't on the engine's types. */
const scriptsOn = (node: GraphNode | null | undefined) =>
(node as unknown as { script?: { cameraControls?: CameraDriver; walkthroughCamera?: CameraDriver } } | null)?.script;
/**
* @param cameraEntity - The camera to drive. Callers above {@link CameraEntityContext} pass it
* directly; the ones beneath take it from the context, and anything with neither falls back to
* looking an entity named `camera` in the scene graph.
*/
export const useCameraPosition = (cameraEntity?: Entity | null) => {
const app = useApp();
const contextCamera = useContext(CameraEntityContext);
const resolvedCamera = cameraEntity ?? contextCamera;
const findCamera = useCallback(() => resolvedCamera ?? app.root.findByName('camera'), [app, resolvedCamera]);
/**
* The script driving the camera. WalkthroughCamera is mounted on the rig above the camera rather
* than on the camera entity, so that it poses a camera the walkthrough adopted from a host scene
* as readily as one of its own; CameraControls sits on the camera entity itself.
*/
const findControls = useCallback((camera: GraphNode | null): CameraDriver | undefined => {
const own = scriptsOn(camera);
return own?.cameraControls ?? own?.walkthroughCamera ?? scriptsOn(camera?.parent)?.walkthroughCamera;
}, []);
const setCamera = useCallback(
(focus: [number, number, number], position?: [number, number, number], horizontalNdcBias = 0) => {
const camera = findCamera();
const controls = findControls(camera);
if (controls && camera) {
// The extra bias arg is only honored by WalkthroughCamera; CameraControls ignores it.
controls.reset(new Vec3(focus), position ? new Vec3(position) : camera.getPosition(), horizontalNdcBias);
}
},
[findCamera, findControls]
);
const getCameraState = useCallback(() => {
const camera = findCamera();
const controls = findControls(camera);
if (camera && controls) {
const position = camera.getPosition();
const focusPoint = controls?.focusPoint;
return {
position: [position.x, position.y, position.z] as [number, number, number],
focus: [focusPoint.x, focusPoint.y, focusPoint.z] as [number, number, number],
};
}
return null;
}, [findCamera, findControls]);
return useMemo(() => ({ setCamera, getCameraState }), [setCamera, getCameraState]);
};
|