Hello,
Link to website: Vite + React
Link to repo: GitHub - adaniel2/danieldoescode: Portfolio website
I am new to react-three-fiber, and facing some issues with the camera when loading and rendering point cloud files. The visualizer uses a custom Points component that leverages a cached color mapping (via a React context) to set up a Three.js BufferGeometry. For camera controls and framing, I use Drei’s <OrbitControls>
, <Bounds>
, and <Center>
components. Rotation works fine after initialization; however, the camera’s starting distance is inconsistent—it sometimes starts too zoomed in and sometimes too zoomed out:
How can I go about this? Before requiring to map colour to vertexes by their altitude, I was following this example, which still had a few issues with the camara when loaded: https://codesandbox.io/p/sandbox/pcdloader-tephow
Here is my current code (I am going to deal with all the prop drilling later):
// PointCloudViewer.jsx
import React, { useRef, useEffect, useState, useContext } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Bounds, Center } from "@react-three/drei";
import * as THREE from "three";
import classes from "./PointCloudViewer.module.css";
import { DarkModeSwitch } from "react-toggle-dark-mode";
import { IoIosCloseCircleOutline } from "react-icons/io";
import ToggleHeaderButton from "./ToggleHeaderButton";
import SideBar from "./SideBar";
import {
PointCloudMappingContext,
PointCloudMappingProvider,
} from "./PointCloudMappingContext";
// Points component that uses the cached mapping from context and applies the given point size.
function Points({ size }) {
const geometryRef = useRef();
const mapping = useContext(PointCloudMappingContext);
useEffect(() => {
if (!mapping) return;
if (geometryRef.current) {
// Set positions and colors from the cached mapping.
geometryRef.current.setAttribute(
"position",
new THREE.Float32BufferAttribute(mapping.positions, 3)
);
geometryRef.current.setAttribute(
"color",
new THREE.Float32BufferAttribute(mapping.colors, 3)
);
// Compute the bounding information.
geometryRef.current.computeBoundingBox();
geometryRef.current.computeBoundingSphere();
}
}, [mapping]);
return (
<points>
<bufferGeometry ref={geometryRef} />
<pointsMaterial size={size} vertexColors />
</points>
);
}
export default function PointCloudViewer({
points,
onClose,
confirmation,
isHeaderVisible,
setHeaderVisible,
setViewerActive,
isSideBarVisible,
}) {
if (!points || !confirmation) return null;
const [isDarkMode, setDarkMode] = useState(false);
const [pointSize, setPointSize] = useState(0.0004);
const toggleDarkMode = (checked) => {
setDarkMode(checked);
};
useEffect(() => {
setViewerActive(true);
return () => {
setViewerActive(false);
};
}, [setViewerActive]);
return (
<PointCloudMappingProvider points={points} mappingAxis="y">
<div className={classes.overlay} data-viewer-type="point-cloud">
<ToggleHeaderButton
isHeaderVisible={isHeaderVisible}
setHeaderVisible={setHeaderVisible}
/>
<div
className={classes.buttonContainer}
style={{
top: isHeaderVisible ? "92px" : "36px",
transition: "top 0.3s ease-in-out",
}}
>
<IoIosCloseCircleOutline
onClick={onClose}
className={`${classes.closeButton} ${
isDarkMode ? classes.darkCloseButton : ""
}`}
/>
<DarkModeSwitch
checked={isDarkMode}
onChange={toggleDarkMode}
size={20}
className={classes.darkModeToggle}
/>
</div>
{isSideBarVisible && (
<SideBar isHeaderVisible={isHeaderVisible} setPointSize={setPointSize} />
)}
<div className={classes.canvasContainer}>
<Canvas>
<color
attach="background"
args={isDarkMode ? ["#333"] : ["#ffffff"]}
/>
<ambientLight />
<OrbitControls makeDefault />
<Bounds fit clip>
<Center>
<Points size={pointSize} />
</Center>
</Bounds>
</Canvas>
</div>
</div>
</PointCloudMappingProvider>
);
}
Appreciate any assistance I can get, as I would love to get this fixed.