added crane mechanics ui and dblclick zoom effect chnage
This commit is contained in:
@@ -24,7 +24,7 @@ export function useModelEventHandlers({
|
||||
boundingBox: THREE.Box3 | null,
|
||||
groupRef: React.RefObject<THREE.Group>,
|
||||
}) {
|
||||
const { controls, gl } = useThree();
|
||||
const { controls, gl, camera } = useThree();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toggleView } = useToggleView();
|
||||
@@ -68,25 +68,47 @@ export function useModelEventHandlers({
|
||||
|
||||
const handleDblClick = (asset: Asset) => {
|
||||
if (asset && activeTool === "cursor" && boundingBox && groupRef.current && activeModule === 'builder') {
|
||||
const size = boundingBox.getSize(new THREE.Vector3());
|
||||
const center = boundingBox.getCenter(new THREE.Vector3());
|
||||
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
groupRef.current.localToWorld(front);
|
||||
front.sub(groupRef.current.position).normalize();
|
||||
const frontView = false;
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
if (frontView) {
|
||||
const size = boundingBox.getSize(new THREE.Vector3());
|
||||
const center = boundingBox.getCenter(new THREE.Vector3());
|
||||
|
||||
(controls as CameraControls).setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
(controls as CameraControls).setTarget(center.x, center.y, center.z, true);
|
||||
(controls as CameraControls).fitToBox(groupRef.current, true, {
|
||||
cover: true,
|
||||
paddingTop: 5,
|
||||
paddingLeft: 5,
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
const front = new THREE.Vector3(0, 0, 1);
|
||||
groupRef.current.localToWorld(front);
|
||||
front.sub(groupRef.current.position).normalize();
|
||||
|
||||
const distance = Math.max(size.x, size.y, size.z) * 2;
|
||||
const newPosition = center.clone().addScaledVector(front, distance);
|
||||
|
||||
(controls as CameraControls).setPosition(newPosition.x, newPosition.y, newPosition.z, true);
|
||||
(controls as CameraControls).setTarget(center.x, center.y, center.z, true);
|
||||
(controls as CameraControls).fitToBox(groupRef.current, true, {
|
||||
cover: true,
|
||||
paddingTop: 5,
|
||||
paddingLeft: 5,
|
||||
paddingBottom: 5,
|
||||
paddingRight: 5,
|
||||
});
|
||||
} else {
|
||||
|
||||
const collisionPos = new THREE.Vector3();
|
||||
groupRef.current.getWorldPosition(collisionPos);
|
||||
|
||||
const currentPos = new THREE.Vector3().copy(camera.position);
|
||||
|
||||
const target = new THREE.Vector3();
|
||||
if (!controls) return;
|
||||
(controls as CameraControls).getTarget(target);
|
||||
const direction = new THREE.Vector3().subVectors(target, currentPos).normalize();
|
||||
|
||||
const offsetDistance = 5;
|
||||
const newCameraPos = new THREE.Vector3().copy(collisionPos).sub(direction.multiplyScalar(offsetDistance));
|
||||
|
||||
camera.position.copy(newCameraPos);
|
||||
(controls as CameraControls).setLookAt(newCameraPos.x, newCameraPos.y, newCameraPos.z, collisionPos.x, 0, collisionPos.z, true);
|
||||
}
|
||||
|
||||
setSelectedFloorItem(groupRef.current);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import * as THREE from 'three';
|
||||
import { useFrame, useThree } from '@react-three/fiber';
|
||||
import { Sphere, Box } from '@react-three/drei';
|
||||
import { useAnimationPlaySpeed, usePauseButtonStore, usePlayButtonStore } from '../../../../../store/usePlayButtonStore';
|
||||
import { useSceneContext } from '../../../../scene/sceneContext';
|
||||
|
||||
@@ -26,7 +25,6 @@ function PillarJibAnimator({
|
||||
const { speed } = useAnimationPlaySpeed();
|
||||
|
||||
const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>();
|
||||
const [isInside, setIsInside] = useState<[boolean, boolean]>([false, false]);
|
||||
const [currentTargetIndex, setCurrentTargetIndex] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -101,7 +99,6 @@ function PillarJibAnimator({
|
||||
});
|
||||
|
||||
setClampedPoints(newClampedPoints);
|
||||
setIsInside(newIsInside);
|
||||
}, [crane.modelUuid]);
|
||||
|
||||
useFrame(() => {
|
||||
@@ -227,29 +224,8 @@ function PillarJibAnimator({
|
||||
}
|
||||
});
|
||||
|
||||
if (!clampedPoints) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
{points.map((point, i) => (
|
||||
<Box
|
||||
key={`original-${i}`}
|
||||
position={point}
|
||||
args={[0.15, 0.15, 0.15]}
|
||||
>
|
||||
<meshStandardMaterial color="yellow" />
|
||||
</Box>
|
||||
))}
|
||||
|
||||
{clampedPoints.map((point, i) => (
|
||||
<Sphere
|
||||
key={`clamped-${i}`}
|
||||
position={point}
|
||||
args={[0.1, 16, 16]}
|
||||
>
|
||||
<meshStandardMaterial color={isInside[i] ? 'lightgreen' : 'orange'} />
|
||||
</Sphere>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
import { useMemo } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import * as THREE from "three";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { Box, Sphere } from "@react-three/drei";
|
||||
|
||||
function PillarJibHelper({ crane }: { crane: CraneStatus }) {
|
||||
function PillarJibHelper({
|
||||
crane,
|
||||
points
|
||||
}: {
|
||||
crane: CraneStatus,
|
||||
points: [THREE.Vector3, THREE.Vector3];
|
||||
}) {
|
||||
const { scene } = useThree();
|
||||
const [clampedPoints, setClampedPoints] = useState<[THREE.Vector3, THREE.Vector3]>();
|
||||
const [isInside, setIsInside] = useState<[boolean, boolean]>([false, false]);
|
||||
|
||||
const { geometry, position } = useMemo(() => {
|
||||
const model = scene.getObjectByProperty('uuid', crane.modelUuid);
|
||||
@@ -51,26 +60,81 @@ function PillarJibHelper({ crane }: { crane: CraneStatus }) {
|
||||
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
||||
const position: [number, number, number] = [baseWorld.x, cylinderYPosition, baseWorld.z];
|
||||
|
||||
const yMin = hookWorld.y + hookMaxOffset;
|
||||
const yMax = hookWorld.y + hookMinOffset;
|
||||
|
||||
function clampToCylinder(pos: THREE.Vector3) {
|
||||
const xzDist = new THREE.Vector2(pos.x - baseWorld.x, pos.z - baseWorld.z);
|
||||
const distance = xzDist.length();
|
||||
|
||||
let clampedDistance = THREE.MathUtils.clamp(distance, innerRadius, outerRadius);
|
||||
if (distance > 0) {
|
||||
clampedDistance = Math.max(innerRadius, Math.min(distance, outerRadius));
|
||||
}
|
||||
|
||||
const clampedXZ = xzDist.normalize().multiplyScalar(clampedDistance);
|
||||
const y = THREE.MathUtils.clamp(pos.y, yMin, yMax);
|
||||
|
||||
return new THREE.Vector3(baseWorld.x + clampedXZ.x, y, baseWorld.z + clampedXZ.y);
|
||||
}
|
||||
|
||||
const newClampedPoints: [THREE.Vector3, THREE.Vector3] = [new THREE.Vector3(), new THREE.Vector3()];
|
||||
const newIsInside: [boolean, boolean] = [false, false];
|
||||
|
||||
points.forEach((point, i) => {
|
||||
const xzDist = new THREE.Vector2(point.x - baseWorld.x, point.z - baseWorld.z).length();
|
||||
const insideXZ = xzDist >= innerRadius && xzDist <= outerRadius;
|
||||
const insideY = point.y >= yMin && point.y <= yMax;
|
||||
newIsInside[i] = insideXZ && insideY;
|
||||
|
||||
newClampedPoints[i] = newIsInside[i] ? point.clone() : clampToCylinder(point);
|
||||
});
|
||||
|
||||
setClampedPoints(newClampedPoints);
|
||||
setIsInside(newIsInside);
|
||||
|
||||
return { geometry, position };
|
||||
}, [scene, crane.modelUuid]);
|
||||
|
||||
if (!geometry || !position) return null;
|
||||
|
||||
return (
|
||||
<mesh
|
||||
geometry={geometry}
|
||||
position={position}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={0x888888}
|
||||
metalness={0.5}
|
||||
roughness={0.4}
|
||||
side={THREE.DoubleSide}
|
||||
transparent={true}
|
||||
opacity={0.3}
|
||||
/>
|
||||
</mesh>
|
||||
<>
|
||||
<mesh
|
||||
geometry={geometry}
|
||||
position={position}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
>
|
||||
<meshStandardMaterial
|
||||
color={0x888888}
|
||||
metalness={0.5}
|
||||
roughness={0.4}
|
||||
side={THREE.DoubleSide}
|
||||
transparent={true}
|
||||
opacity={0.3}
|
||||
/>
|
||||
</mesh>
|
||||
|
||||
{points.map((point, i) => (
|
||||
<Box
|
||||
key={`original-${i}`}
|
||||
position={point}
|
||||
args={[0.15, 0.15, 0.15]}
|
||||
>
|
||||
<meshStandardMaterial color="yellow" />
|
||||
</Box>
|
||||
))}
|
||||
|
||||
{clampedPoints && clampedPoints.map((point, i) => (
|
||||
<Sphere
|
||||
key={`clamped-${i}`}
|
||||
position={point}
|
||||
args={[0.1, 16, 16]}
|
||||
>
|
||||
<meshStandardMaterial color={isInside[i] ? 'lightgreen' : 'orange'} />
|
||||
</Sphere>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,13 @@ function PillarJibInstance({ crane }: { crane: CraneStatus }) {
|
||||
onAnimationComplete={handleAnimationComplete}
|
||||
/>
|
||||
|
||||
<PillarJibHelper crane={crane} />
|
||||
{/* <PillarJibHelper
|
||||
crane={crane}
|
||||
points={[
|
||||
new THREE.Vector3(...position1),
|
||||
new THREE.Vector3(...position2)]
|
||||
}
|
||||
/> */}
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user