feat: Enhance InputWithDropDown to support disabled state

fix: Update Model component to manage animation states and transitions more effectively

feat: Implement worker action handling in useWorkerHandler for material management

feat: Add MaterialAnimator to HumanInstance for dynamic material loading

feat: Extend useTriggerHandler to support interactions between humans and various entities

feat: Create WorkerAction component for managing load capacity and actions

feat: Introduce MaterialAnimator for human instances to visualize material loads

refactor: Update asset store to manage animation completion state

fix: Ensure proper handling of human materials in useHumanStore
This commit is contained in:
2025-07-04 13:14:39 +05:30
parent 7cf82629e9
commit 02490214d9
17 changed files with 990 additions and 261 deletions

View File

@@ -35,7 +35,7 @@ const AssetProperties: React.FC = () => {
const handleAnimationClick = (animation: string) => { const handleAnimationClick = (animation: string) => {
if (selectedFloorItem) { if (selectedFloorItem) {
setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation); setCurrentAnimation(selectedFloorItem.uuid, animation, true, loopAnimation, true);
} }
} }

View File

@@ -1,95 +1,70 @@
import React from "react"; import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown"; import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import EyeDropInput from "../../../../../ui/inputs/EyeDropInput";
interface TravelActionProps { interface TravelActionProps {
loadCapacity: { loadCapacity: {
value: string; value: string;
min: number; min: number;
max: number; max: number;
defaultValue: string; defaultValue: string;
onChange: (value: string) => void; onChange: (value: string) => void;
}; };
unloadDuration: { unloadDuration: {
value: string; value: string;
min: number; min: number;
max: number; max: number;
defaultValue: string; defaultValue: string;
onChange: (value: string) => void; onChange: (value: string) => void;
}; };
pickPoint?: { clearPoints: () => void;
value: string;
onChange: (value: string) => void;
};
unloadPoint?: {
value: string;
onChange: (value: string) => void;
};
clearPoints: () => void;
} }
const TravelAction: React.FC<TravelActionProps> = ({ const TravelAction: React.FC<TravelActionProps> = ({
loadCapacity, loadCapacity,
unloadDuration, unloadDuration,
pickPoint, clearPoints,
unloadPoint,
clearPoints,
}) => { }) => {
return ( return (
<> <>
<InputWithDropDown <InputWithDropDown
label="Load Capacity" label="Load Capacity"
value={loadCapacity.value} value={loadCapacity.value}
min={loadCapacity.min} min={loadCapacity.min}
max={loadCapacity.max} max={loadCapacity.max}
defaultValue={loadCapacity.defaultValue} defaultValue={loadCapacity.defaultValue}
step={1} step={1}
activeOption="s" activeOption="unit"
onClick={() => {}} onClick={() => { }}
onChange={loadCapacity.onChange} onChange={loadCapacity.onChange}
/> />
<InputWithDropDown <InputWithDropDown
label="Unload Duration" label="Unload Duration"
value={unloadDuration.value} value={unloadDuration.value}
min={unloadDuration.min} min={unloadDuration.min}
max={unloadDuration.max} max={unloadDuration.max}
defaultValue={unloadDuration.defaultValue} defaultValue={unloadDuration.defaultValue}
step={0.1} step={0.1}
activeOption="s" activeOption="s"
onClick={() => {}} onClick={() => { }}
onChange={unloadDuration.onChange} onChange={unloadDuration.onChange}
/> />
<div className="selected-actions-list"> <div className="selected-actions-list">
<div className="value-field-container"> <div className="value-field-container">
<div className="label">Reset</div> <div className="label">Reset</div>
<button <button
id="rest-button" id="rest-button"
type="button" type="button"
className="regularDropdown-container" className="regularDropdown-container"
onClick={() => { onClick={() => {
clearPoints(); clearPoints();
}} }}
> >
Clear Clear
</button> </button>
</div> </div>
</div> </div>
{pickPoint && ( </>
<EyeDropInput );
label="Pick Point"
value={pickPoint.value}
onChange={pickPoint.onChange}
/>
)}
{unloadPoint && (
<EyeDropInput
label="Unload Point"
value={unloadPoint.value}
onChange={unloadPoint.onChange}
/>
)}
</>
);
}; };
export default TravelAction; export default TravelAction;

View File

@@ -0,0 +1,54 @@
import React from "react";
import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
interface WorkerActionProps {
loadCapacity: {
value: string;
min: number;
max: number;
step: number;
defaultValue: string;
disabled?: boolean,
onChange: (value: string) => void;
};
clearPoints: () => void;
}
const WorkerAction: React.FC<WorkerActionProps> = ({
loadCapacity,
clearPoints,
}) => {
return (
<>
<InputWithDropDown
label="Load Capacity"
value={loadCapacity.value}
min={loadCapacity.min}
max={loadCapacity.max}
disabled={loadCapacity.disabled}
defaultValue={loadCapacity.defaultValue}
step={loadCapacity.step}
activeOption="unit"
onClick={() => { }}
onChange={loadCapacity.onChange}
/>
<div className="selected-actions-list">
<div className="value-field-container">
<div className="label">Reset</div>
<button
id="rest-button"
type="button"
className="regularDropdown-container"
onClick={() => {
clearPoints();
}}
>
Clear
</button>
</div>
</div>
</>
);
};
export default WorkerAction;

View File

@@ -1,97 +0,0 @@
import React, { useRef } from "react";
import { AddIcon, RemoveIcon, ResizeHeightIcon } from "../../../../../icons/ExportCommonIcons";
import { handleResize } from "../../../../../../functions/handleResizePannel";
import { useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore";
import RenameInput from "../../../../../ui/inputs/RenameInput";
interface AnimationListProps {
animationOptions: string[];
animationSequences: {
animationUuid: string;
animationName: string;
animationType: "behaviour" | "animatedTravel";
animation: string | null;
travelPoints?: { startPoint: [number, number, number] | null; endPoint: [number, number, number] | null };
}[];
onAddAnimation: () => void;
onRemoveAnimation: (animationUuid: string) => void;
handleAnimationSelect: (animationUuid: string) => void;
handleRenameAnimation: (animationUuid: string, newName: string) => void;
}
const AnimationList: React.FC<AnimationListProps> = ({
animationSequences,
onAddAnimation,
onRemoveAnimation,
handleAnimationSelect,
handleRenameAnimation,
}) => {
const animationContainerRef = useRef<HTMLDivElement>(null);
const { selectedAction } = useSelectedAction();
const { selectedAnimation } = useSelectedAnimation();
return (
<div className="actions-list-container">
<div className="actions">
<div className="header">
<div className="header-value">Animation Sequences</div>
<button
id="add-action-button"
className="add-button"
onClick={onAddAnimation}
disabled={!selectedAction.actionId}
>
<AddIcon /> Add
</button>
</div>
<div
className="lists-main-container"
ref={animationContainerRef}
style={{ height: "120px" }}
>
<div className="list-container">
{animationSequences.map((sequence) => (
<div
key={sequence.animationUuid}
className={`list-item ${selectedAnimation?.animationUuid === sequence.animationUuid ? "active" : ""}`}
>
<button
id="action-button"
className="value"
onClick={() =>
handleAnimationSelect(sequence.animationUuid)
}
>
<RenameInput
value={sequence.animationName}
onRename={(value) => handleRenameAnimation(sequence.animationUuid, value)}
/>
</button>
{animationSequences.length > 1 && (
<button
id="remove-action-button"
className="remove-button"
onClick={() => onRemoveAnimation(sequence.animationUuid)}
>
<RemoveIcon />
</button>
)}
</div>
))}
</div>
{animationSequences.length > 0 && (
<button
className="resize-icon"
id="action-resize"
onMouseDown={(e: any) => handleResize(e, animationContainerRef)}
>
<ResizeHeightIcon />
</button>
)}
</div>
</div>
</div>
);
};
export default AnimationList;

View File

@@ -4,30 +4,27 @@ import InputWithDropDown from "../../../../../ui/inputs/InputWithDropDown";
import RenameInput from "../../../../../ui/inputs/RenameInput"; import RenameInput from "../../../../../ui/inputs/RenameInput";
import LabledDropdown from "../../../../../ui/inputs/LabledDropdown"; import LabledDropdown from "../../../../../ui/inputs/LabledDropdown";
import Trigger from "../trigger/Trigger"; import Trigger from "../trigger/Trigger";
import PickAndPlaceAction from "../actions/PickAndPlaceAction";
import ActionsList from "../components/ActionsList"; import ActionsList from "../components/ActionsList";
import AnimationList from "../components/AnimationList";
import { useSelectedEventData, useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore"; import { useSelectedEventData, useSelectedAction, useSelectedAnimation } from "../../../../../../store/simulation/useSimulationStore";
import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi"; import { upsertProductOrEventApi } from "../../../../../../services/simulation/products/UpsertProductOrEventApi";
import { useProductContext } from "../../../../../../modules/simulation/products/productContext"; import { useProductContext } from "../../../../../../modules/simulation/products/productContext";
import { useVersionContext } from "../../../../../../modules/builder/version/versionContext"; import { useVersionContext } from "../../../../../../modules/builder/version/versionContext";
import { useSceneContext } from "../../../../../../modules/scene/sceneContext"; import { useSceneContext } from "../../../../../../modules/scene/sceneContext";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import WorkerAction from "../actions/workerAction";
function HumanMechanics() { function HumanMechanics() {
const [activeOption, setActiveOption] = useState<"worker">("worker"); const [activeOption, setActiveOption] = useState<"worker">("worker");
const [animationOptions, setAnimationOptions] = useState<string[]>([]);
const [speed, setSpeed] = useState("0.5"); const [speed, setSpeed] = useState("0.5");
const [loadCapacity, setLoadCapacity] = useState("1");
const [currentAction, setCurrentAction] = useState<HumanAction | undefined>(); const [currentAction, setCurrentAction] = useState<HumanAction | undefined>();
const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>(); const [selectedPointData, setSelectedPointData] = useState<HumanPointSchema | undefined>();
const { selectedEventData } = useSelectedEventData(); const { selectedEventData } = useSelectedEventData();
const { productStore, assetStore } = useSceneContext(); const { productStore } = useSceneContext();
const { getAssetById } = assetStore(); const { getPointByUuid, updateEvent, updateAction, addAction, removeAction, getEventByModelUuid } = productStore();
const { getPointByUuid, getEventByModelUuid, updateEvent, updateAction, addAction, removeAction } = productStore();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction(); const { selectedAction, setSelectedAction, clearSelectedAction } = useSelectedAction();
const { selectedAnimation, setSelectedAnimation } = useSelectedAnimation();
const { selectedVersionStore } = useVersionContext(); const { selectedVersionStore } = useVersionContext();
const { selectedVersion } = selectedVersionStore(); const { selectedVersion } = selectedVersionStore();
const { projectId } = useParams(); const { projectId } = useParams();
@@ -44,6 +41,13 @@ function HumanMechanics() {
setSelectedPointData(point); setSelectedPointData(point);
setCurrentAction(point.action); setCurrentAction(point.action);
setSelectedAction(point.action.actionUuid, point.action.actionName); setSelectedAction(point.action.actionUuid, point.action.actionName);
setSpeed((
getEventByModelUuid(
selectedProduct.productUuid,
selectedEventData?.data.modelUuid || ""
) as HumanEventSchema | undefined
)?.speed?.toString() || "1");
setLoadCapacity(point.action.loadCapacity.toString());
} }
} else { } else {
clearSelectedAction(); clearSelectedAction();
@@ -68,6 +72,7 @@ function HumanMechanics() {
clearSelectedAction(); clearSelectedAction();
setCurrentAction(undefined); setCurrentAction(undefined);
setSpeed("0.5"); setSpeed("0.5");
setLoadCapacity("1");
} }
}, [selectedEventData, selectedProduct, selectedAction]); }, [selectedEventData, selectedProduct, selectedAction]);
@@ -130,6 +135,29 @@ function HumanMechanics() {
setSpeed(value); setSpeed(value);
}; };
const handleLoadCapacityChange = (value: string) => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
const updatedAction = { ...currentAction };
updatedAction.loadCapacity = parseInt(value)
const updatedPoint = { ...selectedPointData, action: updatedAction };
const event = updateAction(
selectedProduct.productUuid,
selectedAction.actionId,
updatedAction
);
if (event) {
updateBackend(selectedProduct.productName, selectedProduct.productUuid, projectId || '', event);
}
setCurrentAction(updatedAction);
setSelectedPointData(updatedPoint);
setLoadCapacity(value);
};
const handleClearPoints = () => { const handleClearPoints = () => {
if (!currentAction || !selectedPointData || !selectedAction.actionId) return; if (!currentAction || !selectedPointData || !selectedAction.actionId) return;
@@ -239,7 +267,18 @@ function HumanMechanics() {
disabled={true} disabled={true}
/> />
</div> </div>
<PickAndPlaceAction clearPoints={handleClearPoints} /> <WorkerAction
loadCapacity={{
value: loadCapacity,
min: 1,
max: 5,
step: 1,
defaultValue: "1",
disabled: true,
onChange: handleLoadCapacityChange,
}}
clearPoints={handleClearPoints}
/>
<div className="tirgger"> <div className="tirgger">
<Trigger selectedPointData={selectedPointData as any} type="Human" /> <Trigger selectedPointData={selectedPointData as any} type="Human" />
</div> </div>

View File

@@ -272,14 +272,6 @@ function VehicleMechanics() {
onChange: handleUnloadDurationChange, onChange: handleUnloadDurationChange,
}} }}
clearPoints={handleClearPoints} clearPoints={handleClearPoints}
// pickPoint={{
// value: currentPickPoint,
// onChange: handlePickPointChange,
// }}
// unloadPoint={{
// value: currentUnloadPoint,
// onChange: handleUnloadPointChange,
// }}
/> />
)} )}
</div> </div>

View File

@@ -8,6 +8,7 @@ type InputWithDropDownProps = {
max?: number; max?: number;
step?: number; step?: number;
defaultValue?: string; defaultValue?: string;
disabled?: boolean;
options?: string[]; // Array of dropdown options options?: string[]; // Array of dropdown options
activeOption?: string; // The currently active dropdown option activeOption?: string; // The currently active dropdown option
onClick?: () => void; onClick?: () => void;
@@ -23,6 +24,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
max, max,
step, step,
defaultValue, defaultValue,
disabled = false,
options, options,
activeOption, activeOption,
onClick, onClick,
@@ -54,6 +56,7 @@ const InputWithDropDown: React.FC<InputWithDropDownProps> = ({
type="number" type="number"
defaultValue={value} defaultValue={value}
// value={value} // value={value}
disabled={disabled}
onChange={(e) => { onChange={(e) => {
onChange(e.target.value); onChange(e.target.value);
}} }}

View File

@@ -24,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 { removeAsset, setAnimations, resetAnimation } = assetStore(); const { removeAsset, setAnimations, resetAnimation, setAnimationComplete, setCurrentAnimation: setAnmationAnimation } = assetStore();
const { setTop } = useTopData(); const { setTop } = useTopData();
const { setLeft } = useLeftData(); const { setLeft } = useLeftData();
const { getIsEventInProduct } = productStore(); const { getIsEventInProduct } = productStore();
@@ -49,10 +49,9 @@ function Model({ asset }: { readonly asset: Asset }) {
const { userId, organization } = getUserData(); const { userId, organization } = getUserData();
const mixerRef = useRef<THREE.AnimationMixer>(); const mixerRef = useRef<THREE.AnimationMixer>();
const actions = useRef<{ [name: string]: THREE.AnimationAction }>({}); const actions = useRef<{ [name: string]: THREE.AnimationAction }>({});
const [currentAnimation, setCurrentAnimation] = useState<string | null>(null);
const [previousAnimation, setPreviousAnimation] = useState<string | null>(null); const [previousAnimation, setPreviousAnimation] = useState<string | null>(null);
const [blendFactor, setBlendFactor] = useState(0); const blendFactor = useRef(0);
const blendDuration = 0.3; const blendDuration = 0.5;
useEffect(() => { useEffect(() => {
setDeletableFloorItem(null); setDeletableFloorItem(null);
@@ -283,63 +282,48 @@ function Model({ asset }: { readonly asset: Asset }) {
} }
const handleAnimationComplete = useCallback(() => { const handleAnimationComplete = useCallback(() => {
console.log(`Animation "${currentAnimation}" completed`); if (asset.animationState) {
}, [currentAnimation]); setAnimationComplete(asset.modelUuid, true);
}
}, [asset.animationState]);
useFrame((_, delta) => { useFrame((_, delta) => {
if (mixerRef.current) { if (mixerRef.current) {
if (blendFactor < 1) {
setBlendFactor(prev => Math.min(prev + delta / blendDuration, 1));
}
mixerRef.current.update(delta); mixerRef.current.update(delta);
} }
}); });
useEffect(() => { useEffect(() => {
if (asset.animationState && asset.animationState.isPlaying) { if (!asset.animationState || !mixerRef.current) return;
if (!mixerRef.current) return;
if (asset.animationState.current !== currentAnimation) { const { current, loopAnimation, isPlaying } = asset.animationState;
setPreviousAnimation(currentAnimation); const currentAction = actions.current[current];
setCurrentAnimation(asset.animationState.current); const previousAction = previousAnimation ? actions.current[previousAnimation] : null;
setBlendFactor(0);
if (isPlaying && currentAction) {
blendFactor.current = 0;
currentAction.reset();
currentAction.setLoop(loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce, loopAnimation ? Infinity : 1);
currentAction.clampWhenFinished = true;
if (previousAction && previousAction !== currentAction) {
previousAction.crossFadeTo(currentAction, blendDuration, false);
} }
const currentAction = actions.current[asset.animationState.current]; currentAction.play();
const previousAction = previousAnimation ? actions.current[previousAnimation] : null; mixerRef.current.addEventListener('finished', handleAnimationComplete);
setPreviousAnimation(current);
if (currentAction) {
const loopMode = asset.animationState.loopAnimation ? THREE.LoopRepeat : THREE.LoopOnce;
currentAction.reset();
currentAction.setLoop(loopMode, loopMode === THREE.LoopRepeat ? Infinity : 1);
currentAction.play();
mixerRef.current.addEventListener('finished', handleAnimationComplete);
if (previousAction && blendFactor < 1) {
previousAction.crossFadeTo(currentAction, blendDuration, true);
}
}
Object.entries(actions.current).forEach(([name, action]) => {
if ((asset.animationState && name !== asset.animationState.current) && name !== previousAnimation) {
action.stop();
}
});
} else { } else {
Object.values(actions.current).forEach((action) => action.stop()); Object.values(actions.current).forEach((action) => action.stop());
setCurrentAnimation(null);
} }
return () => { return () => {
if (mixerRef.current) { if (mixerRef.current) {
mixerRef.current.removeEventListener('finished', handleAnimationComplete); mixerRef.current.removeEventListener('finished', handleAnimationComplete);
} }
} };
}, [asset.animationState, currentAnimation, previousAnimation, handleAnimationComplete]); }, [asset.animationState?.current, asset.animationState?.isPlaying]);
return ( return (
<group <group

View File

@@ -5,10 +5,10 @@ import { useProductContext } from "../../../products/productContext";
export function useWorkerHandler() { export function useWorkerHandler() {
const { materialStore, humanStore, productStore } = useSceneContext(); const { materialStore, humanStore, productStore } = useSceneContext();
const { getMaterialById } = materialStore(); const { getMaterialById } = materialStore();
const { } = humanStore();
const { getModelUuidByActionUuid } = productStore(); const { getModelUuidByActionUuid } = productStore();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const { incrementHumanLoad, addCurrentMaterial } = humanStore();
const workerLogStatus = (materialUuid: string, status: string) => { const workerLogStatus = (materialUuid: string, status: string) => {
echo.info(`${materialUuid}, ${status}`); echo.info(`${materialUuid}, ${status}`);
@@ -23,6 +23,8 @@ export function useWorkerHandler() {
const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid); const modelUuid = getModelUuidByActionUuid(selectedProduct.productUuid, action.actionUuid);
if (!modelUuid) return; if (!modelUuid) return;
incrementHumanLoad(modelUuid, 1);
addCurrentMaterial(modelUuid, material.materialType, material.materialId);
workerLogStatus(material.materialName, `performing worker action`); workerLogStatus(material.materialName, `performing worker action`);

View File

@@ -15,8 +15,9 @@ interface HumanAnimatorProps {
} }
function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) { function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, startUnloadingProcess }: Readonly<HumanAnimatorProps>) {
const { humanStore } = useSceneContext(); const { humanStore, assetStore } = useSceneContext();
const { getHumanById } = humanStore(); const { getHumanById } = humanStore();
const { setCurrentAnimation } = assetStore();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
@@ -116,6 +117,17 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
const t = (progressRef.current - accumulatedDistance) / segmentDistance; const t = (progressRef.current - accumulatedDistance) / segmentDistance;
const position = start.clone().lerp(end, t); const position = start.clone().lerp(end, t);
object.position.copy(position); object.position.copy(position);
if (human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
}
} else {
if (human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
}
} }
} }
@@ -133,6 +145,11 @@ function HumanAnimator({ path, handleCallBack, currentPhase, human, reset, start
object.rotation.copy(targetEuler); object.rotation.copy(targetEuler);
setRestingRotation(false); setRestingRotation(false);
} }
if (human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'idle_with_box', true, true, true);
} else {
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
}
return; return;
} }
} }

View File

@@ -0,0 +1,44 @@
import { useEffect, useRef, useState } from 'react';
import { useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel';
const MaterialAnimator = ({ human }: { human: HumanStatus }) => {
const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false);
const { scene } = useThree();
useEffect(() => {
setHasLoad(human.currentLoad > 0);
}, [human.currentLoad]);
useEffect(() => {
if (!hasLoad || !meshRef.current) return;
const humanModel = scene.getObjectByProperty("uuid", human.modelUuid) as THREE.Object3D;
if (!humanModel) return;
const bone = humanModel.getObjectByName('PlaceObjectRefBone') as THREE.Bone;
if (bone) {
bone.add(meshRef.current);
meshRef.current.position.set(0, 0, 0);
meshRef.current.rotation.set(0, 0, 0);
meshRef.current.scale.set(1, 1, 1);
}
}, [hasLoad, human.modelUuid]);
return (
<>
{hasLoad && human.currentMaterials.length > 0 && (
<MaterialModel
matRef={meshRef}
materialId={human.currentMaterials[0].materialId || ''}
materialType={human.currentMaterials[0].materialType || 'Default material'}
/>
)}
</>
);
};
export default MaterialAnimator;

View File

@@ -8,21 +8,24 @@ import { useSceneContext } from '../../../../scene/sceneContext';
import { useProductContext } from '../../../products/productContext'; import { useProductContext } from '../../../products/productContext';
import HumanAnimator from '../animator/humanAnimator'; import HumanAnimator from '../animator/humanAnimator';
import MaterialAnimator from '../animator/materialAnimator';
function HumanInstance({ human }: { human: HumanStatus }) { function HumanInstance({ human }: { human: HumanStatus }) {
const { navMesh } = useNavMesh(); const { navMesh } = useNavMesh();
const { isPlaying } = usePlayButtonStore(); const { isPlaying } = usePlayButtonStore();
const { materialStore, armBotStore, conveyorStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext(); const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
const { removeMaterial, setEndTime } = materialStore(); const { removeMaterial, setEndTime } = materialStore();
const { getStorageUnitById } = storageUnitStore(); const { getStorageUnitById } = storageUnitStore();
const { getArmBotById } = armBotStore(); const { getArmBotById } = armBotStore();
const { getConveyorById } = conveyorStore(); const { getConveyorById } = conveyorStore();
const { getVehicleById } = vehicleStore(); const { getVehicleById } = vehicleStore();
const { getMachineById } = machineStore();
const { triggerPointActions } = useTriggerHandler(); const { triggerPointActions } = useTriggerHandler();
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore(); const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
const { selectedProductStore } = useProductContext(); const { selectedProductStore } = useProductContext();
const { selectedProduct } = selectedProductStore(); const { selectedProduct } = selectedProductStore();
const { humans, setHumanActive, setHumanState, setHumanPicking, clearCurrentMaterials, setHumanLoad, decrementHumanLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore(); const { setHumanActive, setHumanState, setHumanPicking, clearCurrentMaterials, setHumanLoad, decrementHumanLoad, removeLastMaterial, getLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore();
const [currentPhase, setCurrentPhase] = useState<string>('init'); const [currentPhase, setCurrentPhase] = useState<string>('init');
const [path, setPath] = useState<[number, number, number][]>([]); const [path, setPath] = useState<[number, number, number][]>([]);
@@ -32,11 +35,11 @@ function HumanInstance({ human }: { human: HumanStatus }) {
const isPausedRef = useRef<boolean>(false); const isPausedRef = useRef<boolean>(false);
const isSpeedRef = useRef<number>(0); const isSpeedRef = useRef<number>(0);
let startTime: number; let startTime: number;
let fixedInterval: number;
const { speed } = useAnimationPlaySpeed(); const { speed } = useAnimationPlaySpeed();
const { isPaused } = usePauseButtonStore(); const { isPaused } = usePauseButtonStore();
const previousTimeRef = useRef<number | null>(null); const previousTimeRef = useRef<number | null>(null);
const animationFrameIdRef = useRef<number | null>(null); const animationFrameIdRef = useRef<number | null>(null);
const humanAsset = getAssetById(human.modelUuid);
useEffect(() => { useEffect(() => {
isPausedRef.current = isPaused; isPausedRef.current = isPaused;
@@ -69,7 +72,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
}, [navMesh]); }, [navMesh]);
function humanStatus(modelId: string, status: string) { function humanStatus(modelId: string, status: string) {
console.log(`${modelId} , ${status}`); // console.log(`${modelId} , ${status}`);
} }
function reset() { function reset() {
@@ -78,6 +81,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
setHumanPicking(human.modelUuid, false); setHumanPicking(human.modelUuid, false);
setHumanState(human.modelUuid, 'idle'); setHumanState(human.modelUuid, 'idle');
setHumanLoad(human.modelUuid, 0); setHumanLoad(human.modelUuid, 0);
resetAnimation(human.modelUuid);
setPath([]); setPath([]);
startTime = 0; startTime = 0;
isPausedRef.current = false; isPausedRef.current = false;
@@ -110,8 +114,57 @@ function HumanInstance({ human }: { human: HumanStatus }) {
setHumanState(human.modelUuid, 'running'); setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false); setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, true); setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from init, heading to pickup'); humanStatus(human.modelUuid, 'Started from init, heading to pickup');
return; return;
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') {
if (humanAsset && human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
const toDrop = computePath(
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
)
);
setPath(toDrop);
setCurrentPhase('pickup-drop');
setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false);
setHumanPicking(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
}
} else if (human.currentLoad === human.point.action.loadCapacity && human.currentMaterials.length > 0) {
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
}
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
if (human.point.action.pickUpPoint && human.point.action.dropPoint) {
const dropToPickup = computePath(
new THREE.Vector3(
human.point.action.dropPoint.position?.[0] ?? 0,
human.point.action.dropPoint.position?.[1] ?? 0,
human.point.action.dropPoint.position?.[2] ?? 0
),
new THREE.Vector3(
human.point.action.pickUpPoint.position?.[0] ?? 0,
human.point.action.pickUpPoint.position?.[1] ?? 0,
human.point.action.pickUpPoint.position?.[2] ?? 0
)
);
setPath(dropToPickup);
setCurrentPhase('drop-pickup');
setHumanState(human.modelUuid, 'running');
setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, true);
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
}
} }
} }
@@ -119,17 +172,488 @@ function HumanInstance({ human }: { human: HumanStatus }) {
reset() reset()
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [human, currentPhase, path, isPlaying]); }, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
function handleCallBack() { function handleCallBack() {
if (currentPhase === 'init-pickup') { if (currentPhase === 'init-pickup') {
setCurrentPhase('picking'); setCurrentPhase('picking');
setHumanState(human.modelUuid, 'idle');
setHumanPicking(human.modelUuid, true);
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
setPath([]);
} else if (currentPhase === 'pickup-drop') { } else if (currentPhase === 'pickup-drop') {
setCurrentPhase('dropping');
setHumanState(human.modelUuid, 'idle');
setHumanPicking(human.modelUuid, false);
setHumanActive(human.modelUuid, false);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
humanStatus(human.modelUuid, 'Reached drop point');
setPath([]);
} else if (currentPhase === 'drop-pickup') { } else if (currentPhase === 'drop-pickup') {
setCurrentPhase('picking');
setHumanState(human.modelUuid, 'idle');
setHumanPicking(human.modelUuid, true);
setHumanActive(human.modelUuid, false);
setPath([]);
clearCurrentMaterials(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
humanStatus(human.modelUuid, 'Reached pickup point again, cycle complete');
} }
} }
function animate(currentTime: number) {
if (previousTimeRef.current === null) {
previousTimeRef.current = currentTime;
}
const deltaTime = (currentTime - previousTimeRef.current) / 1000;
previousTimeRef.current = currentTime;
if (human.isActive) {
if (!isPausedRef.current) {
activeTimeRef.current += deltaTime * isSpeedRef.current;
}
} else {
if (!isPausedRef.current) {
idleTimeRef.current += deltaTime * isSpeedRef.current;
}
}
animationFrameIdRef.current = requestAnimationFrame(animate);
}
useEffect(() => {
if (!isPlaying) return
if (!human.isActive) {
const roundedActiveTime = Math.round(activeTimeRef.current);
incrementActiveTime(human.modelUuid, roundedActiveTime);
activeTimeRef.current = 0;
} else {
const roundedIdleTime = Math.round(idleTimeRef.current);
incrementIdleTime(human.modelUuid, roundedIdleTime);
idleTimeRef.current = 0;
}
if (animationFrameIdRef.current === null) {
animationFrameIdRef.current = requestAnimationFrame(animate);
}
return () => {
if (animationFrameIdRef.current !== null) {
cancelAnimationFrame(animationFrameIdRef.current);
animationFrameIdRef.current = null;
}
};
}, [human, isPlaying]);
function startUnloadingProcess() { function startUnloadingProcess() {
const humanAsset = getAssetById(human.modelUuid);
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
if (human.point.action.triggers.length > 0) {
const trigger = getTriggerByUuid(selectedProduct.productUuid, human.point.action.triggers[0]?.triggerUuid);
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
if (trigger && model) {
if (model.type === 'transfer') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToConveyor(model);
}
} else if (model.type === 'machine') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToMachine(model);
}
} else if (model.type === 'roboticArm') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToArmBot(model);
}
} else if (model.type === 'storageUnit') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToStorageUnit(model);
}
} else if (model.type === 'vehicle') {
const action = getActionByUuid(selectedProduct.productUuid, human.point.action.actionUuid);
if (action) {
handleMaterialDropToVehicle(model);
}
}
} else {
const droppedMaterial = human.currentLoad;
startTime = performance.now();
handleMaterialDropByDefault(droppedMaterial);
}
} else {
const droppedMaterial = human.currentLoad;
startTime = performance.now();
handleMaterialDropByDefault(droppedMaterial);
}
} else {
requestAnimationFrame(startUnloadingProcess);
}
}
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
if (model && humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
if (model.point.action.actionType === 'store') {
loopMaterialDropToStorage(
human.modelUuid,
human.currentLoad,
model.modelUuid,
model.point.action.storageCapacity,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToStorage(
humanId: string,
humanCurrentLoad: number,
storageUnitId: string,
storageMaxCapacity: number,
action: HumanAction
) {
const storageUnit = getStorageUnitById(storageUnitId);
if (!storageUnit || humanCurrentLoad <= 0 || storageUnit.currentLoad >= storageMaxCapacity) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0 && storageUnit.currentLoad < storageMaxCapacity) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextDrop = () => {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToStorage(
humanId,
humanCurrentLoad,
storageUnitId,
storageMaxCapacity,
action
);
} else {
requestAnimationFrame(waitForNextDrop);
}
};
waitForNextDrop();
}
}
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
const conveyor = getConveyorById(model.modelUuid);
if (conveyor) {
loopMaterialDropToConveyor(
human.modelUuid,
human.currentLoad,
conveyor.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToConveyor(
humanId: string,
humanCurrentLoad: number,
conveyorId: string,
action: HumanAction
) {
const conveyor = getConveyorById(conveyorId);
if (!conveyor || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextDrop = () => {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToConveyor(
humanId,
humanCurrentLoad,
conveyorId,
action
);
} else {
requestAnimationFrame(waitForNextDrop);
}
};
waitForNextDrop();
}
}
function handleMaterialDropToArmBot(model: RoboticArmEventSchema) {
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
const armBot = getArmBotById(model.modelUuid);
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
loopMaterialDropToArmBot(
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToArmBot(
humanId: string,
humanCurrentLoad: number,
armBotId: string,
action: HumanAction
) {
const armBot = getArmBotById(armBotId);
if (!armBot || armBot.state !== 'idle' || armBot.isActive || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextTransfer = () => {
const currentArmBot = getArmBotById(armBotId);
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToArmBot(
humanId,
humanCurrentLoad,
armBotId,
action
);
} else {
requestAnimationFrame(waitForNextTransfer);
}
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}
}
function handleMaterialDropToVehicle(model: VehicleEventSchema) {
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
const vehicle = getVehicleById(model.modelUuid);
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
loopMaterialDropToVehicle(
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToVehicle(
humanId: string,
humanCurrentLoad: number,
vehicleId: string,
action: HumanAction
) {
const vehicle = getVehicleById(vehicleId);
if (!vehicle || vehicle.state !== 'idle' || vehicle.isActive || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextTransfer = () => {
const currentArmBot = getVehicleById(vehicleId);
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToArmBot(
humanId,
humanCurrentLoad,
vehicleId,
action
);
} else {
requestAnimationFrame(waitForNextTransfer);
}
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}
}
function handleMaterialDropToMachine(model: MachineEventSchema) {
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
const checkAnimation = () => {
if (humanAsset?.animationState?.isCompleted) {
const machine = getMachineById(model.modelUuid);
if (machine && machine.state === 'idle' && !machine.isActive) {
loopMaterialDropToMachine(
human.modelUuid,
human.currentLoad,
model.modelUuid,
human.point.action
);
}
} else {
requestAnimationFrame(checkAnimation);
}
};
checkAnimation();
}
function loopMaterialDropToMachine(
humanId: string,
humanCurrentLoad: number,
machineId: string,
action: HumanAction
) {
const machine = getMachineById(machineId);
if (!machine || machine.state !== 'idle' || machine.isActive || humanCurrentLoad <= 0) {
return;
}
decrementHumanLoad(humanId, 1);
humanCurrentLoad -= 1;
const material = removeLastMaterial(humanId);
if (material) {
triggerPointActions(action, material.materialId);
}
if (humanCurrentLoad > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
const waitForNextTransfer = () => {
const currentArmBot = getMachineById(machineId);
if (currentArmBot && currentArmBot.state === 'idle' && !currentArmBot.isActive) {
if (humanAsset?.animationState?.isCompleted) {
loopMaterialDropToArmBot(
humanId,
humanCurrentLoad,
machineId,
action
);
} else {
requestAnimationFrame(waitForNextTransfer);
}
} else {
requestAnimationFrame(waitForNextTransfer);
}
};
waitForNextTransfer();
}
}
function handleMaterialDropByDefault(droppedMaterial: number) {
if (humanAsset?.animationState?.current !== 'drop') {
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
}
if (humanAsset?.animationState?.isCompleted) {
const remainingMaterials = droppedMaterial - 1;
decrementHumanLoad(human.modelUuid, 1);
const material = removeLastMaterial(human.modelUuid);
if (material) {
setEndTime(material.materialId, performance.now());
removeMaterial(material.materialId);
}
if (remainingMaterials > 0) {
resetAnimation(human.modelUuid);
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
requestAnimationFrame(() => handleMaterialDropByDefault(remainingMaterials));
}
return;
}
requestAnimationFrame(() => handleMaterialDropByDefault(droppedMaterial));
} }
return ( return (
@@ -144,6 +668,7 @@ function HumanInstance({ human }: { human: HumanStatus }) {
startUnloadingProcess={startUnloadingProcess} startUnloadingProcess={startUnloadingProcess}
/> />
<MaterialAnimator human={human} />
</> </>
) )
} }

View File

@@ -264,7 +264,6 @@ export function useTriggerHandler() {
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) { if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId); const material = getMaterialById(materialId);
if (material) { if (material) {
const triggeredAction = action;
// Handle current action of the material // Handle current action of the material
handleAction(action, materialId); handleAction(action, materialId);
@@ -291,7 +290,7 @@ export function useTriggerHandler() {
if (human) { if (human) {
if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < (triggeredAction as HumanAction).loadCapacity) { if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
setIsVisible(materialId, false); setIsVisible(materialId, false);
@@ -302,6 +301,9 @@ export function useTriggerHandler() {
// Handle current action using Event Manager // Handle current action using Event Manager
addHumanToMonitor(human.modelUuid, () => { addHumanToMonitor(human.modelUuid, () => {
setIsVisible(materialId, false);
handleAction(action, materialId); handleAction(action, materialId);
}) })
} }
@@ -433,6 +435,9 @@ export function useTriggerHandler() {
} }
} }
} }
} else if (toEvent?.type === 'human') {
// Vehicle to Human
} }
} else if (fromEvent?.type === 'machine') { } else if (fromEvent?.type === 'machine') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -539,6 +544,9 @@ export function useTriggerHandler() {
} else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// Machine to Storage Unit // Machine to Storage Unit
} else if (toEvent?.type === 'human') {
// Machine to Human
} }
} else if (fromEvent?.type === 'roboticArm') { } else if (fromEvent?.type === 'roboticArm') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -812,6 +820,53 @@ export function useTriggerHandler() {
} }
} }
} }
} else if (toEvent?.type === 'human') {
// Robotic Arm to Vehicle
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
setIsPaused(material.materialId, false);
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const human = getHumanById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setNextLocation(material.materialId, null);
if (action) {
if (human) {
if (human.isActive === false && human.state === 'idle' && human.isPicking && human.currentLoad < human.point.action.loadCapacity) {
setIsVisible(materialId, false);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
// Handle current action from human
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
}
} }
} else if (fromEvent?.type === 'storageUnit') { } else if (fromEvent?.type === 'storageUnit') {
if (toEvent?.type === 'transfer') { if (toEvent?.type === 'transfer') {
@@ -829,6 +884,135 @@ export function useTriggerHandler() {
} else if (toEvent?.type === 'storageUnit') { } else if (toEvent?.type === 'storageUnit') {
// Storage Unit to Storage Unit // Storage Unit to Storage Unit
} else if (toEvent?.type === 'human') {
// Storage Unit to Human
}
} else if (fromEvent?.type === 'human') {
if (toEvent?.type === 'transfer') {
// Human to Transfer
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setIsVisible(materialId, true);
if (action &&
action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid &&
action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid
) {
setNextLocation(material.materialId, {
modelUuid: action.triggers[0]?.triggeredAsset?.triggeredModel.modelUuid,
pointUuid: action.triggers[0]?.triggeredAsset?.triggeredPoint?.pointUuid,
});
handleAction(action, materialId);
}
}
}
} else if (toEvent?.type === 'vehicle') {
// Human to Vehicle
} else if (toEvent?.type === 'machine') {
// Human to Machine
} else if (toEvent?.type === 'roboticArm') {
// Human to Robotic Arm
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const armBot = getArmBotById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
if (action && armBot) {
if (armBot.isActive === false && armBot.state === 'idle') {
// Handle current action from arm bot
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
} else if (toEvent?.type === 'storageUnit') {
// Human to Storage Unit
if (materialId && trigger.triggeredAsset && trigger.triggeredAsset.triggeredPoint && trigger.triggeredAsset.triggeredAction) {
const material = getMaterialById(materialId);
if (material) {
const action = getActionByUuid(selectedProduct.productUuid, trigger.triggeredAsset.triggeredAction.actionUuid);
const storageUnit = getStorageUnitById(trigger.triggeredAsset?.triggeredModel.modelUuid);
setPreviousLocation(material.materialId, {
modelUuid: material.current.modelUuid,
pointUuid: material.current.pointUuid,
actionUuid: material.current.actionUuid,
})
setCurrentLocation(material.materialId, {
modelUuid: trigger.triggeredAsset.triggeredModel.modelUuid,
pointUuid: trigger.triggeredAsset.triggeredPoint.pointUuid,
actionUuid: trigger.triggeredAsset?.triggeredAction?.actionUuid,
});
setNextLocation(material.materialId, null);
setIsVisible(materialId, false);
if (action && storageUnit) {
if (storageUnit.currentLoad < storageUnit.point.action.storageCapacity) {
// Handle current action from vehicle
handleAction(action, materialId);
} else {
// Event Manager Needed
}
}
}
}
} }
} }
} }

View File

@@ -3,12 +3,7 @@ import { useThree, useFrame } from '@react-three/fiber';
import * as THREE from 'three'; import * as THREE from 'three';
import { MaterialModel } from '../../../materials/instances/material/materialModel'; import { MaterialModel } from '../../../materials/instances/material/materialModel';
type MaterialAnimatorProps = { const MaterialAnimator = ({ agvDetail }: { agvDetail: VehicleStatus }) => {
agvDetail: VehicleStatus;
};
const MaterialAnimator = ({ agvDetail }: MaterialAnimatorProps) => {
const meshRef = useRef<any>(null!); const meshRef = useRef<any>(null!);
const [hasLoad, setHasLoad] = useState(false); const [hasLoad, setHasLoad] = useState(false);
const { scene } = useThree(); const { scene } = useThree();

View File

@@ -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, isisPlaying: boolean, loopAnimation: boolean) => void; setCurrentAnimation: (modelUuid: string, current: string, isPlaying: boolean, loopAnimation: boolean, isCompleted: boolean) => void;
setAnimationComplete: (modelUuid: string, isCompleted: boolean) => void;
resetAnimation: (modelUuid: string) => 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;
@@ -150,19 +151,29 @@ export const createAssetStore = () => {
if (asset) { if (asset) {
asset.animations = animations; asset.animations = animations;
if (!asset.animationState) { if (!asset.animationState) {
asset.animationState = { current: '', isPlaying: false, loopAnimation: true }; asset.animationState = { current: '', isPlaying: false, loopAnimation: true, isCompleted: true };
} }
} }
}); });
}, },
setCurrentAnimation: (modelUuid, current, isisPlaying, loopAnimation) => { setCurrentAnimation: (modelUuid, current, isPlaying, loopAnimation, isCompleted) => {
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.isPlaying = isisPlaying; asset.animationState.isPlaying = isPlaying;
asset.animationState.loopAnimation = loopAnimation; asset.animationState.loopAnimation = loopAnimation;
asset.animationState.isCompleted = isCompleted;
}
});
},
setAnimationComplete: (modelUuid, isCompleted) => {
set((state) => {
const asset = state.assets.find(a => a.modelUuid === modelUuid);
if (asset?.animationState) {
asset.animationState.isCompleted = isCompleted;
} }
}); });
}, },
@@ -171,7 +182,7 @@ export const createAssetStore = () => {
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: '', isPlaying: false, loopAnimation: true }; asset.animationState = { current: '', isPlaying: false, loopAnimation: true, isCompleted: true };
} }
}); });
}, },

View File

@@ -160,7 +160,7 @@ export const createHumanStore = () => {
set((state) => { set((state) => {
const human = state.humans.find(h => h.modelUuid === modelUuid); const human = state.humans.find(h => h.modelUuid === modelUuid);
if (human && human.currentMaterials.length > 0) { if (human && human.currentMaterials.length > 0) {
removed = human.currentMaterials.pop(); removed = JSON.parse(JSON.stringify(human.currentMaterials.pop()));
} }
}); });
return removed; return removed;

View File

@@ -28,6 +28,7 @@ interface Asset {
current: string; current: string;
isPlaying: boolean; isPlaying: boolean;
loopAnimation: boolean; loopAnimation: boolean;
isCompleted: boolean;
}; };
eventData?: { eventData?: {
type: string; type: string;