Closed ApoorvaBhagchandani closed 1 year ago
The map doesn't expose any events for custom WebGLLayers as it is completely unaware of what is rendered and what/how to interact with the scene. The WebGLLayer is basically an image that is updated every time the map moves. That said, you can interact with it but have to do a little extra work. Here is what I did to get this to work:
Here is a simple example that adds support for clicking a model within the layer:
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<!-- Add references to the Azure Maps Map control JavaScript and CSS files. -->
<link href="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.css" rel="stylesheet" />
<script src="https://atlas.microsoft.com/sdk/javascript/mapcontrol/2/atlas.min.js"></script>
<!-- Babylon.js is a powerful, simple, real-time 3D and open game and rendering engine packed into a friendly framework, which Microsoft initially developed. -->
<script src="https://cdn.babylonjs.com/babylon.js"></script>
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
<script>
var map, layer;
var scene;
//A list of the mesh names in the model that we want to limit interaction to when "picking" the scene, as some models will include items we to exclude, such as the background.
var interactiveMeshes = ['dish', 'dish_center', 'quad', 'truss_dish'];
// Create a renderer that implements atlas.WebGLRenderer
var renderer = {
renderingMode: "3d",
// Method called when the layer is added to the map
onAdd: (map, gl) => {
this.map = map;
// Initialize the Babylon.js engine.
const engine = new BABYLON.Engine(gl, true, { useHighPrecisionMatrix: true }, true);
this.scene = new BABYLON.Scene(engine);
this.scene.autoClear = false;
this.scene.detachControl();
this.scene.beforeRender = function () {
engine.wipeCaches(true);
};
//Store a reference to the scene so we can pick from it later.
scene = this.scene;
this.camera = new BABYLON.Camera("camera", new BABYLON.Vector3(), this.scene);
const light = new BABYLON.HemisphericLight("light", BABYLON.Vector3.One(), this.scene);
BABYLON.SceneLoader.Append(
window.location.href.replace(/babylon.html/gi, '')+ "34m_17/",
"34M_17.gltf",
this.scene
)
// parameters to ensure the model is georeferenced correctly on the map
const modelOrigin = [148.9819, -35.39847];
const modelAltitude = 0;
const modelRotate = [Math.PI / 2, 0, 0];
const modelCoords = atlas.data.MercatorPoint.fromPosition([...modelOrigin, modelAltitude]);
const modelScale = 2 * atlas.data.MercatorPoint.meterInMercatorUnits(-35.39847);
this.modelMatrix = BABYLON.Matrix.Compose(
new BABYLON.Vector3(modelScale, modelScale, modelScale),
BABYLON.Quaternion.FromEulerAngles(modelRotate[0], modelRotate[1], modelRotate[2]),
new BABYLON.Vector3(modelCoords[0], modelCoords[1], modelCoords[2])
);
},
// Method called on each animation frame
render: (gl, matrix) => {
// projection & view matrix
const cameraMatrix = BABYLON.Matrix.FromArray(matrix);
const mvpMatrix = this.modelMatrix.multiply(cameraMatrix);
this.camera.freezeProjectionMatrix(mvpMatrix);
this.scene.render(false);
this.map.triggerRepaint();
}
};
function GetMap() {
map = new atlas.Map("map", {
zoom: 18,
pitch: 60,
center: [148.9819, -35.3981],
style: "satellite_road_labels",
antialias: true,
// Add authentication details for connecting to Azure Maps.
authOptions: {
// Use an Azure Maps key. Get an Azure Maps key at https://azuremaps.com/. NOTE: The primary key should be used as the key.
authType: 'subscriptionKey',
subscriptionKey: '<Your Azure Maps Key>'
}
});
// Wait until the map resources are ready
map.events.add("ready", function () {
// Create a WebGL layer
layer = new atlas.layer.WebGLLayer("babylon", { renderer });
// Add the layer to the map
map.layers.add(layer);
//Add click event to the map.
map.events.add('click', mapClicked);
});
}
function mapClicked(e) {
//Get the pixel location that was clicked.
var pixel = e.pixel;
//Alternatively, if you can get a pixel for a geo-position and use that.
//var pixel = map.positionsToPixels([e.position])[0];
//Pick all meshes that intersect in the scene.
var pickResults = scene.multiPick(pixel[0], pixel[1]);
//Loop through and check to see if one of our prefered meshes was picked.
pickResults.forEach(pickResult => {
//Check that the pick has a hit and is one of the meshes we want to interact with.
if (pickResult.hit && interactiveMeshes.indexOf(pickResult.pickedMesh.name) !== -1) {
//A mesh we allow to be interacted with in the model has been clicked!
console.log(`The ${pickResult.pickedMesh.name} mesh of the model has been clicked!`);
}
})
}
</script>
</head>
<body onload="GetMap()">
<div id="map" style="position:relative;width:100%;height:600px;"></div>
</body>
</html>
I have a PR that adds this sample to the project.
I am currently working on rendering a WebGLLayer using BabylonJS in Typescript similar to the sample given. As there is no option or method for interacting with the layer such as hovering over it or clicking on it, I wanted to ask if anyone had any advice for achieving the same and whether it is on the roadmap for future releases.
Thanks!