Skip to content

Commit

Permalink
546 improve coloring for urdf renderer (#551)
Browse files Browse the repository at this point in the history
* Added full-screen functionality

* Features - BG, Wireframe style, brighter colors
  • Loading branch information
ivntsng authored Nov 7, 2024
1 parent d9b7c87 commit 7684374
Showing 1 changed file with 166 additions and 22 deletions.
188 changes: 166 additions & 22 deletions frontend/src/components/files/URDFRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ interface Props {
files: UntarredFile[];
useControls?: boolean;
visualTheme?: VisualizationTheme | null;
showWireframe?: boolean;
}

const URDFRenderer = ({
urdfContent,
files,
useControls = true,
visualTheme = null,
showWireframe = false,
}: Props) => {
const containerRef = useRef<HTMLDivElement>(null);
const sceneRef = useRef<THREE.Scene | null>(null);
Expand All @@ -59,6 +61,10 @@ const URDFRenderer = ({
const [forceRerender, setForceRerender] = useState(0);
const jointPositionsRef = useRef<{ name: string; value: number }[]>([]);
const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
const [isWireframe, setIsWireframe] = useState(showWireframe);
const wireframeStateRef = useRef<boolean>(showWireframe);
const [isDarkBackground, setIsDarkBackground] = useState(false);
const darkBackgroundStateRef = useRef<boolean>(false);

const applyTheme = useCallback((theme: VisualizationTheme) => {
if (!sceneRef.current) return;
Expand Down Expand Up @@ -114,6 +120,9 @@ const URDFRenderer = ({
name: joint.name,
value: joint.value,
}));
wireframeStateRef.current = isWireframe;
darkBackgroundStateRef.current = isDarkBackground;

setIsFullScreen(false);
setForceRerender((prev) => prev + 1);
}
Expand All @@ -122,14 +131,17 @@ const URDFRenderer = ({
document.addEventListener("fullscreenchange", handleFullScreenChange);
return () =>
document.removeEventListener("fullscreenchange", handleFullScreenChange);
}, [jointControls]);
}, [jointControls, isWireframe, isDarkBackground]);

useEffect(() => {
if (!containerRef.current) return;

const scene = new THREE.Scene();
sceneRef.current = scene;
scene.background = new THREE.Color(0xf0f0f0);
scene.background = new THREE.Color(
darkBackgroundStateRef.current ? 0x222222 : 0xf0f0f0,
);
setIsDarkBackground(darkBackgroundStateRef.current);

const camera = new THREE.PerspectiveCamera(
50,
Expand All @@ -148,24 +160,36 @@ const URDFRenderer = ({
controls.enableDamping = true;
controls.dampingFactor = 0.25;

// Lighting setup
const frontLight = new THREE.DirectionalLight(0xffffff, 0.7);
frontLight.position.set(1, 1, 1);
scene.add(frontLight);
scene.children.forEach((child) => {
if (child instanceof THREE.Light) {
scene.remove(child);
}
});

const mainLight = new THREE.DirectionalLight(0xffffff, 2.0);
mainLight.position.set(5, 5, 5);
scene.add(mainLight);

const backLight = new THREE.DirectionalLight(0xffffff, 0.3);
backLight.position.set(-1, -1, -1);
scene.add(backLight);
const fillLight = new THREE.DirectionalLight(0xffffff, 0.8);
fillLight.position.set(-5, 2, -5);
scene.add(fillLight);

const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);

const loader = new URDFLoader();
loader.loadMeshCb = (path, _manager, onComplete) => {
const fileContent = files.find((f) => f.name.endsWith(path))?.content;
if (fileContent) {
const geometry = new STLLoader().parse(fileContent.buffer);
const material = new THREE.MeshPhongMaterial({ color: 0xaaaaaa });

const material = new THREE.MeshStandardMaterial({
color: 0xaaaaaa,
metalness: 0.4,
roughness: 0.6,
wireframe: showWireframe || false,
});

const mesh = new THREE.Mesh(geometry, material);
onComplete(mesh);
} else {
Expand Down Expand Up @@ -378,10 +402,8 @@ const URDFRenderer = ({
setOrientation((prev) => {
const newOrientation =
prev === "Z-up" ? "Y-up" : prev === "Y-up" ? "X-up" : "Z-up";
if (sceneRef.current && robotRef.current) {
if (robotRef.current) {
const robot = robotRef.current;

// Reset rotations
robot.rotation.set(0, 0, 0);

switch (newOrientation) {
Expand Down Expand Up @@ -465,7 +487,7 @@ const URDFRenderer = ({
}, [resetViewerState]);

const toggleFullScreen = useCallback(() => {
if (!parentRef.current) return;
if (!parentRef.current || isCycling) return;

if (!document.fullscreenElement) {
parentRef.current.requestFullscreen();
Expand All @@ -474,6 +496,18 @@ const URDFRenderer = ({
document.exitFullscreen();
setIsFullScreen(false);
}
}, [isCycling]);

const toggleBackground = useCallback(() => {
if (sceneRef.current) {
setIsDarkBackground((prev) => {
const newIsDark = !prev;
sceneRef.current!.background = new THREE.Color(
newIsDark ? 0x222222 : 0xf0f0f0,
);
return newIsDark;
});
}
}, []);

const getBackgroundColor = useCallback(() => {
Expand All @@ -483,9 +517,98 @@ const URDFRenderer = ({
case "dark":
return "bg-[#222222]";
default:
return "bg-[#f0f0f0]";
return isDarkBackground ? "bg-[#222222]" : "bg-[#f0f0f0]";
}
}, [visualTheme, isDarkBackground]);

useEffect(() => {
if (robotRef.current) {
robotRef.current.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material.wireframe = wireframeStateRef.current;
child.material.needsUpdate = true;
}
});
}
}, [forceRerender]);

useEffect(() => {
if (robotRef.current) {
robotRef.current.traverse((child) => {
if (child instanceof THREE.Mesh) {
child.material.wireframe = isWireframe;
child.material.needsUpdate = true;
}
});
}
wireframeStateRef.current = isWireframe;
}, [isWireframe]);

useEffect(() => {
if (sceneRef.current) {
sceneRef.current.background = new THREE.Color(
isDarkBackground ? 0x222222 : 0xf0f0f0,
);
}
}, [visualTheme]);

if (robotRef.current) {
robotRef.current.traverse((child) => {
if (child instanceof THREE.Mesh) {
const currentColor = child.material.color;
const material = new THREE.MeshStandardMaterial({
color: currentColor,
metalness: 0.4,
roughness: 0.6,
wireframe: isWireframe,
emissive: child.material.emissive,
emissiveIntensity: child.material.emissiveIntensity,
});
child.material = material;
child.material.needsUpdate = true;
}
});
}
}, [orientation, isWireframe, isDarkBackground]);

useEffect(() => {
if (sceneRef.current) {
const backgroundColor = isDarkBackground ? 0x222222 : 0xf0f0f0;
sceneRef.current.background = new THREE.Color(backgroundColor);
darkBackgroundStateRef.current = isDarkBackground;
}
}, [isDarkBackground]);

useEffect(() => {
if (!robotRef.current) return;

const robot = robotRef.current;
robot.rotation.set(0, 0, 0);

switch (orientation) {
case "Y-up":
robot.rotateX(-Math.PI / 2);
break;
case "X-up":
robot.rotateZ(Math.PI / 2);
break;
}

robot.traverse((child) => {
if (child instanceof THREE.Mesh) {
const currentColor = child.material.color;
const material = new THREE.MeshStandardMaterial({
color: currentColor,
metalness: 0.4,
roughness: 0.6,
wireframe: isWireframe,
emissive: child.material.emissive,
emissiveIntensity: child.material.emissiveIntensity,
});
child.material = material;
child.material.needsUpdate = true;
}
});
}, [orientation, isWireframe]);

return (
<div
Expand All @@ -502,10 +625,23 @@ const URDFRenderer = ({
</button>
<button
onClick={toggleFullScreen}
className={`${getOrientationButtonColors(visualTheme)} text-white font-bold w-8 h-8 rounded-full shadow-md flex items-center justify-center`}
disabled={isCycling}
className={`${getOrientationButtonColors(visualTheme)} text-white font-bold w-8 h-8 rounded-full shadow-md flex items-center justify-center disabled:opacity-50`}
>
{isFullScreen ? <FaCompress /> : <FaExpand />}
</button>
<button
onClick={() => setIsWireframe(!isWireframe)}
className={`${getOrientationButtonColors(visualTheme)} text-white font-bold w-8 h-8 rounded-full shadow-md flex items-center justify-center`}
>
{isWireframe ? "S" : "W"}
</button>
<button
onClick={toggleBackground}
className={`${getOrientationButtonColors(visualTheme)} text-white font-bold w-8 h-8 rounded-full shadow-md flex items-center justify-center`}
>
{isDarkBackground ? "L" : "D"}
</button>
</div>
</div>

Expand Down Expand Up @@ -544,7 +680,9 @@ const URDFRenderer = ({
</div>
{urdfInfo && (
<div className="p-4 rounded-lg shadow-md mb-4 bg-grey-100 font-mono">
<ul className="text-sm text-gray-800">
<ul
className={`text-sm ${isDarkBackground ? "text-gray-200" : "text-gray-800"}`}
>
<li>Joint Count: {urdfInfo.jointCount}</li>
<li>Link Count: {urdfInfo.linkCount}</li>
</ul>
Expand All @@ -554,10 +692,14 @@ const URDFRenderer = ({
{jointControls.map((joint, index) => (
<div key={joint.name} className="text-sm">
<div className="flex justify-between items-center mb-1">
<label className="font-medium text-gray-700">
<label
className={`font-medium ${isDarkBackground ? "text-gray-200" : "text-gray-700"}`}
>
{joint.name}
</label>
<span className="text-xs text-gray-500">
<span
className={`text-xs ${isDarkBackground ? "text-gray-400" : "text-gray-500"}`}
>
{joint.value.toFixed(2)}
</span>
</div>
Expand All @@ -573,7 +715,9 @@ const URDFRenderer = ({
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
disabled={isCycling}
/>
<div className="flex justify-between text-xs text-gray-500 mt-1">
<div
className={`flex justify-between text-xs ${isDarkBackground ? "text-gray-400" : "text-gray-500"} mt-1`}
>
<span>{joint.min.toFixed(2)}</span>
<span>{joint.max.toFixed(2)}</span>
</div>
Expand Down

0 comments on commit 7684374

Please sign in to comment.