Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Helpers to track OutlinePass selection
function Scene() {
return (
<Canvas>
<OutlineItemsProvider>
<SomeMesh />
<SomeMesh />
<SomeMesh />
<Effects />
</OutlineItemsProvider>
</Canvas>
);
}
function Effects() {
const { gl, scene, camera, size } = useThree();
const composer = useRef<EffectComposer>();
const aspect = useMemo(() => new Vector2(size.width, size.height), [size]);
const { outlineItems } = useContext(OutlineContext);
useEffect(() => composer.current?.setSize(size.width, size.height), [size]);
useFrame(() => composer.current?.render(), 1);
return (
<effectComposer ref={composer} args={[gl]}>
<renderPass attachArray="passes" args={[scene, camera]} />
<outlinePass
attachArray="passes"
args={[aspect, scene, camera]}
selectedObjects={outlineItems}
/>
</effectComposer>
);
}
function SomeMesh() {
const ref = useRef<Object3D>();
return (
<mesh
ref={ref}
{...setOutlineOnHover(ref)}
/>
);
}
import {
createContext,
Dispatch,
MutableRefObject,
ReactNode,
SetStateAction,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { Object3D } from 'three';
export interface OutlineItemsContext {
outlineItems?: Array<Object3D>;
setOutlineItems?: Dispatch<SetStateAction<Array<Object3D>>>;
}
export const OutlineContext = createContext<OutlineItemsContext>({});
/**
* Set on the props of a mesh to trigger an outline effect whenever the mouse
* is hovered over it.
*
* @note The mesh must be beneath an OutlineItemsProvider
*/
export function setOutlineOnHover(ref: MutableRefObject<Object3D | undefined>) {
const { setOutlineItems } = useContext(OutlineContext);
const onPointerOver = useCallback(() => {
if (setOutlineItems) setOutlineItems((items) => [...items, ref.current!]);
}, []);
const onPointerOut = useCallback(() => {
if (setOutlineItems)
setOutlineItems((items) => items.filter((item) => item !== ref.current!));
}, []);
return { onPointerOver, onPointerOut };
}
/**
* Wrap your scene in this provider to make it easier to assign outline items.
*/
export const OutlineItemsProvider = ({ children }: { children: ReactNode }) => {
const [outlineItems, setOutlineItems] = useState<Array<Object3D>>([]);
return (
<OutlineContext.Provider value={{ outlineItems, setOutlineItems }}>
{children}
</OutlineContext.Provider>
);
};
@CharlieHess

This comment has been minimized.

Copy link
Owner Author

@CharlieHess CharlieHess commented Aug 8, 2020

If you're trying to keep your effects chain isolated from the rest of your code, tracking outline items for the OutlinePass selectedObjects is a huge pain. Here are some helpers that use React.Context to allow setting the selectedObjects without reaching into your effects chain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment