From d926809decdbaacb2947bc8648fa6d39c70d6711 Mon Sep 17 00:00:00 2001 From: Poovizhi99 Date: Thu, 26 Jun 2025 15:11:52 +0530 Subject: [PATCH] Add scene context and animation handling to AssetProperties and Model components - Enhanced animation handling in Model component with animation state management. - Updated useAssetStore to support multiple animations for assets. --- .../layout/sidebarRight/SideBarRight.tsx | 8 ++ .../properties/AssetProperties.tsx | 26 ++++++ .../builder/asset/models/model/model.tsx | 84 +++++++++++++++++-- app/src/store/builder/useAssetStore.ts | 9 +- 4 files changed, 117 insertions(+), 10 deletions(-) diff --git a/app/src/components/layout/sidebarRight/SideBarRight.tsx b/app/src/components/layout/sidebarRight/SideBarRight.tsx index d6413bc..5fbf079 100644 --- a/app/src/components/layout/sidebarRight/SideBarRight.tsx +++ b/app/src/components/layout/sidebarRight/SideBarRight.tsx @@ -31,6 +31,7 @@ import AisleProperties from "./properties/AisleProperties"; import WallProperties from "./properties/WallProperties"; import { useBuilderStore } from "../../../store/builder/useBuilderStore"; import SelectedWallProperties from "./properties/SelectedWallProperties"; +import { useSceneContext } from "../../../modules/scene/sceneContext"; const SideBarRight: React.FC = () => { const { activeModule } = useModuleStore(); @@ -38,12 +39,15 @@ const SideBarRight: React.FC = () => { const { toolMode } = useToolMode(); const { subModule, setSubModule } = useSubModuleStore(); const { selectedFloorItem } = useSelectedFloorItem(); + const { selectedWall } = useBuilderStore(); const { selectedEventData } = useSelectedEventData(); const { selectedEventSphere } = useSelectedEventSphere(); const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore(); const { isVersionSaved } = useSaveVersion(); + + // Reset activeList whenever activeModule changes useEffect(() => { if (activeModule !== "simulation") setSubModule("properties"); @@ -142,6 +146,8 @@ const SideBarRight: React.FC = () => { )} + + {/* process builder */} {!viewVersionHistory && subModule === "properties" && @@ -170,6 +176,7 @@ const SideBarRight: React.FC = () => {
+
)} @@ -224,6 +231,7 @@ const SideBarRight: React.FC = () => { )} )} + {/* realtime visualization */} {activeModule === "visualization" && } diff --git a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx index 4223129..da6e2e3 100644 --- a/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx +++ b/app/src/components/layout/sidebarRight/properties/AssetProperties.tsx @@ -5,6 +5,7 @@ import { RemoveIcon } from "../../../icons/ExportCommonIcons"; import PositionInput from "../customInput/PositionInputs"; import RotationInput from "../customInput/RotationInput"; import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store"; +import { useSceneContext } from "../../../../modules/scene/sceneContext"; interface UserData { id: number; // Unique identifier for the user data @@ -18,6 +19,9 @@ const AssetProperties: React.FC = () => { const { selectedFloorItem } = useSelectedFloorItem(); const { objectPosition } = useObjectPosition(); const { objectRotation } = useObjectRotation(); + const { assetStore } = useSceneContext(); + const { assets, setCurrentAnimation } = assetStore() + // Function to handle adding new user data const handleAddUserData = () => { const newUserData: UserData = { @@ -45,6 +49,12 @@ const AssetProperties: React.FC = () => { ); }; + const handleAnimationClick = (animation: string) => { + if (selectedFloorItem) { + const isPlaying = selectedFloorItem.animationState?.playing || false; + setCurrentAnimation(selectedFloorItem.uuid, animation, !isPlaying); + } + } return (
{/* Name */} @@ -96,6 +106,22 @@ const AssetProperties: React.FC = () => { + Add
+
+ {assets.map((asset) => ( +
+ { + asset.modelUuid === selectedFloorItem.uuid && + asset.animations && asset.animations.length > 0 + && asset.animations.map((animation, index) => ( +
{ handleAnimationClick(animation) }}> + {animation} +
+ )) + } + +
+ ))} +
); }; diff --git a/app/src/modules/builder/asset/models/model/model.tsx b/app/src/modules/builder/asset/models/model/model.tsx index 9354232..4d10c28 100644 --- a/app/src/modules/builder/asset/models/model/model.tsx +++ b/app/src/modules/builder/asset/models/model/model.tsx @@ -6,7 +6,7 @@ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'; import { ThreeEvent, useFrame, useThree } from '@react-three/fiber'; import { useActiveTool, useDeletableFloorItem, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store'; import { AssetBoundingBox } from '../../functions/assetBoundingBox'; -import { CameraControls } from '@react-three/drei'; +import { CameraControls, Html } from '@react-three/drei'; import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore'; import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore'; import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore'; @@ -23,7 +23,7 @@ function Model({ asset }: { readonly asset: Asset }) { const { subModule } = useSubModuleStore(); const { activeModule } = useModuleStore(); const { assetStore, eventStore, productStore } = useSceneContext(); - const { removeAsset } = assetStore(); + const { assets, removeAsset, setAnimations } = assetStore(); const { setTop } = useTopData(); const { setLeft } = useLeftData(); const { getIsEventInProduct } = productStore(); @@ -45,6 +45,9 @@ function Model({ asset }: { readonly asset: Asset }) { const { selectedVersion } = selectedVersionStore(); const { projectId } = useParams(); const { userId, organization } = getUserData(); + const [animationNames, setAnimationNames] = useState([]); + const mixerRef = useRef(); + const actions = useRef<{ [name: string]: THREE.AnimationAction }>({}); useEffect(() => { setDeletableFloorItem(null); @@ -59,11 +62,45 @@ function Model({ asset }: { readonly asset: Asset }) { const loadModel = async () => { try { // Check Cache + // const assetId = asset.assetId; + // const cachedModel = THREE.Cache.get(assetId); + // if (cachedModel) { + // setGltfScene(cachedModel.scene.clone()); + // calculateBoundingBox(cachedModel.scene); + // return; + // } + + // Check Cache + // const assetId = asset.assetId; + // console.log('assetId: ', assetId); + // const cachedModel = THREE.Cache.get(assetId); + // console.log('cachedModel: ', cachedModel); + // if (cachedModel) { + // setGltfScene(cachedModel.scene.clone()); + // calculateBoundingBox(cachedModel.scene); + // return; + // } + const assetId = asset.assetId; const cachedModel = THREE.Cache.get(assetId); if (cachedModel) { - setGltfScene(cachedModel.scene.clone()); - calculateBoundingBox(cachedModel.scene); + const clonedScene = cachedModel.scene.clone(); + clonedScene.animations = cachedModel.animations || []; + setGltfScene(clonedScene); + calculateBoundingBox(clonedScene); + if (cachedModel.animations && clonedScene.animations.length > 0) { + const animationName = clonedScene.animations.map((clip: any) => clip.name); + setAnimationNames(animationName) + setAnimations(asset.modelUuid, animationName) + mixerRef.current = new THREE.AnimationMixer(clonedScene); + + clonedScene.animations.forEach((animation: any) => { + const action = mixerRef.current!.clipAction(animation); + actions.current[animation.name] = action; + }); + } else { + console.log('No animations'); + } return; } @@ -252,6 +289,32 @@ function Model({ asset }: { readonly asset: Asset }) { clearSelectedAsset() } } + useFrame((_, delta) => { + if (mixerRef.current) { + mixerRef.current.update(delta); + } + }); + + + useEffect(() => { + const handlePlay = (clipName: string) => { + console.log('clipName: ', clipName, asset.animationState); + if (!mixerRef.current) return; + + Object.values(actions.current).forEach((action) => action.stop()); + + const action = actions.current[clipName]; + if (action && asset.animationState?.playing) { + action.reset().setLoop(THREE.LoopOnce, 1).play(); + console.log(`Playing: ${clipName}`); + } else { + console.warn(`No action found for: ${clipName}`); + } + }; + + handlePlay(asset.animationState?.current || ''); + + }, [asset]) return ( ) )} - + {/* + +
+ {animationNames.map((name) => ( + + ))} +
+ +
*/} + ); } diff --git a/app/src/store/builder/useAssetStore.ts b/app/src/store/builder/useAssetStore.ts index 66a3bf2..fec16b4 100644 --- a/app/src/store/builder/useAssetStore.ts +++ b/app/src/store/builder/useAssetStore.ts @@ -21,7 +21,7 @@ interface AssetsStore { setOpacity: (modelUuid: string, opacity: number) => void; // Animation controls - setAnimation: (modelUuid: string, animation: string) => void; + setAnimations: (modelUuid: string, animations: string[]) => void; setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void; addAnimation: (modelUuid: string, animation: string) => void; removeAnimation: (modelUuid: string, animation: string) => void; @@ -143,14 +143,13 @@ export const createAssetStore = () => { }, // Animation controls - setAnimation: (modelUuid, animation) => { + setAnimations: (modelUuid, animations) => { set((state) => { const asset = state.assets.find(a => a.modelUuid === modelUuid); if (asset) { + asset.animations = animations; if (!asset.animationState) { - asset.animationState = { current: animation, playing: false }; - } else { - asset.animationState.current = animation; + asset.animationState = { current: '', playing: false }; } } });