I recently found out there is a very handy method in three-box for placing three.js objects on the map which is "projectToworld".
While trying to place my three.js objects using the method, I realized that the Vector3 the method returns are really huge and not on the map.
According to the documentation of threebox, it says
projectToWorld
tb.projectToWorld(lnglat) : THREE.Vector3
Calculate the corresponding THREE.Vector3 for a given lnglat. It's inverse method is tb.unprojectFromWorld.
So I decided to use this method to locate my animated object in three js canvas. But what the methods returns are really huge.
So as I expected, these values don't place the three objects on the map and all the objects disappeared because they presumably are placed at very distant locations.
How do I fix this issue?
I made a minimal code to demonstrate this issue as below.
- instantiating map
var viewOrigin = [-73.8, 40.7];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/jotnajoa/ckpj6g4ho3nvf18pg0r98pjes/draft',
zoom: 12,
center: viewOrigin,
pitch: 60,
antialias: true // create the gl context with MSAA antialiasing, so custom layers are antialiased
});
var modelAltitude = 0;
- Set mapboxgl
function translateCoords(lon, lat) {
let destination = [lon, lat];
let finalCoord = mapboxgl.MercatorCoordinate.fromLngLat(destination, 0);
return finalCoord
}
var modelAsMercatorCoordinate = translateCoords(viewOrigin[0], viewOrigin[1]);
var modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits()
};
- set variables for three.js render outside of mapboxgl because I need to access it outside of mapboxgl lifecycle hook.
const camera = new THREE.Camera();
const scene = new THREE.Scene();
// Setting map, canvas, renderer variable from the outside to access that from outside later
var map;
var canvas;
var renderer;
let particleGroup;
var frameCount = 0;
- Instantiate mapboxgl customlayer
function generateMap() {
var customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd: function(map, gl) {
map = map;
canvas = map.getCanvas()
renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
preserveDrawingBuffer: true
});
renderer.autoClear = false;
},
render: function(gl, matrix) {
frameCount += 0.1;
rotate(frameCount)
var m = new THREE.Matrix4().fromArray(matrix);
var l = new THREE.Matrix4()
.makeTranslation(
modelTransform.translateX,
modelTransform.translateY,
modelTransform.translateZ
)
.scale(
new THREE.Vector3(
modelTransform.scale, -modelTransform.scale,
modelTransform.scale
)
)
camera.projectionMatrix = m.multiply(l);
renderer.resetState();
renderer.render(scene, camera);
map.triggerRepaint();
}
};
map.on('style.load', function() {
map.addLayer(customLayer, 'waterway-label');
});
}
- Draw constantly revolving sphere
function fillingSpheres(group) {
const meshGroup = new THREE.Group();
const { count } = group.geometry.attributes.position
const { array } = group.geometry.attributes.position
for (let i = 0; i < count; i++) {
let i3 = i * 3;
const x = array[i3]
const y = array[i3 + 1]
const z = array[i3 + 2];
const posVec = new THREE.Vector3(x, y, z);
const circleGeo = new THREE.SphereGeometry(10, 10, 10);
const circleMtl = new THREE.MeshBasicMaterial({ color: 'green' });
const circleMesh = new THREE.Mesh(circleGeo, circleMtl);
circleMesh.userData.destination = posExample[i % 10];
circleMesh.position.set(posVec.x, posVec.y, posVec.z)
meshGroup.add(circleMesh)
};
console.log(meshGroup)
return meshGroup;
}
- Move the spheres to the corresponding location on the map (This is where the issue occurs)
First, I instantiated tb object based on the current map setting
function setThreeBox() {
window.tb = new Threebox(
map,
map.getCanvas().getContext('webgl'), {
realSunlight: true,
enableSelectingObjects: true, //change this to false to disable 3D objects selection
enableTooltips: true, // change this to false to disable default tooltips on fill-extrusion and 3D models
}
);
tb.altitudeStep = 1;
}
Second, get the coordinates by projectToWorld and locate them
function moveSphere() {
// instantiate threebox object based on map
setThreeBox()
const { children } = particleGroup
children.forEach((d) => {
const { destination } = d.userData;
const coordVec = tb.projectToWorld([destination.lng, destination.lat])
console.log(coordVec)
d.position.set(coordVec)
})
}
Then, all the coordinates are like x:200000 and it no longer shows the three objects on the scene.
What did I do wrong?
The reason why I based on three.js not solely on three-box is that I can't illustrate the revolving circle on the three.js coordinates space in three-box.
The running code is in the following link.
https://codepen.io/jotnajoa/pen/poeKoOW (for some reason the sample is running after refreshing the code, doesn't start immediately once it is opened)
It would be grateful if someone who knows well about three-box could help me on this. I assume it could be easier to solve this if I could directly access to scene() object in three-box only environment. If it's accessible, I could easily make the frame-based animation in three-box. (Maybe I'm wrong)
Placing is triggered when the button is clicked.