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 };
}
}
});