Skip to content

Commit e6fd48e

Browse files
authored
Merge pull request #217 from krishauser/sr_viz_shiyang_clean
smoother animation and camera follow
2 parents ed81a94 + d512622 commit e6fd48e

7 files changed

Lines changed: 142 additions & 121 deletions

File tree

GEMstack/onboard/visualization/sr_viz/threeD/src/components/OtherVehicle.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
"use client";
22

3-
import { useRef, useMemo, useEffect, useState } from "react";
3+
import React, { useRef, useEffect, useMemo, useState } from "react";
44
import { useFrame } from "@react-three/fiber";
55
import * as THREE from "three";
66
import { FrameData } from "@/types/FrameData";
77
import { currentOtherVehicle } from "@/config/otherVehicleConfig";
88
import URDFLoader from "urdf-loader";
9+
import { getInterpolatedFrame } from "@/utils/getInterpolatedFrame";
910

1011
interface OtherVehicleProps {
1112
id: string;
@@ -25,25 +26,22 @@ export default function OtherVehicle({ id, timeline, time }: OtherVehicleProps)
2526

2627
useEffect(() => {
2728
const loader = new URDFLoader();
28-
2929
loader.load(
3030
modelPath,
3131
(robot) => {
3232
robot.scale.set(scale[0], scale[1], scale[2]);
3333
robot.rotation.set(rotation[0], rotation[1], rotation[2]);
3434
robot.position.set(offset[0], offset[1], offset[2]);
35-
3635
robot.traverse((child) => {
37-
if (child instanceof THREE.Mesh && child.material instanceof THREE.MeshStandardMaterial) {
36+
if (
37+
child instanceof THREE.Mesh &&
38+
child.material instanceof THREE.MeshStandardMaterial
39+
) {
3840
child.material = child.material.clone();
3941
child.material.color.set(bodyColor);
4042
}
4143
});
42-
43-
if (vehicleGroup.current) {
44-
vehicleGroup.current.add(robot);
45-
}
46-
44+
vehicleGroup.current.add(robot);
4745
setIsLoaded(true);
4846
},
4947
undefined,
@@ -55,13 +53,10 @@ export default function OtherVehicle({ id, timeline, time }: OtherVehicleProps)
5553

5654
useFrame(() => {
5755
if (!ref.current || timeline.length === 0) return;
58-
59-
const frame = timeline.find((f) => f.time >= time) ?? timeline.at(-1);
56+
const frame = getInterpolatedFrame(timeline, time);
6057
if (!frame) return;
61-
6258
targetPosition.set(frame.x, 0, frame.y);
6359
ref.current.position.lerp(targetPosition, 0.2);
64-
6560
targetQuaternion.setFromEuler(new THREE.Euler(0, -frame.yaw, 0));
6661
ref.current.quaternion.slerp(targetQuaternion, 0.2);
6762
});

GEMstack/onboard/visualization/sr_viz/threeD/src/components/Pedestrian.tsx

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
"use client";
22

3-
import { useRef, useMemo, useEffect, useState } from "react";
3+
import React, { useRef, useMemo, useEffect, useState } from "react";
44
import { useFrame } from "@react-three/fiber";
5-
import { Mesh, Object3D, MeshStandardMaterial } from "three";
5+
import {
6+
Mesh,
7+
Object3D,
8+
MeshStandardMaterial,
9+
Vector3,
10+
Quaternion,
11+
Euler,
12+
} from "three";
613
import { useGLTF } from "@react-three/drei";
714
import { FrameData } from "@/types/FrameData";
815
import { currentPedestrian } from "@/config/pedestrianConfig";
16+
import { getInterpolatedFrame } from "@/utils/getInterpolatedFrame";
917

1018
interface PedestrianProps {
1119
id: string;
@@ -15,12 +23,15 @@ interface PedestrianProps {
1523

1624
export default function Pedestrian({ id, timeline, time }: PedestrianProps) {
1725
const [mounted, setMounted] = useState(false);
18-
1926
const ref = useRef<Mesh>(null);
27+
2028
const { modelPath, scale, rotation, offset, bodyColor } = currentPedestrian;
2129
const { scene } = useGLTF(modelPath);
2230
const clonedScene = useMemo(() => scene.clone(true), [scene]);
2331

32+
const targetPosition = useMemo(() => new Vector3(), []);
33+
const targetQuaternion = useMemo(() => new Quaternion(), []);
34+
2435
useEffect(() => {
2536
setMounted(true);
2637
}, []);
@@ -39,17 +50,14 @@ export default function Pedestrian({ id, timeline, time }: PedestrianProps) {
3950

4051
useFrame(() => {
4152
if (!ref.current || timeline.length === 0) return;
42-
43-
const frame = timeline.find((f) => f.time >= time) ?? timeline.at(-1);
53+
const frame = getInterpolatedFrame(timeline, time);
4454
if (!frame) return;
45-
46-
ref.current.position.set(frame.x, 0, frame.y);
47-
ref.current.rotation.y = -frame.yaw;
55+
targetPosition.set(frame.x, 0, frame.y);
56+
ref.current.position.lerp(targetPosition, 0.2);
57+
targetQuaternion.setFromEuler(new Euler(0, -frame.yaw, 0));
58+
ref.current.quaternion.slerp(targetQuaternion, 0.2);
4859
});
4960

50-
const hasSpawned = timeline.length > 0 && timeline[0].time <= time;
51-
if (!mounted || !hasSpawned) return null;
52-
5361
return (
5462
<primitive
5563
ref={ref as React.RefObject<Object3D>}
@@ -59,4 +67,4 @@ export default function Pedestrian({ id, timeline, time }: PedestrianProps) {
5967
position={offset}
6068
/>
6169
);
62-
}
70+
}

GEMstack/onboard/visualization/sr_viz/threeD/src/components/TrafficCone.tsx

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
"use client";
22

3-
import { useRef, useMemo, useEffect, useState } from "react";
3+
import React, { useRef, useMemo, useEffect, useState } from "react";
44
import { useFrame } from "@react-three/fiber";
5-
import { Mesh, Object3D, MeshStandardMaterial } from "three";
5+
import {
6+
Mesh,
7+
Object3D,
8+
MeshStandardMaterial,
9+
Vector3,
10+
Quaternion,
11+
Euler,
12+
} from "three";
613
import { useGLTF } from "@react-three/drei";
714
import { FrameData } from "@/types/FrameData";
815
import { currentTrafficCone } from "@/config/trafficConeConfig";
16+
import { getInterpolatedFrame } from "@/utils/getInterpolatedFrame";
917

1018
interface TrafficConeProps {
1119
id: string;
@@ -15,12 +23,14 @@ interface TrafficConeProps {
1523

1624
export default function TrafficCone({ id, timeline, time }: TrafficConeProps) {
1725
const [mounted, setMounted] = useState(false);
18-
1926
const ref = useRef<Mesh>(null);
2027
const { modelPath, scale, rotation, offset, bodyColor } = currentTrafficCone;
2128
const { scene } = useGLTF(modelPath);
2229
const clonedScene = useMemo(() => scene.clone(true), [scene]);
2330

31+
const targetPosition = useMemo(() => new Vector3(), []);
32+
const targetQuaternion = useMemo(() => new Quaternion(), []);
33+
2434
useEffect(() => {
2535
setMounted(true);
2636
}, []);
@@ -38,16 +48,15 @@ export default function TrafficCone({ id, timeline, time }: TrafficConeProps) {
3848
}, [clonedScene, bodyColor]);
3949

4050
useFrame(() => {
41-
const frame = timeline.find((f) => f.time >= time);
42-
if (frame && ref.current) {
43-
ref.current.position.set(frame.x, frame.z, frame.y);
44-
ref.current.rotation.y = -frame.yaw;
45-
}
51+
if (!ref.current || timeline.length === 0) return;
52+
const frame = getInterpolatedFrame(timeline, time);
53+
if (!frame) return;
54+
targetPosition.set(frame.x, frame.z, frame.y);
55+
ref.current.position.lerp(targetPosition, 0.2);
56+
targetQuaternion.setFromEuler(new Euler(0, -frame.yaw, 0));
57+
ref.current.quaternion.slerp(targetQuaternion, 0.2);
4658
});
4759

48-
const hasSpawned = timeline.length > 0 && timeline[0].time <= time;
49-
if (!mounted || !hasSpawned) return null;
50-
5160
return (
5261
<primitive
5362
ref={ref as React.RefObject<Object3D>}

GEMstack/onboard/visualization/sr_viz/threeD/src/components/TrafficLight.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,40 @@
11
"use client";
22

3-
import { useRef, useMemo, useEffect, useState } from "react";
3+
import React, { useRef, useMemo, useEffect, useState } from "react";
44
import { useFrame } from "@react-three/fiber";
5-
import { Mesh, Object3D, MeshStandardMaterial } from "three";
5+
import {
6+
Mesh,
7+
Object3D,
8+
MeshStandardMaterial,
9+
Vector3,
10+
Quaternion,
11+
Euler,
12+
} from "three";
613
import { useGLTF } from "@react-three/drei";
714
import { FrameData } from "@/types/FrameData";
815
import { currentTrafficLight } from "@/config/trafficLightConfig";
16+
import { getInterpolatedFrame } from "@/utils/getInterpolatedFrame";
917

1018
interface TrafficLightProps {
1119
id: string;
1220
timeline: FrameData[];
1321
time: number;
1422
}
1523

16-
export default function TrafficLight({ id, timeline, time }: TrafficLightProps) {
24+
export default function TrafficLight({
25+
id,
26+
timeline,
27+
time,
28+
}: TrafficLightProps) {
1729
const [mounted, setMounted] = useState(false);
18-
1930
const ref = useRef<Mesh>(null);
2031
const { modelPath, scale, rotation, offset, bodyColor } = currentTrafficLight;
2132
const { scene } = useGLTF(modelPath);
2233
const clonedScene = useMemo(() => scene.clone(true), [scene]);
2334

35+
const targetPosition = useMemo(() => new Vector3(), []);
36+
const targetQuaternion = useMemo(() => new Quaternion(), []);
37+
2438
useEffect(() => {
2539
setMounted(true);
2640
}, []);
@@ -38,16 +52,15 @@ export default function TrafficLight({ id, timeline, time }: TrafficLightProps)
3852
}, [clonedScene, bodyColor]);
3953

4054
useFrame(() => {
41-
const frame = timeline.find((f) => f.time >= time);
42-
if (frame && ref.current) {
43-
ref.current.position.set(frame.x, frame.z, frame.y);
44-
ref.current.rotation.y = -frame.yaw;
45-
}
55+
if (!ref.current || timeline.length === 0) return;
56+
const frame = getInterpolatedFrame(timeline, time);
57+
if (!frame) return;
58+
targetPosition.set(frame.x, frame.z, frame.y);
59+
ref.current.position.lerp(targetPosition, 0.2);
60+
targetQuaternion.setFromEuler(new Euler(0, -frame.yaw, 0));
61+
ref.current.quaternion.slerp(targetQuaternion, 0.2);
4662
});
4763

48-
const hasSpawned = timeline.length > 0 && timeline[0].time <= time;
49-
if (!mounted || !hasSpawned) return null;
50-
5164
return (
5265
<primitive
5366
ref={ref as React.RefObject<Object3D>}

GEMstack/onboard/visualization/sr_viz/threeD/src/components/Vehicle.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { currentVehicle } from "@/config/vehicleConfig";
66
import { FrameData } from "@/types/FrameData";
77
import { OrbitControls } from "@react-three/drei";
88
import URDFLoader from "urdf-loader";
9+
import { getInterpolatedFrame } from "@/utils/getInterpolatedFrame";
910

1011
interface VehicleProps {
1112
timeline: FrameData[];
@@ -15,7 +16,7 @@ interface VehicleProps {
1516
export default function Vehicle({ timeline, time }: VehicleProps) {
1617
const ref = useRef<THREE.Group>(null);
1718
const vehicleGroup = useRef<THREE.Group>(new THREE.Group());
18-
const mode = useCameraController(ref, timeline, time);
19+
const mode = useCameraController(ref);
1920

2021
const targetPosition = useMemo(() => new THREE.Vector3(), []);
2122
const targetQuaternion = useMemo(() => new THREE.Quaternion(), []);
@@ -55,14 +56,11 @@ export default function Vehicle({ timeline, time }: VehicleProps) {
5556

5657
useFrame(() => {
5758
if (!ref.current || timeline.length === 0) return;
58-
59-
const frame = timeline.find((f) => f.time >= time) ?? timeline.at(-1);
60-
if (!frame) return;
61-
62-
targetPosition.set(frame.x, 0, frame.y);
59+
const interp = getInterpolatedFrame(timeline, time);
60+
if (!interp) return;
61+
targetPosition.set(interp.x, 0, interp.y);
6362
ref.current.position.lerp(targetPosition, 0.2);
64-
65-
targetQuaternion.setFromEuler(new THREE.Euler(0, -frame.yaw, 0));
63+
targetQuaternion.setFromEuler(new THREE.Euler(0, -interp.yaw, 0));
6664
ref.current.quaternion.slerp(targetQuaternion, 0.2);
6765
});
6866

@@ -74,4 +72,4 @@ export default function Vehicle({ timeline, time }: VehicleProps) {
7472
{mode === "free" && <OrbitControls />}
7573
</>
7674
);
77-
}
75+
}

0 commit comments

Comments
 (0)