Skip to content

Commit

Permalink
fix(composer):Camera and Light helper visibility toggling (#294)
Browse files Browse the repository at this point in the history
  • Loading branch information
jwills-jdubs authored Oct 26, 2022
1 parent 7899333 commit 4f62051
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 96 deletions.
6 changes: 3 additions & 3 deletions packages/scene-composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@
"coverageThreshold": {
"global": {
"lines": 77.56,
"statements": 76.71,
"functions": 77.64,
"branches": 63.57,
"statements": 76.73,
"functions": 77.68,
"branches": 63.67,
"branchesTrue": 100
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,14 @@ jest.mock('@react-three/fiber', () => {
};
});

jest.mock('@react-three/drei/core/useHelper', () => {
const originalModule = jest.requireActual('@react-three/drei/core/useHelper');
return {
...originalModule,
useHelper: jest.fn(() => ({})),
};
});

jest.mock('../../../../hooks/useSelectedNode', () => {
return jest.fn().mockReturnValue({
selectedSceneNodeRef: 'testRef',
});
});

jest.mock('../../../../hooks/useActiveCamera', () => {
return jest.fn();
return jest.fn().mockReturnValue({ activeCameraName: 'test-camera', setActiveCameraName: jest.fn() });
});

describe('CameraComponent', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,9 @@ exports[`CameraComponent should render correctly for orthographic camera 1`] = `
<group
name="CAMERA_COMPONENT_testRef"
>
<div
bottom="-3"
data-mocked="OrthographicCamera"
far="100"
left="-3"
near="0"
right="3"
top="3"
zoom="1"
>
<meshbasicmaterial
attach="material"
/>
</div>
<primitive
object="[object Object]"
/>
</group>
</div>
`;
Expand All @@ -36,17 +25,9 @@ exports[`CameraComponent should render correctly for perspective camera 1`] = `
<group
name="CAMERA_COMPONENT_testRef"
>
<div
aspect="1.7777777777777777"
data-mocked="PerspectiveCamera"
far="100"
fov="60"
near="0"
>
<meshbasicmaterial
attach="material"
/>
</div>
<primitive
object="[object Object]"
/>
</group>
</div>
`;
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as THREE from 'three';
import React, { useEffect } from 'react';
import { useHelper } from '@react-three/drei/core/useHelper';
import { OrthographicCamera, PerspectiveCamera } from '@react-three/drei';
import { Camera, useThree } from '@react-three/fiber';
import React, { useEffect, useMemo } from 'react';
import { useThree } from '@react-three/fiber';

import useLifecycleLogging from '../../../logger/react-logger/hooks/useLifecycleLogging';
import { ISceneNodeInternal, useEditorState, ICameraComponentInternal } from '../../../store';
Expand All @@ -20,14 +18,10 @@ interface ICameraComponentProps {
const CameraComponent: React.FC<ICameraComponentProps> = ({ node, component }: ICameraComponentProps) => {
const sceneComposerId = useSceneComposerId();
useLifecycleLogging('CameraComponent');
const camera = React.useRef<Camera>();

const cameraHelperRef = useHelper(camera, THREE.CameraHelper);
const { isEditing, getObject3DBySceneNodeRef } = useEditorState(sceneComposerId);
const size = useThree((state) => state.size);
const { fov, far, near, zoom } = component;

const { isEditing, isLoadingModel, getObject3DBySceneNodeRef } = useEditorState(sceneComposerId);

const { selectedSceneNodeRef } = useSelectedNode();
const { activeCameraName, setActiveCameraSettings } = useActiveCamera();

Expand All @@ -47,40 +41,21 @@ const CameraComponent: React.FC<ICameraComponentProps> = ({ node, component }: I
}
}, [activeCameraName]);

useEffect(() => {
if (cameraHelperRef.current) {
cameraHelperRef.current.visible = isEditing() && !isLoadingModel;
const cameraHelper = useMemo(() => {
let camera: THREE.OrthographicCamera | THREE.PerspectiveCamera;
if (component.cameraType === 'Orthographic') {
camera = new THREE.OrthographicCamera(-3, 3, 3, -3, near, far);
camera.zoom = zoom;
} else {
camera = new THREE.PerspectiveCamera(fov, size.width / size.height, near, far);
camera.zoom = zoom;
}
}, [isEditing, isLoadingModel, cameraHelperRef]);

let cameraNode: JSX.Element;
if (component.cameraType === 'Orthographic') {
cameraNode = (
<OrthographicCamera
// TODO: Make Editable once we expose this camera type
top={3}
bottom={-3}
left={-3}
right={3}
far={far}
zoom={1}
near={near}
ref={camera}
>
<meshBasicMaterial attach='material' />
</OrthographicCamera>
);
} else {
cameraNode = (
<PerspectiveCamera fov={fov} far={far} near={near} aspect={size.width / size.height} ref={camera}>
<meshBasicMaterial attach='material' />
</PerspectiveCamera>
);
}
return new THREE.CameraHelper(camera);
}, [component.cameraType]);

return (
<group name={getComponentGroupName(node.ref, 'CAMERA')}>
{isEditing() && selectedSceneNodeRef === node.ref && cameraNode}
{isEditing() && selectedSceneNodeRef === node.ref && <primitive object={cameraHelper} />}
</group>
);
};
Expand Down
25 changes: 18 additions & 7 deletions packages/scene-composer/src/hooks/useEditorHelper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as THREE from 'three';
import { MutableRefObject, useEffect, useRef } from 'react';
import { useThree, useFrame } from '@react-three/fiber';
import { useFrame } from '@react-three/fiber';

import { useStore } from '../store';
import { getFinalTransform } from '../utils/nodeUtils';

type Helper = THREE.Object3D & {
update: () => void;
Expand All @@ -28,24 +29,34 @@ export function useEditorHelper<T>(
) {
const helper = useRef<Helper>();

const scene = useThree((state) => state.scene);

useEffect(() => {
if (isEditing) {
if (proto && object3D.current) {
helper.current = new (proto as any)(object3D.current, ...args);

if (helper.current) {
scene.add(helper.current);
const helperTransform = {
position: helper.current.position.clone(),
rotation: helper.current.rotation.clone(),
scale: helper.current.scale.clone(),
};
const finalTransform = getFinalTransform(helperTransform, object3D.current);
const group = new THREE.Group();
group.add(helper.current);
group.position.copy(finalTransform.position);
group.rotation.copy(finalTransform.rotation);
group.scale.copy(finalTransform.scale);
object3D.current.add(group);
}
}
}

return () => {
if (helper.current) {
scene.remove(helper.current);
if (helper.current?.parent) {
object3D.current?.remove(helper.current.parent);
}
};
}, [isEditing, scene, proto, object3D, args]);
}, [isEditing, proto, object3D, args]);

useStore(sceneComposerId).subscribe((state) => {
if (helper.current) {
Expand Down
2 changes: 1 addition & 1 deletion packages/scene-composer/src/utils/nodeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ type Transform = {
* @param {THREE.Object3D} parent
* @returns {Transform} final transform based on all ancestors.
*/
const getFinalTransform = (transform: Transform, parent?: THREE.Object3D | null): Transform => {
export const getFinalTransform = (transform: Transform, parent?: THREE.Object3D | null): Transform => {
if (!parent) return transform;

const finalPosition = parent.worldToLocal(transform.position.clone());
Expand Down
20 changes: 8 additions & 12 deletions packages/scene-composer/tests/hooks/useEditorHelper.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,18 @@ import { useStore } from '../../src/store';

let container = null;
let helper = null;
let scene = null;

jest.mock('@react-three/fiber', () => {
const originalModule = jest.requireActual('@react-three/fiber');
return {
__esModule: true,
...originalModule,
useThree: jest.fn(() => scene),
useFrame: jest.fn(),
};
});

beforeEach(() => {
scene = {
add: jest.fn(),
remove: jest.fn(),
} as any;
jest.clearAllMocks();
container = document.createElement('div') as any;
document.body.appendChild(container as any);
});
Expand All @@ -36,11 +31,13 @@ afterEach(() => {
});

const object3D = new Object3D();
object3D.add = jest.fn();
object3D.remove = jest.fn();
const mutableRefObj = {
current: object3D,
};

class HelperType {}
class HelperType extends Object3D {}

interface TestComponentPros {
isEditing: boolean;
Expand All @@ -57,14 +54,13 @@ describe('return correct editor helper.', () => {
act(() => {
render(<TestComponent isEditing={true} sceneComposerId='sceneComposerId' />, container);
});
expect((scene as any).add).toBeCalledTimes(1);
expect(object3D.add).toBeCalledTimes(1);

act(() => {
unmountComponentAtNode(container as any);
});

expect((scene as any).remove).toBeCalledTimes(1);
expect((helper as any).current.visible).toBe(undefined);
expect(object3D.remove).toBeCalledTimes(1);
useStore('sceneComposerId').setState({});
expect((helper as any).current.visible).toBe(true);
});
Expand All @@ -73,9 +69,9 @@ describe('return correct editor helper.', () => {
act(() => {
render(<TestComponent isEditing={false} sceneComposerId='sceneComposerId' />, container);
});
expect((scene as any).add).toBeCalledTimes(0);
expect(object3D.add).toBeCalledTimes(0);
unmountComponentAtNode(container as any);
expect((scene as any).remove).toBeCalledTimes(0);
expect(object3D.remove).toBeCalledTimes(0);
expect((helper as any).current).toBe(undefined);
useStore('sceneComposerId').setState({});
expect((helper as any).current).toBe(undefined);
Expand Down

0 comments on commit 4f62051

Please sign in to comment.