I'm working on a Three.js project (React) using the @react-three/fiber and @react-three/drei libraries. My goal is to place markers on the surface of a terrain .glb model based on specific coordinates. However, I'm facing an issue where the markers are being positioned below the model instead of on its surface.
import React, { useEffect, useRef, useState } from "react";
import { Canvas, useFrame } from "@react-three/fiber";
import { PresentationControls, Stage, useGLTF } from "@react-three/drei";
import * as THREE from "three";
function Model({ setLandscape, ...props }) {
const gltf = useGLTF("/landscape_free.glb");
useEffect(() => {
setLandscape(gltf.scene);
}, [gltf, setLandscape]);
return <primitive object={gltf.scene} {...props} scale={[0.8, 0.8, 0.8]} />;
}
function Marker({ initialPosition, landscape }) {
const ref = useRef();
useEffect(() => {
if (landscape && ref.current) {
const raycaster = new THREE.Raycaster();
const direction = new THREE.Vector3(0, -1, 0); // Cast downward
const positionAdjusted = initialPosition.clone();
positionAdjusted.y += 1; // Move ray origin slightly above the model
raycaster.set(positionAdjusted, direction);
const intersects = raycaster.intersectObject(landscape, true);
if (intersects.length > 0) {
ref.current.position.copy(intersects[0].point);
} else {
console.warn("No intersection found for marker at", initialPosition);
}
}
}, [initialPosition, landscape]);
const { scene } = useGLTF("/pointer.glb");
return <primitive object={scene} ref={ref} scale={[0.01, 0.01, 0.01]} />;
}
const coordinates = [
new THREE.Vector3(0, 0.034, 0),
new THREE.Vector3(-0.4, 0.2, -0.4),
new THREE.Vector3(-0.4, 0.1, -0.4),
];
function Map() {
const [landscape, setLandscape] = useState(null);
useEffect(() => {
if (landscape) {
const boundingBox = new THREE.Box3().setFromObject(landscape);
console.log("Landscape bounding box:", boundingBox);
console.log("Min:", boundingBox.min);
console.log("Max:", boundingBox.max);
}
}, [landscape]);
return (
<div>
<Canvas
dpr={[1, 2]}
shadows
camera={{ fov: 45 }}
style={{
position: "absolute",
width: "100%",
height: "100%",
touchAction: "none",
}}
>
<PresentationControls
speed={1.5}
global
zoom={1}
polar={[-0.1, Math.PI / 4]}
>
<Stage environment="sunset">
{coordinates.map((coord, index) => (
<Marker
key={index}
initialPosition={coord}
landscape={landscape}
/>
))}
<Model scale={0.8} setLandscape={setLandscape} />
</Stage>
</PresentationControls>
</Canvas>
</div>
);
}
export default Map;
My glb model is this one: https://sketchfab.com/3d-models/landscape-free-05a23d3d1c1d4e78aee7fc9257bf5332
Problems:
- I have three coordinates but only one marker is shown.
- Marker is shown below the model
How it is displayed in the browser
I tried Raycasting to show it above the model but it did not work.