NASA-AMMOS / 3DTilesRendererJS

Renderer for 3D Tiles in Javascript using three.js
https://nasa-ammos.github.io/3DTilesRendererJS/example/bundle/mars.html
Apache License 2.0
1.54k stars 276 forks source link

Add an example demonstrating how to use with MapboxGL #426

Open xiaxiangfeng opened 8 months ago

xiaxiangfeng commented 8 months ago

Describe the bug

After adding 3Dtiles to mapboxgl using 3DTilesRendererJS, the model disappears when the rotated map is greater than 180 degrees

To Reproduce

Steps to reproduce the behavior:

  1. Using mapboxgl to customize layers and add 3D tiles
  2. Use the right mouse button to rotate the map
  3. When the rotation is greater than 180 degrees, the model disappears

Code The address of the code on GitHub

https://github.com/xiaxiangfeng/3DTilesRendererJS.Test/tree/main/docs

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Display a map on a webpage</title>
    <meta
      name="viewport"
      content="initial-scale=1,maximum-scale=1,user-scalable=no"
    />
    <link
      href="https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.css"
      rel="stylesheet"
    />
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.13.0/mapbox-gl.js"></script>
    <script
      async
      src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"
    ></script>
    <style>
      body {
        margin: 0;
        padding: 0;
      }

      #map {
        position: absolute;
        top: 0;
        bottom: 0;
        width: 100%;
      }
    </style>
  </head>

  <body>
    <div id="map"></div>
    <script type="importmap">
      {
        "imports": {
          "three": "https://unpkg.com/three@0.135.0/build/three.module.js",
          "three/examples/jsm/": "https://unpkg.com/three@0.140.0/examples/jsm/"
        }
      }
    </script>

    <script type="module">
      import * as THREE from "three";
      import { DebugTilesRenderer as TilesRenderer } from "./3DTilesRendererJS/index.js";

      window.THREE = THREE;

      var origin = [113.37796671195645, 22.994043025862794];

      mapboxgl.accessToken =
        "pk.eyJ1IjoieGlheGlhbmdmbmVnIiwiYSI6ImNscWYyNjU3azByd3gya3JxOTVrc2NkY3UifQ.362MspMnDi9ZGH-D6P1CtQ";
      const map = new mapboxgl.Map({
        container: "map",
        style: "mapbox://styles/mapbox/dark-v9",
        center: origin,
        zoom: 17.86614600777933,
        pitch: 70,
        bearing: -40,
      });

      const marker1 = new mapboxgl.Marker().setLngLat(origin).addTo(map);

      let centerM;

      function rotationBetweenDirections(dir1, dir2) {
        const rotation = new THREE.Quaternion();
        const a = new THREE.Vector3().crossVectors(dir1, dir2);
        rotation.x = a.x;
        rotation.y = a.y;
        rotation.z = a.z;
        rotation.w = 1 + dir1.clone().dot(dir2);
        rotation.normalize();

        return rotation;
      }

      map.on("load", () => {
        let renderer, scene, camera, world, tiles;
        const mixers = [];

        map.addLayer({
          id: "custom_layer",
          type: "custom",
          renderingMode: "3d",
          onAdd: function (map, mbxContext) {
            const center = map.getCenter();
            this.center = mapboxgl.MercatorCoordinate.fromLngLat(
              [center.lng, center.lat],
              0
            );
            centerM = this.center;

            const { x, y, z } = centerM;

            const scale = centerM.meterInMercatorCoordinateUnits();

            this.cameraTransform = new THREE.Matrix4()
              .makeTranslation(x, y, z)
              .scale(new THREE.Vector3(scale, -scale, scale));

            renderer = new THREE.WebGLRenderer({
              alpha: true,
              antialias: true,
              canvas: map.getCanvas(),
              context: mbxContext,
            });

            renderer.shadowMap.enabled = true;
            renderer.autoClear = false;

            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(
              28,
              window.innerWidth / window.innerHeight,
              0.000000000001,
              Infinity
            );

            const url = "./baowei21/tileset.json";

            tiles = new TilesRenderer(url);

            tiles.onLoadTileSet = (tileset) => {
              const box = new THREE.Box3();
              const sphere = new THREE.Sphere();
              const matrix = new THREE.Matrix4();

              let position;
              let distanceToEllipsoidCenter;

              if (tiles.getOrientedBounds(box, matrix)) {
                position = new THREE.Vector3().setFromMatrixPosition(matrix);
                distanceToEllipsoidCenter = position.length();
              } else if (tiles.getBoundingSphere(sphere)) {
                position = sphere.center.clone();
                distanceToEllipsoidCenter = position.length();
              }

              const surfaceDirection = position.normalize();
              const up = new THREE.Vector3(0, 1, 0);
              const rotationToNorthPole = rotationBetweenDirections(
                surfaceDirection,
                up
              );

              tiles.group.quaternion.x = rotationToNorthPole.x;
              tiles.group.quaternion.y = rotationToNorthPole.y;
              tiles.group.quaternion.z = rotationToNorthPole.z;
              tiles.group.quaternion.w = rotationToNorthPole.w;

              tiles.group.position.y = -distanceToEllipsoidCenter;
            };

            tiles.setCamera(camera);
            tiles.setResolutionFromRenderer(camera, renderer);

            world = new THREE.Group();

            world.add(tiles.group);
            scene.add(world);
          },

          render: function (gl, matrix) {
            const mercatorMatrix = new THREE.Matrix4().fromArray(matrix);
            camera.projectionMatrix = mercatorMatrix.multiply(
              this.cameraTransform
            );

            renderer.resetState();

            tiles.update();
            // Render the scene and repaint the map
            renderer.render(scene, camera);
            map.triggerRepaint();
          },
        });
      });
    </script>
  </body>
</html>

Live example https://xiaxiangfeng.github.io/3DTilesRendererJS.Test/mapbox.three.camera.3dtiles.html

Expected behavior

Display normally during rotation

Screenshots normal image

bug Disappears after rotating 180 degrees image

Platform:

gkjohnson commented 8 months ago

Without a live, editable example it's not possible to debug - but there must be sometihng wrong with your integration with Mapbox since the model works in the live example in this project:

image

That aside your initialized "near" and "far" values are not valid, though it's not clear if this would cause issues.

            camera = new THREE.PerspectiveCamera(
              28,
              window.innerWidth / window.innerHeight,
              0.000000000001,
              Infinity
            );
xiaxiangfeng commented 8 months ago

https://github.com/xiaxiangfeng/3DTilesRendererJS.Test/tree/main/docs

I created this dome on the codepen. I guess it should be the same issue with mapboxgl syncing with threejs, but I don't know how to fix this, hope I can get your help

codepen

https://codepen.io/xiaxiangfeng/pen/KKEKmgq

gkjohnson commented 8 months ago

I've taken a look at the codepen and these settings strike me as odd:

this.cameraTransform = new THREE.Matrix4()
  .makeTranslation(x, y, z)
  .scale(new THREE.Vector3(scale, -scale, scale));
const mercatorMatrix = new THREE.Matrix4().fromArray(matrix);
camera.projectionMatrix = mercatorMatrix.multiply(this.cameraTransform);

I don't know how the render mapbox API works but please make sure the transformations and projection are set correctly on the threejs camera - these are required for the tiles renderer to function.

xiaxiangfeng commented 8 months ago

I have resolved this issue and make a PR. Waiting for merge

xiaxiangfeng commented 8 months ago

I have resolved this issue and make a PR. Waiting for merge

Currently, there are still problems that have not been solved when using mapboxgl. The specific how to use it and the existing problems are in the PR at the address below. I hope someone can complete this PR or provide some help and suggestions in the future.

https://github.com/NASA-AMMOS/3DTilesRendererJS/pull/435