Skip to content

Commit

Permalink
Merge 3976eac into 3a479d3
Browse files Browse the repository at this point in the history
  • Loading branch information
ibgreen committed Sep 20, 2019
2 parents 3a479d3 + 3976eac commit d539d24
Show file tree
Hide file tree
Showing 23 changed files with 518 additions and 90 deletions.
8 changes: 6 additions & 2 deletions examples/3d-tiles-deck/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import React, {PureComponent} from 'react';
import {render} from 'react-dom';
import {StaticMap} from 'react-map-gl';
import DeckGL from '@deck.gl/react';

import {MapController, FlyToInterpolator} from '@deck.gl/core';
import {Tile3DLayer} from '@deck.gl/geo-layers';
// import {Tile3DLayer} from '@deck.gl/geo-layers';
import Tile3DLayer from './3d-tile-layer/tile-3d-layer';

import {lumaStats} from '@luma.gl/core';
import {StatsWidget} from '@probe.gl/stats-widget';

// To manage dependencies and bundle size, the app must decide which supporting loaders to bring in
import {registerLoaders} from '@loaders.gl/core';
import {DracoWorkerLoader} from '@loaders.gl/draco';
import {GLTFLoader} from '@loaders.gl/gltf';

registerLoaders([DracoWorkerLoader]);
registerLoaders([GLTFLoader, DracoWorkerLoader]);

import ControlPanel from './components/control-panel';
import fileDrop from './components/file-drop';
Expand Down
3 changes: 1 addition & 2 deletions examples/3d-tiles-deck/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
"generate": "node ./generate-index/index.js"
},
"dependencies": {
"@babel/polyfill": "^7.4.4",
"@deck.gl/core": "^7.3.0-beta.1",
"@deck.gl/geo-layers": "^7.3.0-beta.1",
"@deck.gl/layers": "^7.3.0-beta.1",
"@deck.gl/mesh-layers": "^7.3.0-beta.1",
"@deck.gl/react": "^7.3.0-beta.1",
"@loaders.gl/core": "^1.3.0",
"@loaders.gl/draco": "^1.3.0",
"@loaders.gl/polyfills": "^1.3.0",
"@loaders.gl/gltf": "^1.3.0",
"probe.gl": "^3.1.0",
"@probe.gl/stats-widget": "^3.1.0",
"prop-types": "^15.7.2",
Expand Down
94 changes: 94 additions & 0 deletions examples/3d-tiles/3d-tile-layer/get-frame-state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {Vector3} from 'math.gl';
import {CullingVolume, Plane} from '@math.gl/culling';
import {Ellipsoid} from '@math.gl/geospatial';

const scratchPlane = new Plane();
const scratchPosition = new Vector3();
const cullingVolume = new CullingVolume([
new Plane(),
new Plane(),
new Plane(),
new Plane(),
new Plane(),
new Plane()
]);

// Extracts a frame state appropriate for tile culling from a deck.gl viewport
// TODO - this could likely be generalized and merged back into deck.gl for other culling scenarios
export function getFrameState(viewport, frameNumber) {
// Traverse and and request. Update _selectedTiles so that we know what to render.
const {cameraDirection, cameraUp, height} = viewport;
const {metersPerPixel} = viewport.distanceScales;

const viewportCenterCartographic = [viewport.longitude, viewport.latitude, 0];
// TODO - Ellipsoid.eastNorthUpToFixedFrame() breaks on raw array, create a Vector.
// TODO - Ellipsoid.eastNorthUpToFixedFrame() takes a cartesian, is that intuitive?
const viewportCenterCartesian = Ellipsoid.WGS84.cartographicToCartesian(
viewportCenterCartographic,
new Vector3()
);
const enuToFixedTransform = Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);

const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
const cameraPositionCartesian = Ellipsoid.WGS84.cartographicToCartesian(
cameraPositionCartographic,
new Vector3()
);

// These should still be normalized as the transform has scale 1 (goes from meters to meters)
const cameraDirectionCartesian = new Vector3(
enuToFixedTransform.transformAsVector(new Vector3(cameraDirection).scale(metersPerPixel))
).normalize();
const cameraUpCartesian = new Vector3(
enuToFixedTransform.transformAsVector(new Vector3(cameraUp).scale(metersPerPixel))
).normalize();

commonSpacePlanesToWGS84(viewport);

// TODO: make a file/class for frameState and document what needs to be attached to this so that traversal can function
return {
camera: {
position: cameraPositionCartesian,
direction: cameraDirectionCartesian,
up: cameraUpCartesian
},
height,
cullingVolume,
frameNumber, // TODO: This can be the same between updates, what number is unique for between updates?
sseDenominator: 1.15 // Assumes fovy = 60 degrees
};
}

function commonSpacePlanesToWGS84(viewport) {
// Extract frustum planes based on current view.
const viewportCenterCartographic = [viewport.longitude, viewport.latitude, 0];
const viewportCenterCartesian = Ellipsoid.WGS84.cartographicToCartesian(
viewportCenterCartographic,
new Vector3()
);

const frustumPlanes = viewport.getFrustumPlanes();
let i = 0;
for (const dir in frustumPlanes) {
const plane = frustumPlanes[dir];
const distanceToCenter = plane.normal.dot(viewport.center);
scratchPosition
.copy(plane.normal)
.scale(plane.distance - distanceToCenter)
.add(viewport.center);
const cartographicPos = viewport.unprojectPosition(scratchPosition);

const cartesianPos = Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, new Vector3());

scratchPlane.normal
.copy(cartesianPos)
.subtract(viewportCenterCartesian)
.scale(-1) // Want the normal to point into the frustum since that's what culling expects
.normalize();
scratchPlane.distance = Math.abs(scratchPlane.normal.dot(cartesianPos));

cullingVolume.planes[i].normal.copy(scratchPlane.normal);
cullingVolume.planes[i].distance = scratchPlane.distance;
i = i + 1;
}
}
250 changes: 250 additions & 0 deletions examples/3d-tiles/3d-tile-layer/tile-3d-layer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
// NOTE: This is a copy of the deck.gl layer
// Use this for easy debugging
/* global fetch */

import {COORDINATE_SYSTEM, CompositeLayer} from '@deck.gl/core';
import {PointCloudLayer} from '@deck.gl/layers';
import {ScenegraphLayer} from '@deck.gl/mesh-layers';

import {Tileset3D, _getIonTilesetMetadata} from '@loaders.gl/3d-tiles';

import {getFrameState} from './get-frame-state';

const defaultProps = {
getPointColor: [0, 0, 0],
pointSize: 1.0,
opacity: 1.0,

data: null,
_ionAssetId: null,
_ionAccessToken: null,
loadOptions: {throttleRequests: true},

onTilesetLoad: tileset3d => {},
onTileLoad: tileHeader => {},
onTileUnload: tileHeader => {},
onTileLoadFail: (tile, message, url) => {}
};

export default class Tile3DLayer extends CompositeLayer {
initializeState() {
this.state = {
layerMap: {},
tileset3d: null
};
}

shouldUpdateState({changeFlags}) {
return changeFlags.somethingChanged;
}

async updateState({props, oldProps}) {
if (props.data && props.data !== oldProps.data) {
await this._loadTileset(props.data);
} else if (
(props._ionAccessToken || props._ionAssetId) &&
(props._ionAccessToken !== oldProps._ionAccessToken ||
props._ionAssetId !== oldProps._ionAssetId)
) {
await this._loadTilesetFromIon(props._ionAccessToken, props._ionAssetId);
}

const {tileset3d} = this.state;
await this._updateTileset(tileset3d);
}

async _loadTileset(tilesetUrl, fetchOptions, ionMetadata) {
const response = await fetch(tilesetUrl, fetchOptions);
const tilesetJson = await response.json();

const loadOptions = this.getLoadOptions();

const tileset3d = new Tileset3D(tilesetJson, tilesetUrl, {
onTileLoad: tileHeader => {
this.props.onTileLoad(tileHeader);
this._updateTileset(tileset3d);
this.setNeedsUpdate();
},
onTileUnload: this.props.onTileUnload,
onTileLoadFail: this.props.onTileLoadFail,
// TODO: explicit passing should not be needed, registerLoaders should suffice
fetchOptions,
...ionMetadata,
...loadOptions
});

this.setState({
tileset3d,
layerMap: {}
});

if (tileset3d) {
this.props.onTilesetLoad(tileset3d);
}
}

async _loadTilesetFromIon(ionAccessToken, ionAssetId) {
const ionMetadata = await _getIonTilesetMetadata(ionAccessToken, ionAssetId);
const {url, headers} = ionMetadata;
return await this._loadTileset(url, {headers}, ionMetadata);
}

_updateTileset(tileset3d) {
const {timeline, viewport} = this.context;
if (!timeline || !viewport || !tileset3d) {
return;
}

// use Date.now() as frame identifier for now and later used to filter layers for rendering
const frameState = getFrameState(viewport, Date.now());
tileset3d.update(frameState);
this._updateLayerMap(frameState.frameNumber);
}

// `Layer` instances is created and added to the map if it doesn't exist yet.
_updateLayerMap(frameNumber) {
const {tileset3d, layerMap} = this.state;

// create layers for new tiles
const {selectedTiles} = tileset3d;
const tilesWithoutLayer = selectedTiles.filter(tile => !layerMap[tile.fullUri]);

for (const tile of tilesWithoutLayer) {
// TODO - why do we call this here? Being "selected" should automatically add it to cache?
tileset3d.addTileToCache(tile);

layerMap[tile.fullUri] = {
layer: this._create3DTileLayer(tile),
tile
};
}

// update layer visibility
this._selectLayers(frameNumber);
}

// Grab only those layers who were selected this frame.
_selectLayers(frameNumber) {
const {layerMap} = this.state;
const layerMapValues = Object.values(layerMap);

for (const value of layerMapValues) {
const {tile} = value;
let {layer} = value;

if (tile.selectedFrame === frameNumber) {
if (layer && layer.props && !layer.props.visible) {
// Still has GPU resource but visibility is turned off so turn it back on so we can render it.
layer = layer.clone({visible: true});
layerMap[tile.fullUri].layer = layer;
}
} else if (tile.contentUnloaded) {
// Was cleaned up from tileset cache. We no longer need to track it.
delete layerMap[tile.fullUri];
} else if (layer && layer.props && layer.props.visible) {
// Still in tileset cache but doesn't need to render this frame. Keep the GPU resource bound but don't render it.
layer = layer.clone({visible: false});
layerMap[tile.fullUri].layer = layer;
}
}

this.setState({layers: Object.values(layerMap).map(layer => layer.layer)});
}

_create3DTileLayer(tileHeader) {
if (!tileHeader.content) {
return null;
}

switch (tileHeader.content.type) {
case 'pnts':
return this._createPointCloudTileLayer(tileHeader);
case 'i3dm':
case 'b3dm':
return this._create3DModelTileLayer(tileHeader);
default:
throw new Error(`Tile3DLayer: Failed to render layer of type ${tileHeader.content.type}`);
}
}

_create3DModelTileLayer(tileHeader) {
const {gltf} = tileHeader.content;
const {instances, cartographicOrigin, modelMatrix} = tileHeader.content;

const SubLayerClass = this.getSubLayerClass('scenegraph', ScenegraphLayer);

return new SubLayerClass(
{
_lighting: 'pbr'
},
this.getSubLayerProps({
id: 'scenegraph'
}),
{
id: `${this.id}-scenegraph-${tileHeader.fullUri}`,
// Fix for ScenegraphLayer.modelMatrix, under flag in deck 7.3 to avoid breaking existing code
data: instances || [{}],
scenegraph: gltf,

coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS,
coordinateOrigin: cartographicOrigin,
modelMatrix,
_composeModelMatrix: true,
getTransformMatrix: instance => instance.modelMatrix,
getPosition: instance => [0, 0, 0]
}
);
}

_createPointCloudTileLayer(tileHeader) {
const {
attributes,
pointCount,
constantRGBA,
cartographicOrigin,
modelMatrix
} = tileHeader.content;
const {positions, normals, colors} = attributes;

if (!positions) {
return null;
}

const {pointSize, getPointColor} = this.props;
const SubLayerClass = this.getSubLayerClass('pointcloud', PointCloudLayer);

return new SubLayerClass(
{
pointSize
},
this.getSubLayerProps({
id: 'pointcloud'
}),
{
id: `${this.id}-pointcloud-${tileHeader.fullUri}`,
data: {
header: {
vertexCount: pointCount
},
attributes: {
POSITION: positions,
NORMAL: normals,
COLOR_0: colors
}
},
coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS,
coordinateOrigin: cartographicOrigin,
modelMatrix,

getColor: constantRGBA || getPointColor
}
);
}

renderLayers() {
return this.state.layers;
}
}

Tile3DLayer.layerName = 'Tile3DLayer';
Tile3DLayer.defaultProps = defaultProps;
2 changes: 2 additions & 0 deletions examples/gltf-deck/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export default class App extends PureComponent {
parserVersion: 2
}
},
modelMatrix: [1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
_composeModelMatrix: true,
coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS,
coordinateOrigin: MODEL_ORIGIN,
sizeScale: 1000,
Expand Down
Loading

0 comments on commit d539d24

Please sign in to comment.