831 lines
35 KiB
TypeScript
831 lines
35 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
import * as THREE from 'three';
|
|
import { useThree } from '@react-three/fiber';
|
|
import { NavMeshQuery } from '@recast-navigation/core';
|
|
import { useNavMesh } from '../../../../../store/builder/store';
|
|
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
|
import { useTriggerHandler } from '../../../triggers/triggerHandler/useTriggerHandler';
|
|
import { useSceneContext } from '../../../../scene/sceneContext';
|
|
import { useProductContext } from '../../../products/productContext';
|
|
|
|
import HumanAnimator from '../animator/humanAnimator';
|
|
import MaterialAnimator from '../animator/materialAnimator';
|
|
|
|
function HumanInstance({ human }: { human: HumanStatus }) {
|
|
const { navMesh } = useNavMesh();
|
|
const { isPlaying } = usePlayButtonStore();
|
|
const { scene } = useThree();
|
|
const { assetStore, materialStore, armBotStore, conveyorStore, machineStore, vehicleStore, humanStore, storageUnitStore, productStore } = useSceneContext();
|
|
const { removeMaterial, setEndTime, setMaterial, setIsVisible } = materialStore();
|
|
const { getStorageUnitById } = storageUnitStore();
|
|
const { getArmBotById } = armBotStore();
|
|
const { getConveyorById } = conveyorStore();
|
|
const { getVehicleById } = vehicleStore();
|
|
const { getMachineById } = machineStore();
|
|
const { triggerPointActions } = useTriggerHandler();
|
|
const { setCurrentAnimation, resetAnimation, getAssetById } = assetStore();
|
|
const { getActionByUuid, getEventByModelUuid, getTriggerByUuid } = productStore();
|
|
const { selectedProductStore } = useProductContext();
|
|
const { selectedProduct } = selectedProductStore();
|
|
const { setHumanActive, setHumanState, clearCurrentMaterials, setHumanLoad, setHumanScheduled, decrementHumanLoad, removeLastMaterial, incrementIdleTime, incrementActiveTime, resetTime } = humanStore();
|
|
|
|
const [currentPhase, setCurrentPhase] = useState<string>('init');
|
|
const [path, setPath] = useState<[number, number, number][]>([]);
|
|
const pauseTimeRef = useRef<number | null>(null);
|
|
const idleTimeRef = useRef<number>(0);
|
|
const activeTimeRef = useRef<number>(0);
|
|
const isPausedRef = useRef<boolean>(false);
|
|
const isSpeedRef = useRef<number>(0);
|
|
const { speed } = useAnimationPlaySpeed();
|
|
const { isPaused } = usePauseButtonStore();
|
|
const previousTimeRef = useRef<number | null>(null);
|
|
const animationFrameIdRef = useRef<number | null>(null);
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
const processStartTimeRef = useRef<number | null>(null);
|
|
const processTimeRef = useRef<number>(0);
|
|
const processAnimationIdRef = useRef<number | null>(null);
|
|
const accumulatedPausedTimeRef = useRef<number>(0);
|
|
const lastPauseTimeRef = useRef<number | null>(null);
|
|
const hasLoggedHalfway = useRef(false);
|
|
const hasLoggedCompleted = useRef(false);
|
|
|
|
useEffect(() => {
|
|
isPausedRef.current = isPaused;
|
|
}, [isPaused]);
|
|
|
|
useEffect(() => {
|
|
isSpeedRef.current = speed;
|
|
}, [speed]);
|
|
|
|
const computePath = useCallback(
|
|
(start: any, end: any) => {
|
|
try {
|
|
const navMeshQuery = new NavMeshQuery(navMesh);
|
|
const { path: segmentPath } = navMeshQuery.computePath(start, end);
|
|
if (
|
|
segmentPath.length > 0 &&
|
|
Math.round(segmentPath[segmentPath.length - 1].x) == Math.round(end.x) &&
|
|
Math.round(segmentPath[segmentPath.length - 1].z) == Math.round(end.z)
|
|
) {
|
|
return segmentPath?.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
|
} else {
|
|
console.log("There is no path here...Choose valid path")
|
|
const { path: segmentPaths } = navMeshQuery.computePath(start, start);
|
|
return segmentPaths.map(({ x, y, z }) => [x, 0, z] as [number, number, number]) || [];
|
|
}
|
|
} catch {
|
|
console.error("Failed to compute path");
|
|
return [];
|
|
}
|
|
}, [navMesh]);
|
|
|
|
function humanStatus(modelId: string, status: string) {
|
|
// console.log(`${modelId} , ${status}`);
|
|
}
|
|
|
|
function reset() {
|
|
setCurrentPhase('init');
|
|
setHumanActive(human.modelUuid, false);
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setHumanScheduled(human.modelUuid, false);
|
|
setHumanLoad(human.modelUuid, 0);
|
|
resetAnimation(human.modelUuid);
|
|
setPath([]);
|
|
isPausedRef.current = false;
|
|
pauseTimeRef.current = 0;
|
|
resetTime(human.modelUuid)
|
|
activeTimeRef.current = 0
|
|
idleTimeRef.current = 0
|
|
previousTimeRef.current = null
|
|
if (animationFrameIdRef.current !== null) {
|
|
cancelAnimationFrame(animationFrameIdRef.current)
|
|
animationFrameIdRef.current = null
|
|
}
|
|
if (processAnimationIdRef.current) {
|
|
cancelAnimationFrame(processAnimationIdRef.current);
|
|
processAnimationIdRef.current = null;
|
|
}
|
|
processStartTimeRef.current = null;
|
|
processTimeRef.current = 0;
|
|
accumulatedPausedTimeRef.current = 0;
|
|
lastPauseTimeRef.current = null;
|
|
hasLoggedHalfway.current = false;
|
|
hasLoggedCompleted.current = false;
|
|
const object = scene.getObjectByProperty('uuid', human.modelUuid);
|
|
if (object && human) {
|
|
object.position.set(human.position[0], human.position[1], human.position[2]);
|
|
object.rotation.set(human.rotation[0], human.rotation[1], human.rotation[2]);
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (isPlaying) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
|
|
if (!action || !(action as HumanAction).assemblyPoint || (action as HumanAction).actionType === 'worker') return;
|
|
|
|
if (!human.isActive && human.state === 'idle' && (currentPhase === 'init' || currentPhase === 'picking')) {
|
|
|
|
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
|
if (!humanMesh) return;
|
|
|
|
const toPickupPath = computePath(
|
|
new THREE.Vector3(...humanMesh.position.toArray()),
|
|
new THREE.Vector3(
|
|
(action as HumanAction)?.assemblyPoint?.position?.[0] ?? 0,
|
|
(action as HumanAction)?.assemblyPoint?.position?.[1] ?? 0,
|
|
(action as HumanAction)?.assemblyPoint?.position?.[2] ?? 0
|
|
)
|
|
);
|
|
setPath(toPickupPath);
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setCurrentPhase('init_assembly');
|
|
setHumanActive(human.modelUuid, false);
|
|
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
|
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
|
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'waiting') {
|
|
|
|
if (human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current !== 'working_standing') {
|
|
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, false);
|
|
setHumanState(human.modelUuid, 'running');
|
|
setCurrentPhase('assembling');
|
|
setHumanActive(human.modelUuid, true);
|
|
|
|
processStartTimeRef.current = performance.now();
|
|
processTimeRef.current = (action as HumanAction).processTime || 0;
|
|
accumulatedPausedTimeRef.current = 0;
|
|
lastPauseTimeRef.current = null;
|
|
hasLoggedHalfway.current = false;
|
|
hasLoggedCompleted.current = false;
|
|
|
|
if (!processAnimationIdRef.current) {
|
|
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
|
}
|
|
}
|
|
} else if (human.isActive && human.state === 'running' && human.currentMaterials.length > 0 && humanAsset && humanAsset.animationState?.current === 'working_standing' && humanAsset.animationState?.isCompleted) {
|
|
if ((action as HumanAction).assemblyPoint && currentPhase === 'assembling') {
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setCurrentPhase('waiting');
|
|
setHumanActive(human.modelUuid, false);
|
|
setHumanScheduled(human.modelUuid, false);
|
|
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
|
humanStatus(human.modelUuid, 'Human is waiting for material in assembly');
|
|
|
|
decrementHumanLoad(human.modelUuid, 1);
|
|
const material = removeLastMaterial(human.modelUuid);
|
|
if (material) {
|
|
triggerPointActions((action as HumanAction), material.materialId);
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
reset()
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
|
|
|
const trackAssemblyProcess = useCallback(() => {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
|
|
const now = performance.now();
|
|
|
|
if (!processStartTimeRef.current || !(action as HumanAction).processTime || !action) {
|
|
return;
|
|
}
|
|
|
|
if (isPausedRef.current) {
|
|
if (!lastPauseTimeRef.current) {
|
|
lastPauseTimeRef.current = now;
|
|
}
|
|
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
|
return;
|
|
} else if (lastPauseTimeRef.current) {
|
|
accumulatedPausedTimeRef.current += now - lastPauseTimeRef.current;
|
|
lastPauseTimeRef.current = null;
|
|
}
|
|
|
|
const elapsed = (now - processStartTimeRef.current - accumulatedPausedTimeRef.current) * isSpeedRef.current;
|
|
const totalProcessTimeMs = ((action as HumanAction).processTime || 1) * 1000;
|
|
|
|
if (elapsed >= totalProcessTimeMs / 2 && !hasLoggedHalfway.current) {
|
|
hasLoggedHalfway.current = true;
|
|
if (human.currentMaterials.length > 0) {
|
|
setMaterial(human.currentMaterials[0].materialId, (action as HumanAction).swapMaterial || 'Default Material');
|
|
}
|
|
humanStatus(human.modelUuid, `🟡 Human ${human.modelUuid} reached halfway in assembly.`);
|
|
}
|
|
|
|
if (elapsed >= totalProcessTimeMs && !hasLoggedCompleted.current) {
|
|
hasLoggedCompleted.current = true;
|
|
setCurrentAnimation(human.modelUuid, 'working_standing', true, true, true);
|
|
if (processAnimationIdRef.current) {
|
|
cancelAnimationFrame(processAnimationIdRef.current);
|
|
processAnimationIdRef.current = null;
|
|
}
|
|
humanStatus(human.modelUuid, `✅ Human ${human.modelUuid} completed assembly process.`);
|
|
return;
|
|
}
|
|
|
|
processAnimationIdRef.current = requestAnimationFrame(trackAssemblyProcess);
|
|
}, [human.modelUuid, human.currentMaterials]);
|
|
|
|
useEffect(() => {
|
|
if (isPlaying) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
if (!action || action.actionType !== 'worker' || !action.pickUpPoint || !action.dropPoint) return;
|
|
|
|
if (!human.isActive && human.state === 'idle' && (currentPhase === 'init' || currentPhase === 'waiting')) {
|
|
|
|
const humanMesh = scene.getObjectByProperty('uuid', human.modelUuid);
|
|
if (!humanMesh) return;
|
|
|
|
const toPickupPath = computePath(
|
|
new THREE.Vector3(...humanMesh.position.toArray()),
|
|
new THREE.Vector3(
|
|
action?.pickUpPoint?.position?.[0] ?? 0,
|
|
action?.pickUpPoint?.position?.[1] ?? 0,
|
|
action?.pickUpPoint?.position?.[2] ?? 0
|
|
)
|
|
);
|
|
setPath(toPickupPath);
|
|
setCurrentPhase('init-pickup');
|
|
setHumanState(human.modelUuid, 'running');
|
|
setHumanActive(human.modelUuid, true);
|
|
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
|
humanStatus(human.modelUuid, 'Started from init, heading to pickup');
|
|
return;
|
|
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'picking') {
|
|
if (humanAsset && human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset.animationState?.current === 'pickup' && humanAsset.animationState?.isCompleted) {
|
|
if (action.pickUpPoint && action.dropPoint) {
|
|
const toDrop = computePath(
|
|
new THREE.Vector3(
|
|
action.pickUpPoint.position?.[0] ?? 0,
|
|
action.pickUpPoint.position?.[1] ?? 0,
|
|
action.pickUpPoint.position?.[2] ?? 0
|
|
),
|
|
new THREE.Vector3(
|
|
action.dropPoint.position?.[0] ?? 0,
|
|
action.dropPoint.position?.[1] ?? 0,
|
|
action.dropPoint.position?.[2] ?? 0
|
|
)
|
|
);
|
|
setPath(toDrop);
|
|
setCurrentPhase('pickup-drop');
|
|
setHumanState(human.modelUuid, 'running');
|
|
setCurrentAnimation(human.modelUuid, 'walk_with_box', true, true, true);
|
|
humanStatus(human.modelUuid, 'Started from pickup point, heading to drop point');
|
|
}
|
|
} else if (human.currentLoad === action.loadCapacity && human.currentMaterials.length > 0 && humanAsset?.animationState?.current !== 'pickup') {
|
|
if (human.currentMaterials[0]?.materialId) {
|
|
setIsVisible(human.currentMaterials[0]?.materialId, false);
|
|
}
|
|
setCurrentAnimation(human.modelUuid, 'pickup', true, false, false);
|
|
}
|
|
} else if (!human.isActive && human.state === 'idle' && currentPhase === 'dropping' && human.currentLoad === 0) {
|
|
if (action.pickUpPoint && action.dropPoint) {
|
|
const dropToPickup = computePath(
|
|
new THREE.Vector3(
|
|
action.dropPoint.position?.[0] ?? 0,
|
|
action.dropPoint.position?.[1] ?? 0,
|
|
action.dropPoint.position?.[2] ?? 0
|
|
),
|
|
new THREE.Vector3(
|
|
action.pickUpPoint.position?.[0] ?? 0,
|
|
action.pickUpPoint.position?.[1] ?? 0,
|
|
action.pickUpPoint.position?.[2] ?? 0
|
|
)
|
|
);
|
|
setPath(dropToPickup);
|
|
setCurrentPhase('drop-pickup');
|
|
setHumanState(human.modelUuid, 'running');
|
|
setHumanActive(human.modelUuid, true);
|
|
setCurrentAnimation(human.modelUuid, 'walking', true, true, true);
|
|
humanStatus(human.modelUuid, 'Started from dropping point, heading to pickup point');
|
|
}
|
|
}
|
|
|
|
} else {
|
|
reset()
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [human, currentPhase, path, isPlaying, humanAsset?.animationState?.isCompleted]);
|
|
|
|
function handleCallBack() {
|
|
if (currentPhase === 'init-pickup') {
|
|
setCurrentPhase('picking');
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setHumanActive(human.modelUuid, false);
|
|
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
|
humanStatus(human.modelUuid, 'Reached pickup point, waiting for material');
|
|
setPath([]);
|
|
} if (currentPhase === 'init_assembly') {
|
|
setCurrentPhase('waiting');
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setHumanActive(human.modelUuid, false);
|
|
setCurrentAnimation(human.modelUuid, 'idle', true, true, true);
|
|
humanStatus(human.modelUuid, 'Reached assembly point, waiting for material');
|
|
setPath([]);
|
|
} else if (currentPhase === 'pickup-drop') {
|
|
setCurrentPhase('dropping');
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setHumanActive(human.modelUuid, false);
|
|
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
|
humanStatus(human.modelUuid, 'Reached drop point');
|
|
setPath([]);
|
|
} else if (currentPhase === 'drop-pickup') {
|
|
setCurrentPhase('picking');
|
|
setHumanState(human.modelUuid, 'idle');
|
|
setHumanActive(human.modelUuid, false);
|
|
setHumanScheduled(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() {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
if (humanAsset?.animationState?.current !== 'drop') {
|
|
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
|
}
|
|
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
|
if ((action as HumanAction).triggers.length > 0) {
|
|
const trigger = getTriggerByUuid(selectedProduct.productUuid, (action as HumanAction).triggers[0]?.triggerUuid);
|
|
const model = getEventByModelUuid(selectedProduct.productUuid, trigger?.triggeredAsset?.triggeredModel?.modelUuid || '');
|
|
|
|
if (trigger && model) {
|
|
if (model.type === 'transfer') {
|
|
if (action) {
|
|
handleMaterialDropToConveyor(model);
|
|
}
|
|
} else if (model.type === 'machine') {
|
|
if (action) {
|
|
handleMaterialDropToMachine(model);
|
|
}
|
|
} else if (model.type === 'roboticArm') {
|
|
if (action) {
|
|
handleMaterialDropToArmBot(model);
|
|
}
|
|
} else if (model.type === 'storageUnit') {
|
|
if (action) {
|
|
handleMaterialDropToStorageUnit(model);
|
|
}
|
|
} else if (model.type === 'vehicle') {
|
|
if (action) {
|
|
handleMaterialDropToVehicle(model);
|
|
}
|
|
}
|
|
} else {
|
|
const droppedMaterial = human.currentLoad;
|
|
handleMaterialDropByDefault(droppedMaterial);
|
|
}
|
|
} else {
|
|
const droppedMaterial = human.currentLoad;
|
|
handleMaterialDropByDefault(droppedMaterial);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(startUnloadingProcess);
|
|
}
|
|
}
|
|
|
|
function handleMaterialDropToStorageUnit(model: StorageEventSchema) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
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,
|
|
(action as HumanAction)
|
|
);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(checkAnimation);
|
|
}
|
|
};
|
|
checkAnimation();
|
|
}
|
|
|
|
function loopMaterialDropToStorage(
|
|
humanId: string,
|
|
humanCurrentLoad: number,
|
|
storageUnitId: string,
|
|
storageMaxCapacity: number,
|
|
action: HumanAction
|
|
) {
|
|
const storageUnit = getStorageUnitById(storageUnitId);
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
|
|
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?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
|
loopMaterialDropToStorage(
|
|
humanId,
|
|
humanCurrentLoad,
|
|
storageUnitId,
|
|
storageMaxCapacity,
|
|
action
|
|
);
|
|
} else {
|
|
requestAnimationFrame(waitForNextDrop);
|
|
}
|
|
};
|
|
waitForNextDrop();
|
|
}
|
|
}
|
|
|
|
function handleMaterialDropToConveyor(model: ConveyorEventSchema) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
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,
|
|
(action as HumanAction)
|
|
);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(checkAnimation);
|
|
}
|
|
};
|
|
checkAnimation();
|
|
}
|
|
|
|
function loopMaterialDropToConveyor(
|
|
humanId: string,
|
|
humanCurrentLoad: number,
|
|
conveyorId: string,
|
|
action: HumanAction
|
|
) {
|
|
const conveyor = getConveyorById(conveyorId);
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
|
|
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) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
if (humanAsset?.animationState?.current !== 'drop') {
|
|
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
|
}
|
|
|
|
const checkAnimation = () => {
|
|
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
|
const armBot = getArmBotById(model.modelUuid);
|
|
if (armBot && armBot.state === 'idle' && !armBot.isActive) {
|
|
loopMaterialDropToArmBot(
|
|
human.modelUuid,
|
|
human.currentLoad,
|
|
model.modelUuid,
|
|
(action as HumanAction)
|
|
);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(checkAnimation);
|
|
}
|
|
};
|
|
checkAnimation();
|
|
}
|
|
|
|
function loopMaterialDropToArmBot(
|
|
humanId: string,
|
|
humanCurrentLoad: number,
|
|
armBotId: string,
|
|
action: HumanAction
|
|
) {
|
|
const armBot = getArmBotById(armBotId);
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
|
|
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) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
if (humanAsset?.animationState?.current !== 'drop') {
|
|
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
|
}
|
|
|
|
const checkAnimation = () => {
|
|
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
|
const vehicle = getVehicleById(model.modelUuid);
|
|
if (vehicle && vehicle.state === 'idle' && !vehicle.isActive) {
|
|
loopMaterialDropToVehicle(
|
|
human.modelUuid,
|
|
human.currentLoad,
|
|
model.modelUuid,
|
|
(action as HumanAction)
|
|
);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(checkAnimation);
|
|
}
|
|
};
|
|
checkAnimation();
|
|
}
|
|
|
|
function loopMaterialDropToVehicle(
|
|
humanId: string,
|
|
humanCurrentLoad: number,
|
|
vehicleId: string,
|
|
action: HumanAction
|
|
) {
|
|
const vehicle = getVehicleById(vehicleId);
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
|
|
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 currentVehicle = getVehicleById(vehicleId);
|
|
if (currentVehicle && currentVehicle.state === 'idle' && !currentVehicle.isActive) {
|
|
if (humanAsset?.animationState?.isCompleted) {
|
|
loopMaterialDropToVehicle(
|
|
humanId,
|
|
humanCurrentLoad,
|
|
vehicleId,
|
|
action
|
|
);
|
|
} else {
|
|
requestAnimationFrame(waitForNextTransfer);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(waitForNextTransfer);
|
|
}
|
|
};
|
|
waitForNextTransfer();
|
|
}
|
|
}
|
|
|
|
function handleMaterialDropToMachine(model: MachineEventSchema) {
|
|
const action = getActionByUuid(selectedProduct.productUuid, human?.currentAction?.actionUuid || '');
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
if (humanAsset?.animationState?.current !== 'drop') {
|
|
setCurrentAnimation(human.modelUuid, 'drop', true, false, false);
|
|
}
|
|
|
|
const checkAnimation = () => {
|
|
if (humanAsset?.animationState?.current === 'drop' && humanAsset?.animationState?.isCompleted) {
|
|
const machine = getMachineById(model.modelUuid);
|
|
if (machine && machine.state === 'idle' && !machine.isActive) {
|
|
loopMaterialDropToMachine(
|
|
human.modelUuid,
|
|
human.currentLoad,
|
|
model.modelUuid,
|
|
(action as HumanAction)
|
|
);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(checkAnimation);
|
|
}
|
|
};
|
|
checkAnimation();
|
|
}
|
|
|
|
function loopMaterialDropToMachine(
|
|
humanId: string,
|
|
humanCurrentLoad: number,
|
|
machineId: string,
|
|
action: HumanAction
|
|
) {
|
|
const machine = getMachineById(machineId);
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
|
|
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 currentMachine = getMachineById(machineId);
|
|
if (currentMachine && currentMachine.state === 'idle' && !currentMachine.isActive) {
|
|
if (humanAsset?.animationState?.isCompleted) {
|
|
loopMaterialDropToMachine(
|
|
humanId,
|
|
humanCurrentLoad,
|
|
machineId,
|
|
action
|
|
);
|
|
} else {
|
|
requestAnimationFrame(waitForNextTransfer);
|
|
}
|
|
} else {
|
|
requestAnimationFrame(waitForNextTransfer);
|
|
}
|
|
};
|
|
waitForNextTransfer();
|
|
}
|
|
}
|
|
|
|
function handleMaterialDropByDefault(droppedMaterial: number) {
|
|
const humanAsset = getAssetById(human.modelUuid);
|
|
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 (
|
|
<>
|
|
|
|
<HumanAnimator
|
|
path={path}
|
|
handleCallBack={handleCallBack}
|
|
currentPhase={currentPhase}
|
|
human={human}
|
|
reset={reset}
|
|
startUnloadingProcess={startUnloadingProcess}
|
|
/>
|
|
|
|
<MaterialAnimator currentPhase={currentPhase} human={human} />
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default HumanInstance |