first commit
This commit is contained in:
380
app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx
Normal file
380
app/src/modules/simulation/spatialUI/vehicle/vehicleUI.tsx
Normal file
@@ -0,0 +1,380 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import * as Types from "../../../../types/world/worldTypes";
|
||||
import { useGLTF } from "@react-three/drei";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useSelectedEventSphere, useIsDragging, useIsRotating, } from "../../../../store/simulation/useSimulationStore";
|
||||
import { useProductStore } from "../../../../store/simulation/useProductStore";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { DoubleSide, Group, Plane, Vector3 } from "three";
|
||||
|
||||
import startPoint from "../../../../assets/gltf-glb/ui/arrow_green.glb";
|
||||
import startEnd from "../../../../assets/gltf-glb/ui/arrow_red.glb";
|
||||
import { useSceneContext } from "../../../scene/sceneContext";
|
||||
import { useProductContext } from "../../products/productContext";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
const VehicleUI = () => {
|
||||
const { scene: startScene } = useGLTF(startPoint) as any;
|
||||
const { scene: endScene } = useGLTF(startEnd) as any;
|
||||
const startMarker = useRef<Group>(null);
|
||||
const endMarker = useRef<Group>(null);
|
||||
const prevMousePos = useRef({ x: 0, y: 0 });
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { vehicleStore } = useSceneContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { vehicles, getVehicleById } = vehicleStore();
|
||||
const { updateEvent } = useProductStore();
|
||||
const [startPosition, setStartPosition] = useState<[number, number, number]>([0, 1, 0,]);
|
||||
|
||||
const [endPosition, setEndPosition] = useState<[number, number, number]>([0, 1, 0,]);
|
||||
const [startRotation, setStartRotation] = useState<[number, number, number]>([0, 0, 0,]);
|
||||
|
||||
const [endRotation, setEndRotation] = useState<[number, number, number]>([0, 0, 0,]);
|
||||
const [steeringRotation, setSteeringRotation] = useState<[number, number, number]>([0, 0, 0]);
|
||||
|
||||
const { isDragging, setIsDragging } = useIsDragging();
|
||||
const { isRotating, setIsRotating } = useIsRotating();
|
||||
const { raycaster } = useThree();
|
||||
const [point, setPoint] = useState<[number, number, number]>([0, 0, 0]);
|
||||
const plane = useRef(new Plane(new Vector3(0, 1, 0), 0));
|
||||
const [tubeRotation, setTubeRotation] = useState<boolean>(false);
|
||||
const tubeRef = useRef<Group>(null);
|
||||
const outerGroup = useRef<Group>(null);
|
||||
const state: Types.ThreeState = useThree();
|
||||
const controls: any = state.controls;
|
||||
const [selectedVehicleData, setSelectedVechicleData] = useState<{ position: [number, number, number]; rotation: [number, number, number]; }>({ position: [0, 0, 0], rotation: [0, 0, 0] });
|
||||
const CIRCLE_RADIUS = 0.8;
|
||||
const { projectId } = useParams();
|
||||
|
||||
const updateBackend = (
|
||||
productName: string,
|
||||
productUuid: string,
|
||||
projectId: string,
|
||||
eventData: EventsSchema
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productUuid: productUuid,
|
||||
projectId: projectId,
|
||||
eventDatas: eventData,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedEventSphere) return;
|
||||
const selectedVehicle = getVehicleById(
|
||||
selectedEventSphere.userData.modelUuid
|
||||
);
|
||||
|
||||
if (selectedVehicle) {
|
||||
setSelectedVechicleData({
|
||||
position: selectedVehicle.position,
|
||||
rotation: selectedVehicle.rotation,
|
||||
});
|
||||
setPoint(selectedVehicle.point.position);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (selectedVehicle?.point?.action) {
|
||||
const { pickUpPoint, unLoadPoint, steeringAngle } = selectedVehicle.point.action;
|
||||
|
||||
if (pickUpPoint && outerGroup.current) {
|
||||
const worldPos = new Vector3(
|
||||
pickUpPoint.position.x,
|
||||
pickUpPoint.position.y,
|
||||
pickUpPoint.position.z
|
||||
);
|
||||
const localPosition = outerGroup.current.worldToLocal(worldPos.clone());
|
||||
|
||||
setStartPosition([
|
||||
localPosition.x,
|
||||
selectedVehicle.point.position[1],
|
||||
localPosition.z,
|
||||
]);
|
||||
setStartRotation([
|
||||
pickUpPoint.rotation.x,
|
||||
pickUpPoint.rotation.y,
|
||||
pickUpPoint.rotation.z,
|
||||
]);
|
||||
} else {
|
||||
setStartPosition([0, selectedVehicle.point.position[1] + 0.1, 1.5]);
|
||||
setStartRotation([0, 0, 0]);
|
||||
}
|
||||
// end point
|
||||
if (unLoadPoint && outerGroup.current) {
|
||||
const worldPos = new Vector3(
|
||||
unLoadPoint.position.x,
|
||||
unLoadPoint.position.y,
|
||||
unLoadPoint.position.z
|
||||
);
|
||||
const localPosition = outerGroup.current.worldToLocal(worldPos);
|
||||
|
||||
setEndPosition([
|
||||
localPosition.x,
|
||||
selectedVehicle.point.position[1],
|
||||
localPosition.z,
|
||||
]);
|
||||
setEndRotation([
|
||||
unLoadPoint.rotation.x,
|
||||
unLoadPoint.rotation.y,
|
||||
unLoadPoint.rotation.z,
|
||||
]);
|
||||
} else {
|
||||
setEndPosition([0, selectedVehicle.point.position[1] + 0.1, -1.5]);
|
||||
setEndRotation([0, 0, 0]);
|
||||
}
|
||||
setSteeringRotation([0, steeringAngle, 0]);
|
||||
}
|
||||
}, 10);
|
||||
}, [selectedEventSphere, outerGroup.current, vehicles]);
|
||||
|
||||
const handlePointerDown = (
|
||||
e: any,
|
||||
state: "start" | "end",
|
||||
rotation: "start" | "end"
|
||||
) => {
|
||||
if (e.object.name === "handle") {
|
||||
const normalizedX = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
const normalizedY = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
prevMousePos.current = { x: normalizedX, y: normalizedY };
|
||||
setIsRotating(rotation);
|
||||
if (controls) controls.enabled = false;
|
||||
setIsDragging(null);
|
||||
} else {
|
||||
setIsDragging(state);
|
||||
setIsRotating(null);
|
||||
if (controls) controls.enabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerUp = () => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
|
||||
if (selectedEventSphere?.userData.modelUuid) {
|
||||
const updatedVehicle = getVehicleById(selectedEventSphere.userData.modelUuid);
|
||||
|
||||
let globalStartPosition = null;
|
||||
let globalEndPosition = null;
|
||||
|
||||
if (outerGroup.current && startMarker.current && endMarker.current) {
|
||||
const worldPosStart = new Vector3(...startPosition);
|
||||
globalStartPosition = outerGroup.current.localToWorld(
|
||||
worldPosStart.clone()
|
||||
);
|
||||
const worldPosEnd = new Vector3(...endPosition);
|
||||
globalEndPosition = outerGroup.current.localToWorld(
|
||||
worldPosEnd.clone()
|
||||
);
|
||||
}
|
||||
if (updatedVehicle && globalEndPosition && globalStartPosition) {
|
||||
const event = updateEvent(
|
||||
selectedProduct.productUuid,
|
||||
selectedEventSphere.userData.modelUuid,
|
||||
{
|
||||
point: {
|
||||
...updatedVehicle.point,
|
||||
action: {
|
||||
...updatedVehicle.point?.action,
|
||||
pickUpPoint: {
|
||||
position: {
|
||||
x: globalStartPosition.x,
|
||||
y: 0,
|
||||
z: globalStartPosition.z,
|
||||
},
|
||||
rotation: { x: 0, y: startRotation[1], z: 0 },
|
||||
},
|
||||
unLoadPoint: {
|
||||
position: {
|
||||
x: globalEndPosition.x,
|
||||
y: 0,
|
||||
z: globalEndPosition.z,
|
||||
},
|
||||
rotation: { x: 0, y: endRotation[1], z: 0 },
|
||||
},
|
||||
steeringAngle: steeringRotation[1],
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useFrame(() => {
|
||||
if (!isDragging || !plane.current || !raycaster || !outerGroup.current) return;
|
||||
const intersectPoint = new Vector3();
|
||||
const intersects = raycaster.ray.intersectPlane(
|
||||
plane.current,
|
||||
intersectPoint
|
||||
);
|
||||
if (!intersects) return;
|
||||
const localPoint = outerGroup?.current.worldToLocal(intersectPoint.clone());
|
||||
if (isDragging === "start") {
|
||||
setStartPosition([localPoint.x, point[1], localPoint.z]);
|
||||
} else if (isDragging === "end") {
|
||||
setEndPosition([localPoint.x, point[1], localPoint.z]);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const handleGlobalPointerUp = () => {
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
setTubeRotation(false);
|
||||
if (controls) controls.enabled = true;
|
||||
handlePointerUp();
|
||||
};
|
||||
|
||||
if (isDragging || isRotating || tubeRotation) {
|
||||
window.addEventListener("pointerup", handleGlobalPointerUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("pointerup", handleGlobalPointerUp);
|
||||
};
|
||||
}, [isDragging, isRotating, startPosition, startRotation, endPosition, endRotation, tubeRotation, steeringRotation, outerGroup.current, tubeRef.current,]);
|
||||
|
||||
const prevSteeringY = useRef(0);
|
||||
|
||||
useFrame((state) => {
|
||||
if (tubeRotation) {
|
||||
const currentPointerX = state.pointer.x;
|
||||
const deltaX = currentPointerX - prevMousePos.current.x;
|
||||
prevMousePos.current.x = currentPointerX;
|
||||
|
||||
const marker = tubeRef.current;
|
||||
if (marker) {
|
||||
const rotationSpeed = 2;
|
||||
marker.rotation.y += deltaX * rotationSpeed;
|
||||
setSteeringRotation([
|
||||
marker.rotation.x,
|
||||
marker.rotation.y,
|
||||
marker.rotation.z,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
prevSteeringY.current = 0;
|
||||
}
|
||||
});
|
||||
|
||||
useFrame((state) => {
|
||||
if (!isRotating) return;
|
||||
const currentPointerX = state.pointer.x;
|
||||
const deltaX = currentPointerX - prevMousePos.current.x;
|
||||
prevMousePos.current.x = currentPointerX;
|
||||
const marker =
|
||||
isRotating === "start" ? startMarker.current : endMarker.current;
|
||||
if (marker) {
|
||||
const rotationSpeed = 10;
|
||||
marker.rotation.y += deltaX * rotationSpeed;
|
||||
if (isRotating === "start") {
|
||||
setStartRotation([
|
||||
marker.rotation.x,
|
||||
marker.rotation.y,
|
||||
marker.rotation.z,
|
||||
]);
|
||||
} else {
|
||||
setEndRotation([
|
||||
marker.rotation.x,
|
||||
marker.rotation.y,
|
||||
marker.rotation.z,
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return selectedVehicleData ? (
|
||||
<group
|
||||
position={selectedVehicleData.position}
|
||||
rotation={selectedVehicleData.rotation}
|
||||
ref={outerGroup}
|
||||
>
|
||||
<group
|
||||
position={[0, 0, 0]}
|
||||
ref={tubeRef}
|
||||
rotation={steeringRotation}
|
||||
onPointerDown={(e) => {
|
||||
e.stopPropagation();
|
||||
setTubeRotation(true);
|
||||
prevMousePos.current.x = e.pointer.x;
|
||||
controls.enabled = false;
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setTubeRotation(false);
|
||||
}}
|
||||
onPointerUp={() => {
|
||||
controls.enabled = true;
|
||||
setTubeRotation(false);
|
||||
}}
|
||||
>
|
||||
(
|
||||
<mesh
|
||||
position={[0, point[1], 0]}
|
||||
rotation={[-Math.PI / 2, 0, 0]}
|
||||
name="steering"
|
||||
>
|
||||
<ringGeometry args={[CIRCLE_RADIUS, CIRCLE_RADIUS + 0.2, 36]} />
|
||||
<meshBasicMaterial color="yellow" side={DoubleSide} />
|
||||
</mesh>
|
||||
<mesh
|
||||
position={[0, point[1], CIRCLE_RADIUS + 0.24]}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<coneGeometry args={[0.1, 0.3, 12]} />
|
||||
<meshBasicMaterial color="yellow" side={DoubleSide} />
|
||||
</mesh>
|
||||
)
|
||||
</group>
|
||||
|
||||
{/* Start Marker */}
|
||||
<primitive
|
||||
name="startMarker"
|
||||
object={startScene}
|
||||
ref={startMarker}
|
||||
position={startPosition}
|
||||
rotation={startRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "start", "start");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* End Marker */}
|
||||
<primitive
|
||||
name="endMarker"
|
||||
object={endScene}
|
||||
ref={endMarker}
|
||||
position={endPosition}
|
||||
rotation={endRotation}
|
||||
onPointerDown={(e: any) => {
|
||||
e.stopPropagation();
|
||||
handlePointerDown(e, "end", "end");
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
controls.enabled = true;
|
||||
setIsDragging(null);
|
||||
setIsRotating(null);
|
||||
}}
|
||||
/>
|
||||
</group>
|
||||
) : null;
|
||||
};
|
||||
export default VehicleUI;
|
||||
Reference in New Issue
Block a user