コンテンツにスキップ

3D Tiles with implicit tiling

Loading a 3D Tiles 1.1 tileset with implicit tiling, whose tiles are given by subtree files instead of being listed in tileset.json.

display_3dtiles_implicit.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>3D Tiles with implicit tiling</title>
  <meta property="og:description" content="Loading a 3D Tiles 1.1 tileset with implicit tiling, whose tiles are given by subtree files instead of being listed in tileset.json." />
  <script src="./v0.10.7/mapray.min.js"></script>
  <script src="./v0.10.7/maprayui.min.js"></script>
  <link rel="stylesheet" href="./v0.10.7/mapray.css">
  <style>
    body {margin: 0; padding: 0;}
    html, body, div#mapray-container { height: 100%; }
    .map-overlay {
        font: 12px/20px "Robot", sans-serif;
        position: absolute;
        width: 320px;
        top: 0;
        left: 0;
        padding: 10px;
    }
    .map-overlay .map-overlay-inner {
        background-color: #fff;
        color: #000;
        box-shadow: 0 1px 2px rgba(0,0,0,0.1);
        border-radius: 3px;
        padding: 10px;
        margin-bottom: 10px;
    }
    .map-overlay-inner fieldset {
        border: none;
        padding: 0;
        margin: 0 0 10px;
    }
    .map-overlay-inner fieldset:last-child {
        margin: 0;
    }
    .map-overlay-inner label {
        display: block;
        font-weight: bold;
        margin: 0 0 5px;
    }
    .legend span {
        display: inline-block;
        width: 10px;
        height: 10px;
        margin-right: 5px;
    }
  </style>
</head>
<body>
<div id="mapray-container"></div>
<div class="map-overlay top">
    <div class="map-overlay-inner">
        <fieldset>
            <label>Implicit tiling</label>
            tileset.json gives only the root tile and the rule to divide it. Which tiles exist is read from subtree files as the camera comes closer.
        </fieldset>
        <fieldset>
            <label>Camera</label>
            <button id="far_button" type="button">View from far</button>
            <button id="near_button" type="button">Come closer to the centre</button>
        </fieldset>
        <fieldset>
            <label>Tile level (color of the boxes)</label>
            <div class="legend">
                <span style="background-color: #ebebeb"></span>0
                <span style="background-color: #fad940"></span>1
                <span style="background-color: #fa8c26"></span>2
                <span style="background-color: #e03326"></span>3
                <span style="background-color: #8c33bf"></span>4
            </div>
        </fieldset>
        <fieldset>
            <label>Fetched so far</label>
            <span id="fetch_info">-</span>
        </fieldset>
    </div>
</div>
</body>
</html>
<script>
  // Generated by browser-test/scripts/generate-implicit-3dtiles-example.mjs: a sparse quadtree of 5 levels over Yumenoshima, Tokyo.
  const TILESET_URL = new URL( "../data/3dtiles-implicit/tileset.json", location.href ).href;
  const CENTER = { longitude: 139.8270, latitude: 35.6527 };

  const cloudApi = new mapray.cloud.CloudApiV2({
        tokenType: mapray.cloud.CloudApi.TokenType.ACCESS_TOKEN,
        token: "<YOUR_MAPRAY_ACCESS_TOKEN>"
    });
    // The StandardUIView in the ui package includes mouse-based camera manipulation.
    const uiviewer = new maprayui.StandardUIViewer( "mapray-container", {
        dem_provider: new mapray.StandardDemProvider( cloudApi.getDefaultDemAsResource() ),
        atmosphere: new mapray.Atmosphere()
    } );

  function lookAtCenter( distance, height ) {
      const offset = distance / 111000;
      uiviewer.setCameraPosition( { longitude: CENTER.longitude - offset, latitude: CENTER.latitude - offset, height } );
      uiviewer.setLookAtPosition( { longitude: CENTER.longitude, latitude: CENTER.latitude, height: 0 } );
  }
  lookAtCenter( 2500, 2000 );

  // The scene requests tiles itself; nothing in the application changes for implicit tiling.
  const scene = uiviewer.viewer.b3d_collection.createTiles3DScene( new mapray.StandardTiles3DProvider( { url: TILESET_URL } ) );

  // Count what the browser fetched, to show that only the needed subtrees and tiles are read.
  const fetched = { subtrees: new Set(), contents: new Set(), deepest: -1 };
  function countEntries( entries ) {
      for ( const entry of entries ) {
          const subtree = entry.name.match( /\/subtrees\/(\d+)\.\d+\.\d+\.subtree/ );
          const content = entry.name.match( /\/content\/(\d+)\/\d+\/\d+\.glb/ );
          if ( subtree ) {
              fetched.subtrees.add( entry.name );
          }
          if ( content ) {
              fetched.contents.add( entry.name );
              fetched.deepest = Math.max( fetched.deepest, Number( content[1] ) );
          }
      }
  }
  countEntries( performance.getEntriesByType( "resource" ) );
  new PerformanceObserver( list => countEntries( list.getEntries() ) ).observe( { type: "resource", buffered: true } );

  let tileset_bytes = "-";
  let tile_positions = "-";
  fetch( TILESET_URL ).then( response => response.text() ).then( text => {
      const implicit = JSON.parse( text ).root.implicitTiling;
      tileset_bytes = text.length;
      // A quadtree of n levels has 1 + 4 + ... + 4^(n-1) tile positions.
      tile_positions = ( Math.pow( 4, implicit.availableLevels ) - 1 ) / 3;
  } );

  setInterval( () => {
      document.getElementById( 'fetch_info' ).textContent =
          "tileset.json " + tileset_bytes + " bytes for up to " + tile_positions + " tile positions; " +
          fetched.subtrees.size + " subtree files and " + fetched.contents.size + " tiles fetched, deepest level " +
          ( fetched.deepest < 0 ? "-" : fetched.deepest );
  }, 500 );

  document.getElementById( 'far_button' ).addEventListener( 'click', () => lookAtCenter( 2500, 2000 ) );
  document.getElementById( 'near_button' ).addEventListener( 'click', () => lookAtCenter( 700, 450 ) );
</script>

Info

このサンプルコードは、<YOUR_MAPRAY_ACCESS_TOKEN>を、あなたのMapray CloudのアカウントのOrganization Tokenに置き換えるまで、期待通りに動作しません。