diff --git a/CHANGELOG.md b/CHANGELOG.md index 7487f3c..c34840b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ The format is based on [Keep a Changelog](https://github.com/olivierlacan/keep-a ## [Unreleased] +### Added + +### Changed + +### Fixed + +### Removed + +## [1.1.1] - 2023-08-21 + +### Added + +### Changed + +### Fixed + +- Fix editor's preview viewer and data loader. (https://github.com/yushiang-demo/PanoToMesh/pull/29) + +### Removed + ## [1.1.0] - 2023-08-19 ### Added @@ -51,6 +71,7 @@ The format is based on [Keep a Changelog](https://github.com/olivierlacan/keep-a Code which doesn't make changes with pull requests won't be recorded. [unreleased]: https://github.com/yushiang-demo/PanoToMesh/compare/v1.0.0...HEAD +[1.1.1]: https://github.com/yushiang-demo/PanoToMesh/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/yushiang-demo/PanoToMesh/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/yushiang-demo/PanoToMesh/compare/v0.0.0...v1.0.0 [0.0.0]: https://github.com/yushiang-demo/PanoToMesh/releases/tag/v0.0.0 diff --git a/README.md b/README.md index 9975091..957e72d 100644 --- a/README.md +++ b/README.md @@ -5,25 +5,28 @@ yarn yarn dev ``` -## Introduction +# Introduction -### Demo +## Demo + +- [Editor](https://pano-to-mesh.vercel.app/editor#eNqrVkrLyc8vilSyMtBRSk7NzMnMSwdyjHSUchIr80tLjFyUrKJjdZQKEvPyixJzE5WslJQQPP-izPTMPKAKAx1DPVMdg9haABJxGUo#eNpdkMtqwzAQRf9FayGNNJJG8jKU7JosCoUQjFHc2HFxbeOoj1D671W8qeniLGY43Hl8s6Yfx_nACuCsPnd9N7S5YChQMc76eBvfk35gxfEIQjm0zhOhRqMByHAQloxeU_IsBgrWYgBEY0mbcPcC4QqzeA6DAavBGAfBOcvvLcA1i_dPW-YGoBW-LDmb4jDO8S3m_S8pTddCynhVokn1yyCGc5IfWr5OrQQlfZDgJXmpAKDaVsrnDPKeqv1m7rFRig5dPQXztLlsPx9vX8_taWd3TX0SOYH9zdrPXdsN-T_AlcgHlD-_jHJYMQ) +- [Viewer](https://pano-to-mesh.vercel.app#eNqrVkrLyc8vilSyMtBRSk7NzMnMSwdyjHSUchIr80tLjFyUrKJjdZQKEvPyixJzE5WslJQQPP-izPTMPKAKAx1DPVMdg9haABJxGUo#eNpdkMtqwzAQRf9FayGNNJJG8jKU7JosCoUQjFHc2HFxbeOoj1D671W8qeniLGY43Hl8s6Yfx_nACuCsPnd9N7S5YChQMc76eBvfk35gxfEIQjm0zhOhRqMByHAQloxeU_IsBgrWYgBEY0mbcPcC4QqzeA6DAavBGAfBOcvvLcA1i_dPW-YGoBW-LDmb4jDO8S3m_S8pTddCynhVokn1yyCGc5IfWr5OrQQlfZDgJXmpAKDaVsrnDPKeqv1m7rFRig5dPQXztLlsPx9vX8_taWd3TX0SOYH9zdrPXdsN-T_AlcgHlD-_jHJYMQ) ![image](./Demo.png) > Demo image is provided by: > https://as1.ftcdn.net/v2/jpg/01/89/08/78/1000_F_189087887_OBrl3f117Yicp94SBhFwMyxVgbN5Nfcb.jpg -### Features +## Features -- Load panorama from local and url. -- Annotate 3D layout from a panorama. -- Preview 3D mesh. -- Export 3D mesh and texture. +- Load panorama from local or url. +- Annotate layout from a panorama and preview 3D Mesh. +- Save 3D mesh and texture to local. +- Share result of viewer and editor with data embedded in URL. -### Algorithm +## Algorithm -Following flowchart describes how to get geometry layout and texture. +## Mesh and Texture ```mermaid flowchart LR diff --git a/apps/editor.js b/apps/editor.js index fbed6b0..3083e37 100644 --- a/apps/editor.js +++ b/apps/editor.js @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useState } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; import { Loaders, @@ -36,14 +36,15 @@ const getCurrentFormattedTime = () => { }; const dev = process.env.NODE_ENV === "development"; -const Editor = ({ src }) => { +const Editor = ({ data }) => { + const canvas3DRef = useRef(null); const textureCanvasRef = useRef(null); const [preview, setPreview] = useState(false); - const [imageSrc, setImageSrc] = useState(src); + const [imageSrc, setImageSrc] = useState(data.panorama); const panorama = Loaders.useTexture({ src: imageSrc }); - const [panoramaOrigin, setPanoramaOrigin] = useState([0, 1.5, 0]); - const [floorY] = useState(0.0); - const [ceilingY, setCeilingY] = useState(2.0); + const [panoramaOrigin, setPanoramaOrigin] = useState(data.panoramaOrigin); + const [floorY] = useState(data.floorY); + const [ceilingY, setCeilingY] = useState(data.ceilingY); const geometryInfo = useMemo( () => ({ floorY, @@ -51,7 +52,8 @@ const Editor = ({ src }) => { }), [floorY, ceilingY] ); - const { layout2D, eventHandlers } = useClick2AddWalls({ + const { layout2D, eventHandlers, imageCoord } = useClick2AddWalls({ + defaultData: data.layout2D, panoramaOrigin, geometryInfo, selectThresholdPixel: 5, @@ -66,6 +68,7 @@ const Editor = ({ src }) => { }; const hash = useStoreDataToHash({ ...props, + layout2D: imageCoord, panorama: imageSrc, }); @@ -107,6 +110,21 @@ const Editor = ({ src }) => { } }; + useEffect(() => { + if (canvas3DRef.current) { + const removeSceneEvent = canvas3DRef.current.scene.onChange( + ({ target }) => { + const scene = target.getScene(); + canvas3DRef.current.cameraControls.focus(scene); + } + ); + + return () => { + removeSceneEvent(); + }; + } + }, [preview]); + return ( @@ -168,7 +186,7 @@ const Editor = ({ src }) => { ) : ( - + )} @@ -177,4 +195,20 @@ const Editor = ({ src }) => { ); }; -export default Editor; +const PropsParser = ({ data }) => { + return ( + + ); +}; + +export default PropsParser; diff --git a/apps/viewer.js b/apps/viewer.js index f8fa53b..fa4967e 100644 --- a/apps/viewer.js +++ b/apps/viewer.js @@ -1,16 +1,30 @@ -import React, { useEffect, useRef } from "react"; +import React, { useEffect, useRef, useMemo } from "react"; import { Loaders, ThreeCanvas, PanoramaProjectionMesh } from "../three"; +import useClick2AddWalls from "../hooks/useClick2AddWalls"; const dev = process.env.NODE_ENV === "development"; -const Viewer = ({ ceilingY, floorY, layout2D, panorama, panoramaOrigin }) => { +const Viewer = ({ data }) => { const threeRef = useRef(null); + + const geometryInfo = useMemo( + () => ({ + floorY: data.floorY, + ceilingY: data.ceilingY, + }), + [data] + ); + const { layout2D } = useClick2AddWalls({ + defaultData: data.layout2D, + panoramaOrigin: data.panoramaOrigin, + geometryInfo, + selectThresholdPixel: 5, + }); + const textureMeshProps = { - ceilingY, - floorY, + ...data, layout2D, - panorama: Loaders.useTexture({ src: panorama }), - panoramaOrigin, + panorama: Loaders.useTexture({ src: data.panorama }), }; useEffect(() => { @@ -33,7 +47,7 @@ const Viewer = ({ ceilingY, floorY, layout2D, panorama, panoramaOrigin }) => { const PropsParser = ({ data }) => { if (!data) return null; - return ; + return ; }; export default PropsParser; diff --git a/hooks/useClick2AddWalls.js b/hooks/useClick2AddWalls.js index 507f4df..82a6edf 100644 --- a/hooks/useClick2AddWalls.js +++ b/hooks/useClick2AddWalls.js @@ -15,13 +15,14 @@ const pointSelector = (x, y, unitParser, threshold) => { }; const useClick2AddWalls = ({ + defaultData, panoramaOrigin, geometryInfo, selectThresholdPixel, }) => { const [dragging, setDragging] = useState(false); const [previewImageCoord, setPreviewImageCoord] = useState(null); - const [imageCoord, setImageCoord] = useState([]); + const [imageCoord, setImageCoord] = useState(defaultData || []); const [layout2D, setLayout2D] = useState([]); const parser2DCeilingCoordToFloorCoord = ([normalizedX, normalizedY]) => { @@ -132,6 +133,7 @@ const useClick2AddWalls = ({ }, [imageCoord, previewImageCoord, parseMousePointTo3D]); return { + imageCoord, layout2D, eventHandlers: { onMouseDown, diff --git a/hooks/useHash.js b/hooks/useHash.js index 6453112..a574592 100644 --- a/hooks/useHash.js +++ b/hooks/useHash.js @@ -15,6 +15,7 @@ export const useStoreDataToHash = (data) => { }; export const useDecodedHash = () => { + const [isLoading, setIsLoading] = useState(true); const [data, setData] = useState(null); useEffect(() => { @@ -27,7 +28,8 @@ export const useDecodedHash = () => { } catch (e) { console.error(e); } + setIsLoading(false); }, []); - return data; + return { data, isLoading }; }; diff --git a/pages/editor.js b/pages/editor.js index 6580327..4fbe49a 100644 --- a/pages/editor.js +++ b/pages/editor.js @@ -1,8 +1,10 @@ import React from "react"; import Editor from "../apps/editor"; +import { useDecodedHash } from "../hooks/useHash"; const Apps = () => { - return ; + const { data, isLoading } = useDecodedHash(); + return !isLoading && ; }; export default Apps; diff --git a/pages/index.js b/pages/index.js index 4087ecf..3f5c0e7 100644 --- a/pages/index.js +++ b/pages/index.js @@ -3,8 +3,8 @@ import Viewer from "../apps/viewer"; import { useDecodedHash } from "../hooks/useHash"; const Apps = () => { - const data = useDecodedHash(); - return ; + const { data, isLoading } = useDecodedHash(); + return !isLoading && ; }; export default Apps;