diff --git a/packages/scene-composer/package.json b/packages/scene-composer/package.json
index d3f1dacb7..7aae6b584 100644
--- a/packages/scene-composer/package.json
+++ b/packages/scene-composer/package.json
@@ -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
}
}
diff --git a/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/CameraComponent.spec.tsx b/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/CameraComponent.spec.tsx
index b841c2ce0..2a1a812b9 100644
--- a/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/CameraComponent.spec.tsx
+++ b/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/CameraComponent.spec.tsx
@@ -28,14 +28,6 @@ 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',
@@ -43,7 +35,7 @@ jest.mock('../../../../hooks/useSelectedNode', () => {
});
jest.mock('../../../../hooks/useActiveCamera', () => {
- return jest.fn();
+ return jest.fn().mockReturnValue({ activeCameraName: 'test-camera', setActiveCameraName: jest.fn() });
});
describe('CameraComponent', () => {
diff --git a/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/__snapshots__/CameraComponent.spec.tsx.snap b/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/__snapshots__/CameraComponent.spec.tsx.snap
index ca75ea67d..3729673e7 100644
--- a/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/__snapshots__/CameraComponent.spec.tsx.snap
+++ b/packages/scene-composer/src/components/three-fiber/CameraComponent/__tests__/__snapshots__/CameraComponent.spec.tsx.snap
@@ -13,20 +13,9 @@ exports[`CameraComponent should render correctly for orthographic camera 1`] = `
-
-
-
+
`;
@@ -36,17 +25,9 @@ exports[`CameraComponent should render correctly for perspective camera 1`] = `
-
-
-
+
`;
diff --git a/packages/scene-composer/src/components/three-fiber/CameraComponent/index.tsx b/packages/scene-composer/src/components/three-fiber/CameraComponent/index.tsx
index 5048a6d16..108aeedba 100644
--- a/packages/scene-composer/src/components/three-fiber/CameraComponent/index.tsx
+++ b/packages/scene-composer/src/components/three-fiber/CameraComponent/index.tsx
@@ -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';
@@ -20,14 +18,10 @@ interface ICameraComponentProps {
const CameraComponent: React.FC = ({ node, component }: ICameraComponentProps) => {
const sceneComposerId = useSceneComposerId();
useLifecycleLogging('CameraComponent');
- const camera = React.useRef();
-
- 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();
@@ -47,40 +41,21 @@ const CameraComponent: React.FC = ({ 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 = (
-
-
-
- );
- } else {
- cameraNode = (
-
-
-
- );
- }
+ return new THREE.CameraHelper(camera);
+ }, [component.cameraType]);
return (
- {isEditing() && selectedSceneNodeRef === node.ref && cameraNode}
+ {isEditing() && selectedSceneNodeRef === node.ref && }
);
};
diff --git a/packages/scene-composer/src/hooks/useEditorHelper.ts b/packages/scene-composer/src/hooks/useEditorHelper.ts
index 608141ca9..beef2fcf6 100644
--- a/packages/scene-composer/src/hooks/useEditorHelper.ts
+++ b/packages/scene-composer/src/hooks/useEditorHelper.ts
@@ -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;
@@ -28,24 +29,34 @@ export function useEditorHelper(
) {
const helper = useRef();
- 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) {
diff --git a/packages/scene-composer/src/utils/nodeUtils.ts b/packages/scene-composer/src/utils/nodeUtils.ts
index 547ce8d56..4db361aaf 100644
--- a/packages/scene-composer/src/utils/nodeUtils.ts
+++ b/packages/scene-composer/src/utils/nodeUtils.ts
@@ -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());
diff --git a/packages/scene-composer/tests/hooks/useEditorHelper.spec.tsx b/packages/scene-composer/tests/hooks/useEditorHelper.spec.tsx
index 2c1aeb141..cdf5d832f 100644
--- a/packages/scene-composer/tests/hooks/useEditorHelper.spec.tsx
+++ b/packages/scene-composer/tests/hooks/useEditorHelper.spec.tsx
@@ -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);
});
@@ -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;
@@ -57,14 +54,13 @@ describe('return correct editor helper.', () => {
act(() => {
render(, 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);
});
@@ -73,9 +69,9 @@ describe('return correct editor helper.', () => {
act(() => {
render(, 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);