Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bad position of 3D model when 3D terrain is enabled #1654

Closed
767160 opened this issue Sep 17, 2022 · 15 comments · Fixed by #2279
Closed

bad position of 3D model when 3D terrain is enabled #1654

767160 opened this issue Sep 17, 2022 · 15 comments · Fixed by #2279
Assignees
Labels
💰 bounty M Medium Bounty, USD 250 bug Something isn't working PR is more than welcomed Extra attention is needed terrain

Comments

@767160
Copy link

767160 commented Sep 17, 2022

The position of the 3D model and the map are not synchronized.
The problem appears both in Firefox and Chrome

It is illustrated in the videos below:

bug2.mp4
bug1.mp4
@767160
Copy link
Author

767160 commented Sep 17, 2022

Here is the code I use.
This is a merge of documentation's examples of 3d terrain and THREE objet


<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>3D Terrain</title>
		<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
		<script src="https://unpkg.com/[email protected]/dist/maplibre-gl.js"></script>
		<link href="https://unpkg.com/[email protected]/dist/maplibre-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/[email protected]/build/three.min.js"></script>
		<script src="https://unpkg.com/[email protected]/examples/js/loaders/GLTFLoader.js"></script>
		<div id="map"></div>
		<script>
    var map = (window.map = new maplibregl.Map({
        container: 'map',
        zoom: 18,
        center: [11.39085, 47.27574],
        pitch: 52,
        hash: true,
        style: {
            version: 8,
            sources: {
                osm: {
                    type: 'raster',
                    tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
                    tileSize: 256,
                    attribution: '&copy; OpenStreetMap Contributors',
                    maxzoom: 19
                },
                terrainSource: {
                    type: 'raster-dem',
                    url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
                    tileSize: 256
                },
                hillshadeSource: {
                    type: 'raster-dem',
                    url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json',
                    tileSize: 256
                }
            },
            layers: [
                {
                    id: 'osm',
                    type: 'raster',
                    source: 'osm'
                },
                {
                    id: 'hills',
                    type: 'hillshade',
                    source: 'hillshadeSource',
                    layout: { visibility: 'visible' },
                    paint: { 'hillshade-shadow-color': '#473B24' }
                }
            ],
            terrain: {
                source: 'terrainSource',
                exaggeration: 1
            }
        },
        maxZoom: 18,
        maxPitch: 85,
        antialias: true
    }));

    map.addControl(
        new maplibregl.NavigationControl({
            visualizePitch: true,
            showZoom: true,
            showCompass: true
        })
    );

    map.addControl(
        new maplibregl.TerrainControl({
            source: 'terrainSource',
            exaggeration: 1
        })
    );
    
    
    // parameters to ensure the model is georeferenced correctly on the map
var modelOrigin = [11.39085, 47.27574];
var modelAltitude = 0;
var modelRotate = [Math.PI / 2, 0, 0];
 
var modelAsMercatorCoordinate = maplibregl.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()
};
 
var THREE = window.THREE;
 
// configuration of the custom layer for a 3D model per the CustomLayerInterface
var customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, gl) {
this.camera = new THREE.Camera();
this.scene = new THREE.Scene();
 
// create two three.js lights to illuminate the model
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(0, -70, 100).normalize();
this.scene.add(directionalLight);
 
var directionalLight2 = new THREE.DirectionalLight(0xffffff);
directionalLight2.position.set(0, 70, 100).normalize();
this.scene.add(directionalLight2);
 
// use the three.js GLTF loader to add the 3D model to the three.js scene
var loader = new THREE.GLTFLoader();
loader.load(
'https://maplibre.org/maplibre-gl-js-docs/assets/34M_17/34M_17.gltf',
function (gltf) {
this.scene.add(gltf.scene);
}.bind(this)
);
this.map = map;
 
// use the MapLibre GL JS map canvas for three.js
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true
});
 
this.renderer.autoClear = false;
},
render: function (gl, matrix) {
var rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform.rotateX
);
var rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform.rotateY
);
var rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform.rotateZ
);
 
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
)
)
.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', function () {
map.addLayer(customLayer);
});

		</script>
	</body>
</html>

@767160
Copy link
Author

767160 commented Sep 17, 2022

My company would be happy to hire someone to fix this issue

@767160
Copy link
Author

767160 commented Sep 17, 2022

Maybe the problem is just the altitude

bug3.mp4

@HarelM
Copy link
Collaborator

HarelM commented Sep 17, 2022

We've just merged a removal of the elevationOffset, it might help (although not high chances).
Can you try this example with the latest code base?
If you can find someone who can look into it that it can be great, 3D and buildings etc is not an easy problem :-)

@HarelM
Copy link
Collaborator

HarelM commented Sep 17, 2022

Probably related or duplicate of #1294

@birkskyum
Copy link
Member

It's still an issue after the latest changes.

@prozessor13
Copy link
Collaborator

prozessor13 commented Oct 15, 2022

I think on every redraw call you have to grab the elevation with:

map.transform.getElevation(lngLat, map.terrain)

a refetch is necessary because in different zoomlevels with different terrain-tiles the elvation-values are not the same. Then there exists the

map.transform.elevation

variable with the center-altitdue. In maplibre the proj-matrix is translated by this value in negative z-direction. So this has to somehow merged into the altitude of the 3d-model. May

modelAltitude = map.transform.getElevation(modelOrigin, map.terrain) - map.transform.elevation;

do the trick, but did not tested this, just a guess.

Note: The jump on the moveend event is, because the center-altitude is only updated at moveend. This is to let the camera in the same height during dragging. Then, on moveend, the zoom-level is recalculated, to let the camera on its position.

@HarelM
Copy link
Collaborator

HarelM commented Mar 3, 2023

I've assigned a M bounty.
Link to parent Bounty: maplibre/maplibre#189

@HarelM HarelM added the 💰 bounty M Medium Bounty, USD 250 label Mar 3, 2023
@manhcuongincusar1
Copy link
Contributor

Hi @HarelM, I am Cuong (call me Stefan) at devs pool. Can I assign this issue?

@HarelM
Copy link
Collaborator

HarelM commented Mar 4, 2023

Sure! please keep me posted on your progress.

@manhcuongincusar1
Copy link
Contributor

Sure, thank you

@manhcuongincusar1
Copy link
Contributor

manhcuongincusar1 commented Mar 13, 2023

@HarelM I added queryTerrainElevation into camera.ts. This method allows user to get elevation of a point at specific transform's elevation. Can you check this pull request, thanks

Specical thank for your support @hami9x @prozessor13

@hami9x
Copy link

hami9x commented Mar 13, 2023

This is demo code that shows 3d model working with queryTerrainElevation (included in Stefan's pull request)
https://github.com/maplibre/maplibre-gl-js/blob/7bab10e8dfd1680471126b02b4849423a5f50365/test/debug-pages/terrain-3d-object.html

@HarelM
Copy link
Collaborator

HarelM commented Mar 16, 2023

@manhcuongincusar1 please submit an expense report in open collective in order to get the bounty.
Also please link the report here so that we'll know it was you who filed it.
THANKS!

@manhcuongincusar1
Copy link
Contributor

@HarelM Thank you very much, this is the expense link: https://opencollective.com/maplibre/expenses/128542

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💰 bounty M Medium Bounty, USD 250 bug Something isn't working PR is more than welcomed Extra attention is needed terrain
Projects
None yet
6 participants