Я использую raycaster в mapbox с 3d-объектами. Мне нужно обнаружить щелчок по трехмерному объекту. Но когда я щелкаю по объекту, я иногда получаю пустой raycaster.intersectObject (массив) и иногда заполненный массив, скорее, он всегда должен иметь трехмерный объект, который я щелкнул.
У меня есть простая карта, как описано здесь . Я добавил к нему только часть Raycasting, чтобы сделать его максимально простым.
Вот мой код.
var longitude, latitude, diff, map, destLongitude, destLatitude, distFrmlast = 0;
var interval = 5000; // initial condition
var assetArr = [];
var modelOrigin, modelOrigin2;
var modelAltitude, modelAltitude2;
var modelRotate, modelRotate2;
var modelAsMercatorCoordinate, modelAsMercatorCoordinate2;
var modelTransform, modelTransform2;
var THREE;
var customLayer, customLayer2;
var previousDistance = 0, currentDistance = 0;
var clock = new THREE.Clock();
var mixer;
var renderer = null;
$(document).ready(function(){
longitude = 77.123643;
latitude = 28.707272;
assetArr.push({id:"3d0", cord:{lng:longitude,lat:latitude}, url:'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf', scaleFactor:3, rad: 0.02, origCoord:{lng:longitude,lat:latitude}});
showmap();
});
function showmap(callback){
mapboxgl.accessToken = 'pk.eyJ1IjoibWFzaDEyIiwiYSI6ImNrNzBhMHc2aTFhMnkza3J2OG51azV6aW0ifQ.1FeBAzRjqkSQex-u-GiPyw';
map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/streets-v11',
center: [longitude,latitude],
zoom: 17.6,
pitch: 95,
bearing: -17.6,
container: 'map',
antialias: false,
});
var geojson = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [longitude,latitude]
},
'properties': {
'title': 'Mapbox',
'description': 'Park Centra'
}
}
]
};
// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on('zoom',function(){
map.jumpTo({ center: [longitude,latitude] });
});
map.on('rotate',function(){
document.getElementById('info').innerHTML = JSON.stringify(longitude +" : : "+latitude+" : : "+diff);
map.jumpTo({center: [longitude,latitude]});
});
map.on('load', function() {
// Insert the layer beneath any symbol layer.
console.log("map loaded");
var layers = map.getStyle().layers;
console.log(layers);
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
labelLayerId = layers[i].id;
break;
}
}
// map.addLayer(customLayer, 'road-label');
map.addLayer(
{
'id': '3d-buildings',
'source': 'composite',
'source-layer': 'building',
'filter': ['==', 'extrude', 'true'],
'type': 'fill-extrusion',
'minzoom': 15,
'paint': {
'fill-extrusion-color': '#aaa',
// use an 'interpolate' expression to add a smooth transition effect to the
// buildings as the user zooms in
'fill-extrusion-height': [
'interpolate',
['linear'],
['zoom'],
15,
0,
15.05,
['get', 'height']
],
'fill-extrusion-base': [
'interpolate',
['linear'],
['zoom'],
15,
0,
15.05,
['get', 'min_height']
],
'fill-extrusion-opacity': 0.6
}
},
labelLayerId
);
model3(assetArr[0].id, assetArr[0].cord, assetArr[0].url , assetArr[0].scaleFactor);
map.addLayer(customLayer3, 'waterway-label');
});
}
var renderFlag = true,scene, camera;
function model3(Id, coordinates, gltfUrl , scaleFactor){
// parameters to ensure the model is georeferenced correctly on the map
//var modelOrigin = [77.052024, 28.459822];
console.log("Values ",coordinates.lng, coordinates.lat);
modelOrigin3 = [coordinates.lng, coordinates.lat];
modelAltitude3 = 0;
modelRotate3 = [Math.PI / 2, 0, 0];
modelAsMercatorCoordinate3 = mapboxgl.MercatorCoordinate.fromLngLat(
modelOrigin3,
modelAltitude3
);
// transformation parameters to position, rotate and scale the 3D model onto the map
modelTransform3 = {
translateX: modelAsMercatorCoordinate3.x,
translateY: modelAsMercatorCoordinate3.y,
translateZ: modelAsMercatorCoordinate3.z,
rotateX: modelRotate3[0],
rotateY: modelRotate3[1],
rotateZ: modelRotate3[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: modelAsMercatorCoordinate3.meterInMercatorCoordinateUnits(),
langlat: modelOrigin3
};
THREE = window.THREE;
// configuration of the custom layer for a 3D model per the CustomLayerInterface
customLayer3 = {
id: Id,
type: 'custom',
renderingMode: '3d',
onAdd: function (map, gl) {
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
this.scene = new THREE.Scene();
camera = this.camera;
scene = this.scene;
// create two three.js lights to illuminate the model
var light1 = new THREE.AmbientLight( 0xffffff );
light1.position.set(0, -70, 100).normalize();
this.scene.add(light1);
var light2 = new THREE.AmbientLight( 0xffffff );
light2.position.set(0, 70, 100).normalize();
this.scene.add(light2);
// use the three.js GLTF loader to add the 3D model to the three.js scene
var loader = new THREE.GLTFLoader();
loader.load(
gltfUrl,
function (gltf) {
this.scene.add(gltf.scene);
}.bind(this)
);
this.map = map;
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
this.renderer.autoClear = false;
if(renderFlag == true){
renderFlag = false;
this.renderer.domElement.addEventListener( 'touchend', onClick, false );
}
},
render: function (gl, matrix) {
var rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform3.rotateX
);
var rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform3.rotateY
);
var rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform3.rotateZ
);
var m = new THREE.Matrix4().fromArray(matrix);
var l = new THREE.Matrix4()
.makeTranslation(
modelTransform3.translateX,
modelTransform3.translateY,
modelTransform3.translateZ
)
.scale(
new THREE.Vector3(
modelTransform3.scale*scaleFactor,
-modelTransform3.scale*scaleFactor,
modelTransform3.scale*scaleFactor
)
)
.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();
}
}
};
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2( Infinity, Infinity );
function onClick( event ) {
event.preventDefault();
mouse.x = ( event.changedTouches[0].clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.changedTouches[0].clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children, true );
console.log("Here",intersects);
if ( intersects.length > 0 ) {
alert("hi");
console.log( 'Intersection:', intersects[ 0 ].object.name == "");
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Add a 3D model</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://api.mapbox.com/mapbox-gl-js/v1.9.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.9.1/mapbox-gl.css" rel="stylesheet" />
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<script src="https://unpkg.com/three@0.106.2/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.106.2/examples/js/loaders/GLTFLoader.js"></script>
<div id="map"></div>
<script src = "js/test.js"> </script>
</body>
</html>
Журнал иногда при нажатии сообщает мне это
Here [] (js line 347).
, а иногда это похоже на
Here
[{…}]
0:
distance: 45.707979283756266
face: Tb {a: 6, b: 89, c: 23, normal: n, vertexNormals: Array(0), …}
faceIndex: 98
object: ra {uuid: "C0D03F0E-CE94-4B4B-A9B2-C8117268E863", name: "Briks_302", type: "Mesh", parent: G, children: Array(0), …}
point: n {x: 12.70179089111657, y: 2.0773969954241096, z: -43.858503167413765}
uv: C {x: 0.940550558052383, y: 0.42664070999192283}
__proto__: Object
length: 1
__proto__: Array(0)
Почему Я получаю такое неравномерное поведение при нажатии на 3D-объект? Я застрял на этом этапе. Кто-нибудь может мне здесь помочь?
Изменить
Извините за беспорядочный код. На этот раз я попытался удалить нежелательный код. Я попытался переместить матрицу l
в функцию onAdd
, но, согласно вашему ответу здесь , когда я пытался использовать MercatorCoordinate.fromLngLat, я получил неопределенную ошибку MercatorCoordinate cordinate. Затем я попытался переместить матрицу l
в onAdd
, как показано ниже, но все же объект пересечения заполняется только на втором объекте, а не на первом. вот обновленный js
var longitude, latitude, diff, map;
var assetArr = [];
var modelOrigin3;
var modelAltitude3;
var modelRotate3;
var modelAsMercatorCoordinate3;
var modelTransform3;
var THREE;
var renderer = null;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2( Infinity, Infinity );
var lastsArr = [],ar = [],renderFlag = true, scene, camera;
$(document).ready(function(){
longitude = 77.122279;
latitude = 28.706246;
assetArr.push({ id:"3d1", cord:{lng:77.122279,lat:28.706246}, url:'https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf', scaleFactor: 0.5 , rad: 0.015, origCoord:{lng:77.122279,lat:28.706246}});
assetArr.push({ id:"3d2", cord:{lng:77.125959,lat:28.707724}, url:'https://www.zamit.one/location/gun/scene.gltf', scaleFactor: 9 , rad: 0.015, origCoord:{lng:77.125959,lat:28.707724}});
showmap();
});
function showmap(callback){
mapboxgl.accessToken = 'pk.eyJ1IjoibWFzaDEyIiwiYSI6ImNrNzBhMHc2aTFhMnkza3J2OG51azV6aW0ifQ.1FeBAzRjqkSQex-u-GiPyw';
map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/streets-v11',
center: [longitude,latitude],
zoom: 17.6,
pitch: 95,
bearing: -17.6,
container: 'map',
antialias: false,
dragPan: true,
dragRotate: false,
maxZoom: 18.7,
minZoom: 16
});
var geojson = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [longitude,latitude]
},
'properties': {
'title': 'Mapbox',
'description': 'Park Centra'
}
}
]
};
// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on('zoom',function(){
map.jumpTo({ center: [longitude,latitude] });
});
map.on('rotate',function(){
document.getElementById('info').innerHTML = JSON.stringify(longitude +" : : "+latitude+" : : "+diff);
map.jumpTo({center: [longitude,latitude]});
});
map.on('load', function() {
// Insert the layer beneath any symbol layer.
console.log("map loaded");
var layers = map.getStyle().layers;
console.log(layers);
var labelLayerId;
for (var i = 0; i < layers.length; i++) {
if (layers[i].type === 'symbol' && layers[i].layout['text-field']) {
labelLayerId = layers[i].id;
break;
}
}
map.on('click', e => {
onClick(e);
});
map.style.stylesheet.layers.forEach(function(layer) {
if (layer.type === 'symbol' && layer.id != 'waterway-label') {
map.removeLayer(layer.id);
}
});
for(var i = 0; i< assetArr.length; i++){
model3(assetArr[i].id, assetArr[i].cord, assetArr[i].url , assetArr[i].scaleFactor,i);
}
for(var i = 0;i<lastsArr.length; i++){
map.addLayer(lastsArr[i], 'waterway-label');
console.log("i ",i,lastsArr[i]);
}
});
}
function model3(Id, coordinates, gltfUrl , scaleFactor,i){
// parameters to ensure the model is georeferenced correctly on the map
//var modelOrigin = [77.052024, 28.459822];
console.log("Values ",coordinates.lng, coordinates.lat);
modelOrigin3 = [coordinates.lng, coordinates.lat];
modelAltitude3 = 0;
modelRotate3 = [Math.PI / 2, 0, 0];
modelAsMercatorCoordinate3 = mapboxgl.MercatorCoordinate.fromLngLat(
modelOrigin3,
modelAltitude3
);
// transformation parameters to position, rotate and scale the 3D model onto the map
modelTransform3 = {
translateX: modelAsMercatorCoordinate3.x,
translateY: modelAsMercatorCoordinate3.y,
translateZ: modelAsMercatorCoordinate3.z,
rotateX: modelRotate3[0],
rotateY: modelRotate3[1],
rotateZ: modelRotate3[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: modelAsMercatorCoordinate3.meterInMercatorCoordinateUnits(),
langlat: modelOrigin3
};
ar.push(modelTransform3);
var l;
THREE = window.THREE;
// configuration of the custom layer for a 3D model per the CustomLayerInterface
customLayer3 = {
id: Id,
type: 'custom',
renderingMode: '3d',
onAdd: function (map, gl) {
// this.camera = new THREE.Camera();
this.camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
this.scene = new THREE.Scene();
camera = this.camera;
scene = this.scene;
var rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
ar[i].rotateX
);
var rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
ar[i].rotateY
);
var rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
ar[i].rotateZ
);
l = new THREE.Matrix4()
.makeTranslation(
ar[i].translateX,
ar[i].translateY,
ar[i].translateZ
)
.scale(
new THREE.Vector3(
ar[i].scale*scaleFactor,
-ar[i].scale*scaleFactor,
ar[i].scale*scaleFactor
)
)
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
var light1 = new THREE.AmbientLight( 0xffffff );
light1.position.set(0, -70, 100).normalize();
this.scene.add(light1);
var light2 = new THREE.AmbientLight( 0xffffff );
light2.position.set(0, 70, 100).normalize();
this.scene.add(light2);
// use the three.js GLTF loader to add the 3D model to the three.js scene
var loader = new THREE.GLTFLoader();
loader.load(
gltfUrl,
function (gltf) {
this.scene.add(gltf.scene);
}.bind(this)
);
this.map = map;
// use the Mapbox GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
this.renderer.autoClear = false;
if(renderFlag == true){
renderFlag = false;
}
},
render: function (gl, matrix) {
var m = new THREE.Matrix4().fromArray(matrix);
this.camera.projectionMatrix = m.multiply(l);
this.renderer.state.reset();
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
}
lastsArr.push(customLayer3)
};
function onClick( event ) {
event.preventDefault();
//I had to change the changedTouches to point to adapt to the incoming event object
mouse.x = ( event.point.x / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.point.y / window.innerHeight ) * 2 + 1;
const camInverseProjection =
new THREE.Matrix4().getInverse(this.camera.projectionMatrix);
const cameraPosition =
new THREE.Vector3().applyMatrix4(camInverseProjection);
const mousePosition =
new THREE.Vector3(mouse.x, mouse.y, 1)
.applyMatrix4(camInverseProjection);
const viewDirection = mousePosition.clone()
.sub(cameraPosition).normalize();
this.raycaster.set(cameraPosition, viewDirection);
//no change from here on
var intersects = raycaster.intersectObjects( scene.children, true );
console.log("Here",intersects);
if ( intersects.length > 0 ) {
// alert("hi");
for(var i = 0;i<intersects.length;i++){
console.log( 'Intersection:', intersects[ 0 ].object.name);
}
}
}