I made a react map component using maptiler docs. Then i added a 3d gltf object into the map and placed correctly. now my target is to load a custom react component when someone clicked the object, but then issue is ,i didnt get click event correctly. i can easily get map coordinates. but i need the exact click event on 3d object which i clicked. where am i wrong ?
import React, { useEffect, useRef, useState } from "react";
import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import * as maptilersdk from "@maptiler/sdk";
import "@maptiler/sdk/dist/maptiler-sdk.css";
const ThreeDModel = () => {
const mapContainerRef = useRef(null);
const [dishComponent, setDishComponent] = useState(false);
useEffect(() => {
maptilersdk.config.apiKey = "xxxxxxxxxxxxxxx";
const map = (window.map = new maptilersdk.Map({
container: mapContainerRef.current,
style: maptilersdk.MapStyle.STREETS,
zoom: 18,
center: [148.9819, -35.3981],
pitch: 60,
antialias: true,
}));
// const marker = new maptilersdk.Marker().setLngLat([148.9819, -35.3981]).addTo(map);
// parameters to ensure the model is georeferenced correctly on the map
var modelOrigin = [148.9819, -35.39847];
var modelAltitude = 5;
var modelRotate = [Math.PI / 2, 0, 0];
var modelAsMercatorCoordinate = maptilersdk.MercatorCoordinate.fromLngLat(modelOrigin, modelAltitude);
// transformation parameters to position, rotate and scale the 3D model onto the map
var modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* Since our 3D model is in real world meters, a scale transform needs to be
* applied since the CustomLayerInterface expects units in MercatorCoordinates.
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits(),
};
// parameters and transformations remain the same
const customLayer = {
id: "3d-model",
type: "custom",
renderingMode: "3d",
onAdd: function (map, gl) {
this.camera = new THREE.PerspectiveCamera();
this.scene = new THREE.Scene();
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, -70, 100).normalize();
this.scene.add(directionalLight);
const directionalLight2 = new THREE.DirectionalLight(0xffffff);
directionalLight2.position.set(0, 70, 100).normalize();
this.scene.add(directionalLight2);
const loader = new GLTFLoader();
loader.load("https://docs.maptiler.com/sdk-js/assets/34M_17/34M_17.gltf", (gltf) => {
gltf.scene.traverse((child) => {
if (child.isMesh) {
child.material.depthWrite = true;
child.userData = { name: "clicked on dish" };
setDishComponent(true);
// Set custom data for each object
}
});
this.scene.add(gltf.scene);
});
this.map = map;
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});
this.renderer.autoClear = false;
// Bind the onClick function to the customLayer object
this.onClick = this.onClick.bind(this);
// Add event listener for mouse clicks on the canvas
map.getCanvas().addEventListener("click", this.onClick);
},
onClick: function (event) {
const mouse = new THREE.Vector2();
const canvas = this.map.getCanvas();
const rect = canvas.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, this.camera);
const intersects = raycaster.intersectObjects(this.scene.children, true);
if (intersects.length > 0) {
const clickedObject = intersects[0].object;
const objectName = clickedObject.userData.name;
alert("Clicked object: " + objectName);
}
},
render: function (gl, matrix) {
const rotationX = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(1, 0, 0), modelTransform.rotateX);
const rotationY = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0, 1, 0), modelTransform.rotateY);
const rotationZ = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0, 0, 1), modelTransform.rotateZ);
const m = new THREE.Matrix4().fromArray(matrix);
const l = new THREE.Matrix4()
.makeTranslation(modelTransform.translateX, modelTransform.translateY, modelTransform.translateZ)
.scale(new THREE.Vector3(modelTransform.scale, -modelTransform.scale, modelTransform.scale))
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
this.camera.projectionMatrix = m.multiply(l);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
},
};
map.on("style.load", () => {
map.addLayer(customLayer);
});
map.on("click", function (e) {
const lat = e.lngLat.lat;
const lng = e.lngLat.lng;
console.log(lat, lng);
});
}, []);
return (
<div>
<div ref={mapContainerRef} style={{ position: "absolute", top: 0, bottom: 0, width: "100%" }}></div>
</div>
);
};
export default ThreeDModel;