I have been trying to get a 3D scatter plot to display using InstancedMesh of three.js via a React app (therefore using react three fiber).
After a few days I was able to get the 3D scatter plot of spheres and also color them. However I want to be able to select/highlight individual spheres on mouse click/hover. I followed a simple tutorial that used onPointerOver and onPointerOut but that does not seem to work perhaps because it was not meant for InstancedMesh objects.
It looks like I need to use raycaster, but it is not clear to me how to do it. Any suggestions would be really helpful.
Steps to setup -
npx create-react-app demo
cd demo
npm install three
npm i @react-three/fiber
npm i @react-three/drei
Current code that shows the differently colored spheres -
App.jsx
import React from 'react'
import { Suspense } from "react";
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import Spheres from "./IScatter";
import * as THREE from "three";
function App() {
return (
<div>
<Canvas style=>
<OrbitControls enableZoom={true} />
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]}/>
<Suspense fallback={null}>
<primitive object={new THREE.AxesHelper(1.5)} />
<Spheres />
</Suspense>
</Canvas>
</div>
);
}
export default App;
IScatter.jsx
import * as THREE from "three";
import React, { useRef, useState } from "react";
import { useEffect } from "react";
import { DoubleSide } from "three";
const points = [ [1, 0, -1], [0, 1, -0.5], [0.5, 0.5, 0.5], [1,0.25,-1], [1,0,1], [0,1,0.5] ];
const content = ["Hello", "World", "Hello World", "Shoes", "Drone", "Foo Bar"];
const colors = [0,0,0,5,5,5];
const tempSphere = new THREE.Object3D();
const Spheres = () => {
const material = new THREE.MeshLambertMaterial({ opacity: 0.5, side: THREE.DoubleSide, transparent: true,});
const spheresGeometry = new THREE.SphereBufferGeometry(0.25, 15, 15);
const ref = useRef();
const [active, setActive] = useState(false);
const [hover, setHover] = useState(false);
useEffect(() => {
points.map(function (val, row) {
console.log(val, row);
tempSphere.position.set(val[0], val[1], val[2]);
tempSphere.updateMatrix();
ref.current.setMatrixAt(row, tempSphere.matrix);
ref.current.setColorAt(row, new THREE.Color(`hsl(${colors[row]*100}, 100%, 50%)`));
});
ref.current.instanceMatrix.needsUpdate = true;
// ref.current.instanceColor.needsUpdate = true;
});
return (
<instancedMesh onClick={() => {
setActive(!active);
}}
onPointerOver={() => {
setHover(true);
}}
onPointerOut={() => {
setHover(false);
}} ref={ref} rotation={[0,0,0]} args={[spheresGeometry, material, 15]} opacity={active?0.9:0.5} />
);
};
export default Spheres;
I would like to change the opacity of the selected sphere and perhaps make them a little larger so that we know what it selected. If possible I would also like to show the HTML content associated with a sphere, adjacent to the canvas, away from the scatterplot of spheres, whenever a sphere is selected.
EDIT: I essentially want to implement this - https://threejs.org/examples/webgl_instancing_raycast but using react three fiber and to also show associated content of the spheres.
from Unable to figure out how to highlight or select individual objects in InstancedMesh and display custom HTML tooltip using react three fiber
No comments:
Post a Comment