feat: Enhance asset and human event handling with animation and loop capabilities
This commit is contained in:
@@ -6,145 +6,128 @@ import PositionInput from "../customInput/PositionInputs";
|
|||||||
import RotationInput from "../customInput/RotationInput";
|
import RotationInput from "../customInput/RotationInput";
|
||||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
|
import { useSelectedFloorItem, useObjectPosition, useObjectRotation } from "../../../../store/builder/store";
|
||||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||||
|
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||||
|
|
||||||
interface UserData {
|
interface UserData {
|
||||||
id: number; // Unique identifier for the user data
|
id: number;
|
||||||
label: string; // Label of the user data field
|
label: string;
|
||||||
value: string; // Value of the user data field
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AssetProperties: React.FC = () => {
|
const AssetProperties: React.FC = () => {
|
||||||
const [userData, setUserData] = useState<UserData[]>([]); // State to track user data
|
const [userData, setUserData] = useState<UserData[]>([]);
|
||||||
const [nextId, setNextId] = useState(1); // Unique ID for new entries
|
const { selectedFloorItem } = useSelectedFloorItem();
|
||||||
const { selectedFloorItem } = useSelectedFloorItem();
|
const { objectPosition } = useObjectPosition();
|
||||||
const { objectPosition } = useObjectPosition();
|
const { objectRotation } = useObjectRotation();
|
||||||
const { objectRotation } = useObjectRotation();
|
const { assetStore } = useSceneContext();
|
||||||
const { assetStore } = useSceneContext();
|
const { assets, setCurrentAnimation } = assetStore();
|
||||||
const { assets, setCurrentAnimation } = assetStore()
|
const { loopAnimation } = useBuilderStore();
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
|
const [hoveredIndex, setHoveredIndex] = useState<any>(null);
|
||||||
const [isPlaying, setIsplaying] = useState(false);
|
|
||||||
// Function to handle adding new user data
|
const handleAddUserData = () => {
|
||||||
const handleAddUserData = () => {
|
|
||||||
const newUserData: UserData = {
|
|
||||||
id: nextId,
|
|
||||||
label: `Property ${nextId}`,
|
|
||||||
value: "",
|
|
||||||
};
|
};
|
||||||
setUserData([...userData, newUserData]);
|
|
||||||
setNextId(nextId + 1); // Increment the ID for the next entry
|
|
||||||
};
|
|
||||||
|
|
||||||
// Function to update the value of a user data entry
|
const handleUserDataChange = (id: number, newValue: string) => {
|
||||||
const handleUserDataChange = (id: number, newValue: string) => {
|
};
|
||||||
setUserData((prevUserData) =>
|
|
||||||
prevUserData.map((data) =>
|
|
||||||
data.id === id ? { ...data, value: newValue } : data
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove user data
|
const handleRemoveUserData = (id: number) => {
|
||||||
const handleRemoveUserData = (id: number) => {
|
};
|
||||||
setUserData((prevUserData) =>
|
|
||||||
prevUserData.filter((data) => data.id !== id)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAnimationClick = (animation: string) => {
|
const handleAnimationClick = (animation: string) => {
|
||||||
if (selectedFloorItem) {
|
if (selectedFloorItem) {
|
||||||
setCurrentAnimation(selectedFloorItem.uuid, animation, true);
|
setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!selectedFloorItem) return null;
|
if (!selectedFloorItem) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="asset-properties-container">
|
<div className="asset-properties-container">
|
||||||
{/* Name */}
|
{/* Name */}
|
||||||
<div className="header">{selectedFloorItem.userData.modelName}</div>
|
<div className="header">{selectedFloorItem.userData.modelName}</div>
|
||||||
<section>
|
<section>
|
||||||
{objectPosition.x && objectPosition.z &&
|
{objectPosition &&
|
||||||
<PositionInput
|
<PositionInput
|
||||||
onChange={() => { }}
|
onChange={() => { }}
|
||||||
value1={parseFloat(objectPosition.x.toFixed(5))}
|
value1={parseFloat(objectPosition.x.toFixed(5))}
|
||||||
value2={parseFloat(objectPosition.z.toFixed(5))}
|
value2={parseFloat(objectPosition.z.toFixed(5))}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
{objectRotation.y &&
|
{objectRotation &&
|
||||||
<RotationInput
|
<RotationInput
|
||||||
onChange={() => { }}
|
onChange={() => { }}
|
||||||
value={parseFloat(objectRotation.y.toFixed(5))}
|
value={parseFloat(objectRotation.y.toFixed(5))}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div className="header">Render settings</div>
|
<div className="header">Render settings</div>
|
||||||
<InputToggle inputKey="visible" label="Visible" />
|
<InputToggle inputKey="visible" label="Visible" />
|
||||||
<InputToggle inputKey="frustumCull" label="Frustum cull" />
|
<InputToggle inputKey="frustumCull" label="Frustum cull" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div className="header">User Data</div>
|
<div className="header">User Data</div>
|
||||||
{userData.map((data) => (
|
{userData.map((data) => (
|
||||||
<div className="input-container">
|
<div className="input-container">
|
||||||
<InputWithDropDown
|
<InputWithDropDown
|
||||||
key={data.id}
|
key={data.id}
|
||||||
label={data.label}
|
label={data.label}
|
||||||
value={data.value}
|
value={data.value}
|
||||||
editableLabel
|
editableLabel
|
||||||
onChange={(newValue) => handleUserDataChange(data.id, newValue)} // Pass the change handler
|
onChange={(newValue) => handleUserDataChange(data.id, newValue)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className="remove-button"
|
className="remove-button"
|
||||||
onClick={() => handleRemoveUserData(data.id)}
|
onClick={() => handleRemoveUserData(data.id)}
|
||||||
>
|
>
|
||||||
<RemoveIcon />
|
<RemoveIcon />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Add new user data */}
|
{/* Add new user data */}
|
||||||
<div className="optimize-button" onClick={handleAddUserData}>
|
<div className="optimize-button" onClick={handleAddUserData}>
|
||||||
+ Add
|
+ Add
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
|
|
||||||
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
|
|
||||||
{assets.map((asset) => (
|
|
||||||
<div key={asset.modelUuid} className="asset-item">
|
|
||||||
{asset.modelUuid === selectedFloorItem.uuid &&
|
|
||||||
asset.animations &&
|
|
||||||
asset.animations.length > 0 &&
|
|
||||||
asset.animations.map((animation, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
onClick={() => handleAnimationClick(animation)}
|
|
||||||
onMouseEnter={() => setHoveredIndex(index)}
|
|
||||||
onMouseLeave={() => setHoveredIndex(null)}
|
|
||||||
style={{
|
|
||||||
height: "20px",
|
|
||||||
width: "100%",
|
|
||||||
borderRadius: "5px",
|
|
||||||
background:
|
|
||||||
hoveredIndex === index
|
|
||||||
? "#7b4cd3"
|
|
||||||
: "transparent",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{animation.charAt(0).toUpperCase() +
|
|
||||||
animation.slice(1).toLowerCase()}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
</section>
|
||||||
</div>
|
<div style={{ display: "flex", flexDirection: "column", outline: "1px solid var(--border-color)" }}>
|
||||||
))}
|
{selectedFloorItem.uuid && <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>Animations</div>}
|
||||||
</div>
|
{assets.map((asset) => (
|
||||||
</div>
|
<div key={asset.modelUuid} className="asset-item">
|
||||||
);
|
{asset.modelUuid === selectedFloorItem.uuid &&
|
||||||
|
asset.animations &&
|
||||||
|
asset.animations.length > 0 &&
|
||||||
|
asset.animations.map((animation, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
style={{ gap: "15px", cursor: "pointer", padding: "5px" }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
onClick={() => handleAnimationClick(animation)}
|
||||||
|
onMouseEnter={() => setHoveredIndex(index)}
|
||||||
|
onMouseLeave={() => setHoveredIndex(null)}
|
||||||
|
style={{
|
||||||
|
height: "20px",
|
||||||
|
width: "100%",
|
||||||
|
borderRadius: "5px",
|
||||||
|
background:
|
||||||
|
hoveredIndex === index
|
||||||
|
? "#7b4cd3"
|
||||||
|
: "transparent",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{animation.charAt(0).toUpperCase() +
|
||||||
|
animation.slice(1).toLowerCase()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AssetProperties;
|
export default AssetProperties;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import * as THREE from "three"
|
import * as THREE from "three"
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
|
import { getFloorAssets } from '../../../services/factoryBuilder/asset/floorAsset/getFloorItemsApi';
|
||||||
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
import { useLoadingProgress, useRenameModeStore, useSelectedFloorItem, useSelectedItem, useSocketStore } from '../../../store/builder/store';
|
||||||
@@ -226,7 +226,8 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
|||||||
modelUuid: item.modelUuid,
|
modelUuid: item.modelUuid,
|
||||||
modelName: item.modelName,
|
modelName: item.modelName,
|
||||||
position: item.position,
|
position: item.position,
|
||||||
rotation: [item.rotation.x, item.rotation.y, item.rotation.z], state: "idle",
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
state: "idle",
|
||||||
type: "storageUnit",
|
type: "storageUnit",
|
||||||
point: {
|
point: {
|
||||||
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
||||||
@@ -242,6 +243,36 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
addEvent(storageEvent);
|
addEvent(storageEvent);
|
||||||
|
} else if (item.eventData.type === 'Human') {
|
||||||
|
const humanEvent: HumanEventSchema = {
|
||||||
|
modelUuid: item.modelUuid,
|
||||||
|
modelName: item.modelName,
|
||||||
|
position: item.position,
|
||||||
|
rotation: [item.rotation.x, item.rotation.y, item.rotation.z],
|
||||||
|
state: "idle",
|
||||||
|
type: "human",
|
||||||
|
point: {
|
||||||
|
uuid: item.eventData.point?.uuid || THREE.MathUtils.generateUUID(),
|
||||||
|
position: [item.eventData.point?.position[0] || 0, item.eventData.point?.position[1] || 0, item.eventData.point?.position[2] || 0],
|
||||||
|
rotation: [item.eventData.point?.rotation[0] || 0, item.eventData.point?.rotation[1] || 0, item.eventData.point?.rotation[2] || 0],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
actionUuid: THREE.MathUtils.generateUUID(),
|
||||||
|
actionName: "Action 1",
|
||||||
|
actionType: "animation",
|
||||||
|
animation: null,
|
||||||
|
loopAnimation: true,
|
||||||
|
loadCapacity: 1,
|
||||||
|
travelPoints: {
|
||||||
|
startPoint: null,
|
||||||
|
endPoint: null,
|
||||||
|
},
|
||||||
|
triggers: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addEvent(humanEvent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assets.push({
|
assets.push({
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ async function handleModelLoad(
|
|||||||
// SOCKET
|
// SOCKET
|
||||||
|
|
||||||
if (selectedItem.type) {
|
if (selectedItem.type) {
|
||||||
|
console.log('selectedItem: ', selectedItem);
|
||||||
const data = PointsCalculator(
|
const data = PointsCalculator(
|
||||||
selectedItem.type,
|
selectedItem.type,
|
||||||
gltf.scene.clone(),
|
gltf.scene.clone(),
|
||||||
@@ -170,7 +171,7 @@ async function handleModelLoad(
|
|||||||
|
|
||||||
if (!data || !data.points) return;
|
if (!data || !data.points) return;
|
||||||
|
|
||||||
const eventData: any = { type: selectedItem.type, };
|
const eventData: any = { type: selectedItem.type };
|
||||||
|
|
||||||
if (selectedItem.type === "Conveyor") {
|
if (selectedItem.type === "Conveyor") {
|
||||||
const ConveyorEvent: ConveyorEventSchema = {
|
const ConveyorEvent: ConveyorEventSchema = {
|
||||||
@@ -378,6 +379,7 @@ async function handleModelLoad(
|
|||||||
actionName: "Action 1",
|
actionName: "Action 1",
|
||||||
actionType: "animation",
|
actionType: "animation",
|
||||||
animation: null,
|
animation: null,
|
||||||
|
loopAnimation: true,
|
||||||
loadCapacity: 1,
|
loadCapacity: 1,
|
||||||
travelPoints: {
|
travelPoints: {
|
||||||
startPoint: null,
|
startPoint: null,
|
||||||
@@ -416,6 +418,7 @@ async function handleModelLoad(
|
|||||||
userId: userId,
|
userId: userId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('completeData: ', completeData);
|
||||||
socket.emit("v1:model-asset:add", completeData);
|
socket.emit("v1:model-asset:add", completeData);
|
||||||
|
|
||||||
const asset: Asset = {
|
const asset: Asset = {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
|
|||||||
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
||||||
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
import { useActiveTool, useDeletableFloorItem, useLimitDistance, useRenderDistance, useSelectedFloorItem, useSocketStore, useToggleView, useToolMode } from '../../../../../store/builder/store';
|
||||||
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
import { AssetBoundingBox } from '../../functions/assetBoundingBox';
|
||||||
import { CameraControls, Html } from '@react-three/drei';
|
import { CameraControls } from '@react-three/drei';
|
||||||
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
import useModuleStore, { useSubModuleStore } from '../../../../../store/useModuleStore';
|
||||||
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
import { useLeftData, useTopData } from '../../../../../store/visualization/useZone3DWidgetStore';
|
||||||
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
import { useSelectedAsset } from '../../../../../store/simulation/useSimulationStore';
|
||||||
@@ -15,6 +15,7 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { getUserData } from '../../../../../functions/getUserData';
|
import { getUserData } from '../../../../../functions/getUserData';
|
||||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||||
import { useVersionContext } from '../../../version/versionContext';
|
import { useVersionContext } from '../../../version/versionContext';
|
||||||
|
import { SkeletonUtils } from 'three-stdlib';
|
||||||
|
|
||||||
function Model({ asset }: { readonly asset: Asset }) {
|
function Model({ asset }: { readonly asset: Asset }) {
|
||||||
const { camera, controls, gl } = useThree();
|
const { camera, controls, gl } = useThree();
|
||||||
@@ -23,7 +24,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { subModule } = useSubModuleStore();
|
const { subModule } = useSubModuleStore();
|
||||||
const { activeModule } = useModuleStore();
|
const { activeModule } = useModuleStore();
|
||||||
const { assetStore, eventStore, productStore } = useSceneContext();
|
const { assetStore, eventStore, productStore } = useSceneContext();
|
||||||
const { assets, removeAsset, setAnimations } = assetStore();
|
const { removeAsset, setAnimations, resetAnimation } = assetStore();
|
||||||
const { setTop } = useTopData();
|
const { setTop } = useTopData();
|
||||||
const { setLeft } = useLeftData();
|
const { setLeft } = useLeftData();
|
||||||
const { getIsEventInProduct } = productStore();
|
const { getIsEventInProduct } = productStore();
|
||||||
@@ -33,7 +34,7 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
|
const { setSelectedAsset, clearSelectedAsset } = useSelectedAsset();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
|
const { deletableFloorItem, setDeletableFloorItem } = useDeletableFloorItem();
|
||||||
const { setSelectedFloorItem } = useSelectedFloorItem();
|
const { selectedFloorItem, setSelectedFloorItem } = useSelectedFloorItem();
|
||||||
const { limitDistance } = useLimitDistance();
|
const { limitDistance } = useLimitDistance();
|
||||||
const { renderDistance } = useRenderDistance();
|
const { renderDistance } = useRenderDistance();
|
||||||
const [isRendered, setIsRendered] = useState(false);
|
const [isRendered, setIsRendered] = useState(false);
|
||||||
@@ -46,13 +47,15 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
const { selectedVersion } = selectedVersionStore();
|
const { selectedVersion } = selectedVersionStore();
|
||||||
const { projectId } = useParams();
|
const { projectId } = useParams();
|
||||||
const { userId, organization } = getUserData();
|
const { userId, organization } = getUserData();
|
||||||
const [animationNames, setAnimationNames] = useState<string[]>([]);
|
|
||||||
const mixerRef = useRef<THREE.AnimationMixer>();
|
const mixerRef = useRef<THREE.AnimationMixer>();
|
||||||
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDeletableFloorItem(null);
|
setDeletableFloorItem(null);
|
||||||
}, [activeModule, toolMode])
|
if (selectedFloorItem === null) {
|
||||||
|
resetAnimation(asset.modelUuid);
|
||||||
|
}
|
||||||
|
}, [activeModule, toolMode, selectedFloorItem])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loader = new GLTFLoader();
|
const loader = new GLTFLoader();
|
||||||
@@ -62,40 +65,21 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
loader.setDRACOLoader(dracoLoader);
|
loader.setDRACOLoader(dracoLoader);
|
||||||
const loadModel = async () => {
|
const loadModel = async () => {
|
||||||
try {
|
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
|
// 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 assetId = asset.assetId;
|
||||||
const cachedModel = THREE.Cache.get(assetId);
|
const cachedModel = THREE.Cache.get(assetId);
|
||||||
if (cachedModel) {
|
if (cachedModel) {
|
||||||
const clonedScene = cachedModel.scene.clone();
|
const clone: any = SkeletonUtils.clone(cachedModel.scene);
|
||||||
clonedScene.animations = cachedModel.animations || [];
|
clone.animations = cachedModel.animations || [];
|
||||||
setGltfScene(clonedScene);
|
setGltfScene(clone);
|
||||||
calculateBoundingBox(clonedScene);
|
calculateBoundingBox(clone);
|
||||||
if (cachedModel.animations && clonedScene.animations.length > 0) {
|
if (cachedModel.animations && clone.animations.length > 0) {
|
||||||
const animationName = clonedScene.animations.map((clip: any) => clip.name);
|
const animationName = clone.animations.map((clip: any) => clip.name);
|
||||||
setAnimationNames(animationName)
|
|
||||||
setAnimations(asset.modelUuid, animationName)
|
setAnimations(asset.modelUuid, animationName)
|
||||||
mixerRef.current = new THREE.AnimationMixer(clonedScene);
|
mixerRef.current = new THREE.AnimationMixer(clone);
|
||||||
|
|
||||||
clonedScene.animations.forEach((animation: any) => {
|
clone.animations.forEach((animation: any) => {
|
||||||
const action = mixerRef.current!.clipAction(animation);
|
const action = mixerRef.current!.clipAction(animation);
|
||||||
actions.current[animation.name] = action;
|
actions.current[animation.name] = action;
|
||||||
});
|
});
|
||||||
@@ -293,28 +277,27 @@ function Model({ asset }: { readonly asset: Asset }) {
|
|||||||
clearSelectedAsset()
|
clearSelectedAsset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useFrame((_, delta) => {
|
useFrame((_, delta) => {
|
||||||
if (mixerRef.current) {
|
if (mixerRef.current) {
|
||||||
mixerRef.current.update(delta);
|
mixerRef.current.update(delta);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (asset.animationState && asset.animationState.isPlaying) {
|
||||||
if (asset.animationState && asset.animationState.playing) {
|
|
||||||
if (!mixerRef.current) return;
|
if (!mixerRef.current) return;
|
||||||
|
|
||||||
Object.values(actions.current).forEach((action) => action.stop());
|
Object.values(actions.current).forEach((action) => action.stop());
|
||||||
|
|
||||||
const action = actions.current[asset.animationState.current];
|
const action = actions.current[asset.animationState.current];
|
||||||
if (action && asset.animationState?.playing) {
|
if (action && asset.animationState?.isPlaying) {
|
||||||
action.reset().setLoop(THREE.LoopOnce, 1).play();
|
const loopMode = asset.animationState.loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce;
|
||||||
|
action.reset().setLoop(loopMode, loopMode === THREE.LoopRepeat ? Infinity : 1).play();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Object.values(actions.current).forEach((action) => action.stop());
|
Object.values(actions.current).forEach((action) => action.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [asset.animationState])
|
}, [asset.animationState])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ interface AssetsStore {
|
|||||||
|
|
||||||
// Animation controls
|
// Animation controls
|
||||||
setAnimations: (modelUuid: string, animations: string[]) => void;
|
setAnimations: (modelUuid: string, animations: string[]) => void;
|
||||||
setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean) => void;
|
setCurrentAnimation: (modelUuid: string, current: string, isisPlaying: boolean, loopAnimation: boolean) => void;
|
||||||
|
resetAnimation: (modelUuid: string) => void;
|
||||||
addAnimation: (modelUuid: string, animation: string) => void;
|
addAnimation: (modelUuid: string, animation: string) => void;
|
||||||
removeAnimation: (modelUuid: string, animation: string) => void;
|
removeAnimation: (modelUuid: string, animation: string) => void;
|
||||||
|
|
||||||
@@ -149,18 +150,28 @@ export const createAssetStore = () => {
|
|||||||
if (asset) {
|
if (asset) {
|
||||||
asset.animations = animations;
|
asset.animations = animations;
|
||||||
if (!asset.animationState) {
|
if (!asset.animationState) {
|
||||||
asset.animationState = { current: '', playing: false };
|
asset.animationState = { current: '', isPlaying: false, loopAnimation: true };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setCurrentAnimation: (modelUuid, current, isPlaying) => {
|
setCurrentAnimation: (modelUuid, current, isisPlaying, loopAnimation) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
if (asset?.animationState) {
|
if (asset?.animationState) {
|
||||||
asset.animationState.current = current;
|
asset.animationState.current = current;
|
||||||
asset.animationState.playing = isPlaying;
|
asset.animationState.isPlaying = isisPlaying;
|
||||||
|
asset.animationState.loopAnimation = loopAnimation;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
resetAnimation: (modelUuid) => {
|
||||||
|
set((state) => {
|
||||||
|
const asset = state.assets.find(a => a.modelUuid === modelUuid);
|
||||||
|
if (asset?.animationState) {
|
||||||
|
asset.animationState = { current: '', isPlaying: false, loopAnimation: true };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -184,7 +195,7 @@ export const createAssetStore = () => {
|
|||||||
if (asset?.animations) {
|
if (asset?.animations) {
|
||||||
asset.animations = asset.animations.filter(a => a !== animation);
|
asset.animations = asset.animations.filter(a => a !== animation);
|
||||||
if (asset.animationState?.current === animation) {
|
if (asset.animationState?.current === animation) {
|
||||||
asset.animationState.playing = false;
|
asset.animationState.isPlaying = false;
|
||||||
asset.animationState.current = '';
|
asset.animationState.current = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface BuilderState {
|
|||||||
|
|
||||||
// Floor Asset
|
// Floor Asset
|
||||||
selectedFloorAsset: Object3D | null;
|
selectedFloorAsset: Object3D | null;
|
||||||
|
loopAnimation: boolean;
|
||||||
|
|
||||||
// Wall Settings
|
// Wall Settings
|
||||||
selectedWall: Object3D | null;
|
selectedWall: Object3D | null;
|
||||||
@@ -64,6 +65,7 @@ interface BuilderState {
|
|||||||
|
|
||||||
// Setters - Floor Asset
|
// Setters - Floor Asset
|
||||||
setSelectedFloorAsset: (asset: Object3D | null) => void;
|
setSelectedFloorAsset: (asset: Object3D | null) => void;
|
||||||
|
setLoopAnimation: (loop: boolean) => void;
|
||||||
|
|
||||||
// Setters - Wall
|
// Setters - Wall
|
||||||
setSelectedWall: (wall: Object3D | null) => void;
|
setSelectedWall: (wall: Object3D | null) => void;
|
||||||
@@ -118,6 +120,7 @@ export const useBuilderStore = create<BuilderState>()(
|
|||||||
deletableWallAsset: null,
|
deletableWallAsset: null,
|
||||||
|
|
||||||
selectedFloorAsset: null,
|
selectedFloorAsset: null,
|
||||||
|
loopAnimation: true,
|
||||||
|
|
||||||
selectedWall: null,
|
selectedWall: null,
|
||||||
wallThickness: 0.5,
|
wallThickness: 0.5,
|
||||||
@@ -197,6 +200,12 @@ export const useBuilderStore = create<BuilderState>()(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setLoopAnimation(loopAnimation: boolean) {
|
||||||
|
set((state) => {
|
||||||
|
state.loopAnimation = loopAnimation;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// === Setters: Wall ===
|
// === Setters: Wall ===
|
||||||
|
|
||||||
setSelectedWall: (wall: Object3D | null) => {
|
setSelectedWall: (wall: Object3D | null) => {
|
||||||
|
|||||||
3
app/src/types/builderTypes.d.ts
vendored
3
app/src/types/builderTypes.d.ts
vendored
@@ -26,7 +26,8 @@ interface Asset {
|
|||||||
animations?: string[];
|
animations?: string[];
|
||||||
animationState?: {
|
animationState?: {
|
||||||
current: string;
|
current: string;
|
||||||
playing: boolean;
|
isPlaying: boolean;
|
||||||
|
loopAnimation: boolean;
|
||||||
};
|
};
|
||||||
eventData?: {
|
eventData?: {
|
||||||
type: string;
|
type: string;
|
||||||
|
|||||||
1
app/src/types/simulationTypes.d.ts
vendored
1
app/src/types/simulationTypes.d.ts
vendored
@@ -74,6 +74,7 @@ interface HumanAction {
|
|||||||
actionName: string;
|
actionName: string;
|
||||||
actionType: "animation" | "animatedTravel";
|
actionType: "animation" | "animatedTravel";
|
||||||
animation: string | null;
|
animation: string | null;
|
||||||
|
loopAnimation: boolean;
|
||||||
loadCapacity: number;
|
loadCapacity: number;
|
||||||
travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }
|
travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null; }
|
||||||
triggers: TriggerSchema[];
|
triggers: TriggerSchema[];
|
||||||
|
|||||||
Reference in New Issue
Block a user