does anyone have an example of how to call a zoom to fit function with this library?
You could compute bounds in the scene and put your camera at the right place, or render your content according to the viewport. The following will create a plane that fits exactly the screen, and if you resize the window, it will re-render accordingly.
const { viewport } = useThree()
return (
<mesh>
<planeBufferGeometry attach="geometry" args={[viewport.width, viewport.height ]} />
<meshBasicMaterial attach="material" color="hotpink" />
</mesh>
)
@drcmda i am looking for an example where i have a group of meshes that are made up of shape geometry. Once the scene has updated i can call a function like below where object is the group of meshes.
const fitCameraToObject = function(camera, object, offset, controls) {
offset = offset || 1.25;
const boundingBox = new THREE.Box3();
// get bounding box of object - this will be used to setup controls and camera
boundingBox.setFromObject(object);
const center = boundingBox.getCenter();
const size = boundingBox.getSize();
// get the max side of the bounding box (fits to width OR height as needed )
const maxDim = Math.max(size.x, size.y, size.z);
const fov = camera.fov * (Math.PI / 180);
let cameraZ = Math.abs(maxDim / 4 * Math.tan(fov * 2));
cameraZ *= offset; // zoom out a little so that objects don't fill the screen
camera.position.z = cameraZ;
const minZ = boundingBox.min.z;
const cameraToFarEdge = minZ < 0 ? -minZ + cameraZ : cameraZ - minZ;
camera.far = cameraToFarEdge * 3;
camera.updateProjectionMatrix();
if (controls) {
// set camera to rotate around center of loaded object
controls.target = center;
// prevent camera from zooming out far enough to create far plane cutoff
controls.maxDistance = cameraToFarEdge * 2;
controls.saveState();
} else {
camera.lookAt(center);
}
};
You can do that. UseThree gives you access to the scene and the camera.
Here's an example of how I'm adjusting the camera to a desired size. You could probably combine it with the code above for your needs.
const Camera = () => {
const camera = useRef()
const { aspect, size, setDefaultCamera } = useThree()
const pixelToThreeUnitRatio = 1
const planeDistance = 0
const cameraDistance = 500
const distance = cameraDistance - planeDistance
const height = size.height / pixelToThreeUnitRatio
const halfFovRadians = Math.atan((height / 2) / distance)
const fov = 2 * halfFovRadians * (180/Math.PI)
useEffect(() => void setDefaultCamera(camera.current), [])
return <perspectiveCamera
ref={camera}
aspect={aspect}
fov={fov}
position={[0, 0, cameraDistance]}
onUpdate={self => self.updateProjectionMatrix()}
/>
}
const App = () => {
return (
<Canvas >
<Camera />
<Content />
</Canvas>
);
}
thanks @drcmda @ryanking1809
const Content = ({ url }) => {
let group = useRef();
let axis = useRef();
let controls = useRef();
const [ data, loading ] = useFetch(url);
const { camera } = useThree();
camera.up.set(0, 0, 1);
camera.position.z = 4;
if (!loading) {
const json = parser.parse(data, options);
const surfaceJson = json.gbXML.Campus.Surface;
const surfaceMeshes = getSurfaceMeshes(surfaceJson);
if (group.current) {
const bbox = new THREE.Box3().setFromObject(group.current);
const sphere = bbox.getBoundingSphere(new THREE.Sphere());
const { center, radius } = sphere;
controls.current.reset();
controls.current.target.copy(center);
controls.current.maxDistance = 5 * radius;
camera.position.copy(center.clone().add(new THREE.Vector3(1.0 * radius, -1.0 * radius, 1.0 * radius)));
camera.far = 10 * radius;
camera.updateProjectionMatrix();
if (axis.current) {
axis.current.scale.set(radius, radius, radius);
axis.current.position.copy(center);
}
}
//console.log(scene, controls, camera);
return (
<Fragment>
<ambientLight color="#444444" />
<pointLight color="white" intensity={0.5} position={[ 0, 0, 1 ]} />
<directionalLight castShadows />
<axesHelper ref={axis} />
<orbitControls ref={controls} args={[ camera ]} />
<group ref={group} name="meshes">
{surfaceMeshes.map((mesh, index) => (
<primitive
key={index}
onClick={({ object: { userData: data } }) => console.log(data)}
object={mesh}
/>
))}
</group>
</Fragment>
);
}
return <group />;
};
@drcmda love the library, tying this in with graphql and redux is gonna be so sweet.
Just to help others out, I got the desired effect working with an orthographic camera by setting the zoom.
const { camera, size: { width, height } } = useThree();
const aabb = new THREE.Box3().setFromObject(someObject3D);
camera.zoom = Math.min(
width / (aabb.max.x - aabb.min.x),
height / (aabb.max.y - aabb.min.y)
);
camera.updateProjectionMatrix();
Sorry for the Noob qestion @partynikko.
Where do I put this code inside a React.Component? I have an array of meshes I get asynchronously and render , and I want to adjust the camera once I'm done..
better not a react class component, this lib's targeted for function components and hooks. there's only one rule: your function shouldn't have any side-effects in it, do not create or mutate objects in it. run these effects inside of a useEffect.
function Meshes({ url }) {
const { camera }聽= useThree()
const [meshes, setMeshes] = useState([])
useEffect(() => void fetchMeshes(url).then(meshes => {
setMeshes(meshes)
adjustCamera(camera)
}), [url])
return meshes.map(data => (
<mesh>
...
Sorry for the Noob qestion @partynikko.
Where do I put this code inside a React.Component? I have an array of meshes I get asynchronously and render , and I want to adjust the camera once I'm done..
Hi there, no issues!
I agree what drcmda wrote. However, if you post the code you are working with it will be easier to suggest an approach for you :)
Most helpful comment
Just to help others out, I got the desired effect working with an orthographic camera by setting the zoom.