0

I have a Mapbox map that has several animated 3D-arrows that have been added to a custom layer on the map.

However, whenever I zoom out too far on the map, the arrows disappear and the entire map goes white (as if a piece of white paper has been placed between the viewer and the map).

I wasn't able to embed a video to show this behaviour, but here is a link that will show an animation of what is happening: https://i.stack.imgur.com/hw4to.jpg

My assumption is that this has something to do with the gltf model, frustrum, culling, camera. Whenever I change the parameters of the Three.PersepectiveCamera it has no effect on the behaviour.

The following is the code used to implement the custom layer. There are a few other pieces (i.e., onRemove, render) that work alongside this configuration, but I can't see how any of it would be connected.

    // configuration of the custom layer for a 3D model per the CustomLayerInterface
var customLayer = {
    id: layer_id,
    type: 'custom',
    renderingMode: '3d',
    onAdd: function (map, gl) {
        camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 10000);
        // camera = new THREE.Camera();
        scene = new THREE.Scene();

        // create two three.js lights to illuminate the model
        const directionalLight = new THREE.DirectionalLight(0xffffff);
        directionalLight.position.set(0, -70, 100).normalize();
        scene.add(directionalLight);

        const directionalLight2 = new THREE.DirectionalLight(0xffffff);
        directionalLight2.position.set(0, 70, -100).normalize();
        scene.add(directionalLight2);

        // use the three.js GLTF loader to add the 3D model to the three.js scene
        const loader = new THREE.GLTFLoader();
        loader.load(
            arrow_type,
            (gltf) => {
                model = gltf.scene.children[0];
                model.castShadow = true;
                scene.add(model);
                mixer = new THREE.AnimationMixer( model );
                const clip = gltf.animations[0];
                //console.log(gltf);
                mixer.clipAction(clip).play();
                
                delta = clock.getDelta();
                mixer.tick = (delta) => mixer.update(delta);
                animate();
            }
        );

        
        this.map = map;

        // use the Mapbox GL JS map canvas for three.js
        renderer = new THREE.WebGLRenderer({
            canvas: map.getCanvas(),
            context: gl,
            antialias: true
        });

        function animate() {
            delta = clock.getDelta();
            mixer.update(delta);
            //model.tick = (delta) => mixer.update(delta);
            requestAnimationFrame( animate );
            renderer.render( scene, camera );
        }

        renderer.autoClear = false;
    },

    onRemove: function (map, gl) {
        // This function hasn't been tested and may cause problems when removing a 3D object
        this.container.parentNode.removeChild(this.container);
        this.map = undefined;
    },

    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 * length_arr[0],
                    modelTransform.scale * length_arr[1],
                    modelTransform.scale * length_arr[2]
                )
            )
            .multiply(rotationX)
            .multiply(rotationY)
            .multiply(rotationZ);

        camera.projectionMatrix = m.multiply(l);
        renderer.resetState();
        renderer.render(scene, camera);
        map.triggerRepaint();
    }

Any help would be greatly appreciated in solving this problem.

M. Black
  • 353
  • 1
  • 4
  • 20
  • Have you set any minZoom, maxZoom values? If you are going to play hard with 3D and mapbox, I'd recommend you to check out [threebox](https://github.com/jscastro76/threebox) which will solve most of your issues dealing with layers, zooms, I'm maintaining that repo if you have any question – jscastro Jan 05 '23 at 18:35
  • I have not set any minZoom, maxZoom values. Do you mean on the map? Or is there a way to set the zoom values of the custom layer? – M. Black Jan 06 '23 at 18:59
  • On the custom layer. The way the object disappears seems to happen at the same time some layers are hidden due to the zoom. – jscastro Jan 06 '23 at 20:04
  • I feel a bit sheepish because I have no idea how I would set the min/maxZoom values inside the custom layer. I was thinking that perhaps the render property [I have added the onRemove and render properties above] may have something to do with it (camera.projectionMatrix???) but every modification did not seem to make much of a difference. In retrospect, I probably should have started using threebox from the beginning but now that I have so much time invested to get it to its current state, I am reluctant to start everything from scratch. I do appreciate your help jscastro! – M. Black Jan 09 '23 at 16:33

2 Answers2

1

Try adding projection param when creating map.

const map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/streets-v12',
    projection: 'mercator'
});

custom layer only support mercator projection. when you zoom out too far, your map is turning to 3d globe, which doesn't support custom layer.

Bran Zhang
  • 26
  • 3
  • Thanks Bran. This seems to be what the issue was! I have an animated polygon which seems to have disappeared when I changed the projection, but I will dig deeper to see what may be the issue. – M. Black Jan 11 '23 at 19:24
0

Have you tried to increase Camera Far value? https://threejs.org/docs/#api/en/cameras/PerspectiveCamera.far

  • 1
    I have tried several different values for the camera's far parameter and unfortunately none made a difference. – M. Black Jan 11 '23 at 19:10