diff --git a/src/components/EditPage/PageData/Edit3d.jsx b/src/components/EditPage/PageData/Edit3d.jsx index 61b4213..2c0c0f7 100644 --- a/src/components/EditPage/PageData/Edit3d.jsx +++ b/src/components/EditPage/PageData/Edit3d.jsx @@ -7,7 +7,7 @@ import EditObjLoader from './Edit3d/EditObjLoader'; import EditGltfLoader from './Edit3d/EditGltfLoader'; import { objectSizeState } from '../../../store/toolState'; -function Edit3d({ objecturl }) { +function Edit3d({ objecturl, setObjectValue, pageRendering }) { const { camera } = useThree(); const dataSize = useRecoilValue(objectSizeState); @@ -50,7 +50,7 @@ function Edit3d({ objecturl }) { return ( <> - + ) : url?.extension === 'gltf' ? ( ) : null, )} diff --git a/src/components/EditPage/PageData/Edit3d/EditGltfLoader.jsx b/src/components/EditPage/PageData/Edit3d/EditGltfLoader.jsx index 496d9ec..edc6cb6 100644 --- a/src/components/EditPage/PageData/Edit3d/EditGltfLoader.jsx +++ b/src/components/EditPage/PageData/Edit3d/EditGltfLoader.jsx @@ -1,13 +1,23 @@ import React, { useRef, useEffect, useState } from 'react'; import { useFrame, useThree } from '@react-three/fiber'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'; -import { Color, Box3, Vector3, AxesHelper } from 'three'; +import { Color, Box3, Vector3 } from 'three'; import { useRecoilState } from 'recoil'; -import { DragControls } from 'three/examples/jsm/controls/DragControls'; + import { LodingState } from '../../../../store/modalState'; import useKeyDown from '../../../../hooks/EditPage/Handlers/useKeyDown'; +import useObjectDrag from '../../../../hooks/EditPage/useObjectDrag'; -function EditGltfLoader({ objecturl, size, x, y, z, setIsDragging }) { +function EditGltfLoader({ + objecturl, + size, + x, + y, + z, + setIsDragging, + setObjectValue, + pageRendering, +}) { const modelRef = useRef(); const { camera, scene, gl } = useThree(); const [loadingValue, setLoadingValue] = useRecoilState(LodingState); @@ -15,7 +25,6 @@ function EditGltfLoader({ objecturl, size, x, y, z, setIsDragging }) { const [initialScale, setInitialScale] = useState([1, 1, 1]); const movement = useRef({ forward: 0, right: 0, up: 0 }); const gltfUrl = typeof objecturl === 'string' ? objecturl : objecturl.gltf; - const controlsRef = useRef(); useEffect(() => { scene.background = new Color('#FFFFFF'); @@ -40,6 +49,8 @@ function EditGltfLoader({ objecturl, size, x, y, z, setIsDragging }) { setGltf(loadedGltf); setInitialScale([scale, scale, scale]); + loadedGltf.scene.position.set(x, y, z); // 초기 위치 설정 + setLoadingValue(false); }, undefined, @@ -62,37 +73,17 @@ function EditGltfLoader({ objecturl, size, x, y, z, setIsDragging }) { }); useKeyDown(movement, modelRef); - useEffect(() => { - if (modelRef.current) { - controlsRef.current = new DragControls( - [modelRef.current], - camera, - gl.domElement, - ); - - controlsRef.current.addEventListener('dragstart', (event) => { - event.object.material.emissive.set(0xaaaaaa); - setIsDragging(true); - }); - controlsRef.current.addEventListener('dragend', (event) => { - event.object.material.emissive.set(0x000000); - setIsDragging(false); - }); + useObjectDrag({ + modelRef, + camera, + domElement: gl.domElement, + setIsDragging, + setObjectValue, + pageRendering, + objectId: objecturl.id, + }); - return () => { - controlsRef.current.dispose(); - }; - } - return undefined; - }, [camera, gl]); - useEffect(() => { - const axesHelper = new AxesHelper(100); - scene.add(axesHelper); - return () => { - scene.remove(axesHelper); - }; - }, [scene]); return ( {gltf ? ( diff --git a/src/components/EditPage/PageData/Edit3d/EditObjLoader.jsx b/src/components/EditPage/PageData/Edit3d/EditObjLoader.jsx index 650164d..13860ea 100644 --- a/src/components/EditPage/PageData/Edit3d/EditObjLoader.jsx +++ b/src/components/EditPage/PageData/Edit3d/EditObjLoader.jsx @@ -6,10 +6,20 @@ import { useRecoilState } from 'recoil'; import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'; import { LodingState } from '../../../../store/modalState'; import useKeyDown from '../../../../hooks/EditPage/Handlers/useKeyDown'; - -function EditObjLoader({ objecturl, size, x, y, z }) { +import useObjectDrag from '../../../../hooks/EditPage/useObjectDrag'; + +function EditObjLoader({ + objecturl, + size, + x, + y, + z, + setIsDragging, + setObjectValue, + pageRendering, +}) { const modelRef = useRef(); - const { camera } = useThree(); + const { camera, gl } = useThree(); const [loadingValue, setLoadingValue] = useRecoilState(LodingState); const [object, setObject] = useState(null); const [initialScale, setInitialScale] = useState([1, 1, 1]); @@ -27,6 +37,7 @@ function EditObjLoader({ objecturl, size, x, y, z }) { setObject(null); return; } + setLoadingValue(true); const materialUrl = objecturl?.mtl || null; @@ -63,12 +74,6 @@ function EditObjLoader({ objecturl, size, x, y, z }) { loadModel(); }, [objecturl, setLoadingValue]); - useEffect(() => { - if (object) { - setLoadingValue(false); - } - }, [object, setLoadingValue]); - useFrame(() => { if (!modelRef.current) return; const speed = 0.1; @@ -83,6 +88,16 @@ function EditObjLoader({ objecturl, size, x, y, z }) { useKeyDown(movement, modelRef); + useObjectDrag({ + modelRef, + camera, + domElement: gl.domElement, + setIsDragging, + setObjectValue, + pageRendering, + objectId: objecturl.id, + }); + return ( {object ? ( diff --git a/src/components/Modal/atom/Test.jsx b/src/components/Modal/atom/Test.jsx deleted file mode 100644 index 187ecee..0000000 --- a/src/components/Modal/atom/Test.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { useThree } from '@react-three/fiber'; -import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'; -import { Color, Box3, Vector3 } from 'three'; -import { useRecoilState } from 'recoil'; -import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'; -import { LodingState } from '../../../store/modalState'; - -function TestOBJ({ objecturl, x, y, z }) { - const modelRef = useRef(); - const [loadingValue, setLoadingValue] = useRecoilState(LodingState); - const [object, setObject] = useState(null); - - const { scene } = useThree(); - - useEffect(() => { - scene.background = new Color('#FFFFFF'); - }, [scene]); - - useEffect(() => { - if (!objecturl?.obj) { - setLoadingValue(false); - setObject(null); - return; - } - setLoadingValue(true); - - const materialUrl = objecturl?.mtl || null; - const loadUrl = objecturl?.obj || null; - - const loadModel = () => { - const objLoader = new OBJLoader(); - const onLoad = (obj) => { - // 모델의 경계 계산 - const box = new Box3().setFromObject(obj); - const size = new Vector3(); - box.getSize(size); - const maxDimension = Math.max(size.x, size.y, size.z); - - const scale = 5 / maxDimension; - - obj.scale.set(scale, scale, scale); - - setObject(obj); - setLoadingValue(false); - }; - - if (materialUrl) { - const mtlLoader = new MTLLoader(); - mtlLoader.load(materialUrl, (materials) => { - materials.preload(); - objLoader.setMaterials(materials); - objLoader.load(loadUrl, onLoad); - }); - } else { - objLoader.load(loadUrl, onLoad); - } - }; - - loadModel(); - }, [objecturl, setLoadingValue]); - - useEffect(() => { - if (object) { - setLoadingValue(false); - } - }, [object, setLoadingValue]); - - return ( - - {object ? : null} - - ); -} - -export default TestOBJ; diff --git a/src/hooks/AddItem/useObject.js b/src/hooks/AddItem/useObject.js index bf1e79a..1806fa5 100644 --- a/src/hooks/AddItem/useObject.js +++ b/src/hooks/AddItem/useObject.js @@ -30,7 +30,7 @@ const useObject = () => { textures: texturesUrl, size: 0.25, x: getRandomCoordinate(), - y: getRandomCoordinate(), + y: 0, z: getRandomCoordinate(), }; const updatedImages = { diff --git a/src/hooks/EditPage/useItemValue.js b/src/hooks/EditPage/useItemValue.js index 8545fa7..7093728 100644 --- a/src/hooks/EditPage/useItemValue.js +++ b/src/hooks/EditPage/useItemValue.js @@ -1,4 +1,4 @@ -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useRecoilState } from 'recoil'; import { imageList, object3dState, @@ -7,13 +7,14 @@ import { } from '../../store/recoil'; const useItemValue = () => { - const objectValue = useRecoilValue(object3dState); + const [objectValue, setObjectValue] = useRecoilState(object3dState); const [shapeValue, setShapeValue] = useRecoilState(shapeList); const [textValue, setTextValue] = useRecoilState(textList); const [imgValue, setImgValue] = useRecoilState(imageList); return { objectValue, + setObjectValue, shapeValue, setShapeValue, textValue, diff --git a/src/hooks/EditPage/useObjectDrag.js b/src/hooks/EditPage/useObjectDrag.js new file mode 100644 index 0000000..4e73521 --- /dev/null +++ b/src/hooks/EditPage/useObjectDrag.js @@ -0,0 +1,89 @@ +import { useRef, useEffect } from 'react'; +import { DragControls } from 'three/examples/jsm/controls/DragControls'; +import { Vector3 } from 'three'; + +const useObjectDrag = ({ + modelRef, + camera, + domElement, + setIsDragging, + setObjectValue, + pageRendering, + objectId, + scaleFactor = 0.05, +}) => { + const controlsRef = useRef(); + const startPosition = useRef(new Vector3()); + const deltaPosition = useRef(new Vector3()); + + const handlePositionChange = (id, axis, value) => { + setObjectValue((prev) => ({ + ...prev, + [pageRendering]: prev[pageRendering].map((obj) => + obj.id === id ? { ...obj, [axis]: value } : obj, + ), + })); + }; + + useEffect(() => { + if (modelRef.current) { + controlsRef.current = new DragControls( + [modelRef.current], + camera, + domElement, + ); + + controlsRef.current.addEventListener('dragstart', (event) => { + if (event.object.material && event.object.material.emissive) { + event.object.material.emissive.set(0xaaaaaa); + } + if (typeof setIsDragging === 'function') { + setIsDragging(true); + } + startPosition.current.copy(event.object.position); + }); + + controlsRef.current.addEventListener('drag', (event) => { + const { object } = event; + object.position.y = 0; // y축 고정 + + deltaPosition.current.set( + (object.position.x - startPosition.current.x) * scaleFactor, + 0, + (object.position.z - startPosition.current.z) * scaleFactor, + ); + + object.position.x = startPosition.current.x + deltaPosition.current.x; + object.position.z = startPosition.current.z + deltaPosition.current.z; + + // 여기에서 실시간으로 위치를 저장 + handlePositionChange(objectId, 'x', Math.round(object.position.x)); + handlePositionChange(objectId, 'z', Math.round(object.position.z)); + }); + + controlsRef.current.addEventListener('dragend', (event) => { + const { object } = event; + + if (object.material && object.material.emissive) { + object.material.emissive.set(0x000000); + } + if (typeof setIsDragging === 'function') { + setIsDragging(false); + } + // 드래그가 끝난 후 최종 위치를 저장 + handlePositionChange(objectId, 'x', Math.round(object.position.x)); + handlePositionChange(objectId, 'z', Math.round(object.position.z)); + }); + + return () => { + controlsRef.current.dispose(); + }; + } + // 명시적으로 undefined 반환 + return undefined; + }, [camera, domElement, modelRef]); + + return controlsRef; +}; + +export default useObjectDrag; diff --git a/src/pages/EditPage.jsx b/src/pages/EditPage.jsx index edc4fc9..124453c 100644 --- a/src/pages/EditPage.jsx +++ b/src/pages/EditPage.jsx @@ -36,6 +36,7 @@ function EditPage() { const { objectValue, + setObjectValue, shapeValue, setShapeValue, textValue, @@ -187,7 +188,11 @@ function EditPage() { } } > - + ); }