Skip to content

Commit

Permalink
fix(3dtiles): apply matrixWorld to boundingVolume.box
Browse files Browse the repository at this point in the history
Before this commit we only used the translation part of the transform.
We're now using the full 4x4 matrix to match the wording of the 3dtiles
specification:
"The transform property applies to [...] tile.boundingVolume , except
when tile.boundingVolume.region is defined"
  • Loading branch information
peppsac committed Jul 9, 2018
1 parent bbf0a9b commit e6986d8
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 14 deletions.
27 changes: 15 additions & 12 deletions src/Process/3dTilesProcessing.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,22 +220,25 @@ export function pre3dTilesUpdate(context, layer) {
return [layer.root];
}

const cameraLocalPosition = new THREE.Vector3();
const worldPosition = new THREE.Vector3();
function computeNodeSSE(camera, node) {
const boundingVolumeBox = new THREE.Box3();
const boundingVolumeSphere = new THREE.Sphere();
export function computeNodeSSE(camera, node) {
node.distance = 0;
if (node.boundingVolume.region) {
worldPosition.setFromMatrixPosition(node.boundingVolume.region.matrixWorld);
cameraLocalPosition.copy(camera.camera3D.position).sub(worldPosition);
node.distance = node.boundingVolume.region.box3D.distanceToPoint(cameraLocalPosition);
boundingVolumeBox.copy(node.boundingVolume.region.box3D);
boundingVolumeBox.applyMatrix4(node.boundingVolume.region.matrixWorld);
node.distance = boundingVolumeBox.distanceToPoint(camera.camera3D.position);
} else if (node.boundingVolume.box) {
worldPosition.setFromMatrixPosition(node.matrixWorld);
cameraLocalPosition.copy(camera.camera3D.position).sub(worldPosition);
node.distance = node.boundingVolume.box.distanceToPoint(cameraLocalPosition);
// boundingVolume.box is affected by matrixWorld
boundingVolumeBox.copy(node.boundingVolume.box);
boundingVolumeBox.applyMatrix4(node.matrixWorld);
node.distance = boundingVolumeBox.distanceToPoint(camera.camera3D.position);
} else if (node.boundingVolume.sphere) {
worldPosition.setFromMatrixPosition(node.matrixWorld);
cameraLocalPosition.copy(camera.camera3D.position).sub(worldPosition);
node.distance = Math.max(0.0, node.boundingVolume.sphere.distanceToPoint(cameraLocalPosition));
// boundingVolume.sphere is affected by matrixWorld
boundingVolumeSphere.copy(node.boundingVolume.sphere);
boundingVolumeSphere.applyMatrix4(node.matrixWorld);
node.distance = Math.max(0.0,
boundingVolumeSphere.distanceToPoint(camera.camera3D.position));
} else {
return Infinity;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Provider/3dTilesProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Extent from '../Core/Geographic/Extent';
import { init3dTilesLayer } from '../Process/3dTilesProcessing';
import utf8Decoder from '../utils/Utf8Decoder';

function $3dTilesIndex(tileset, baseURL) {
export function $3dTilesIndex(tileset, baseURL) {
let counter = 0;
this.index = {};
const inverseTileTransform = new THREE.Matrix4();
Expand Down Expand Up @@ -135,7 +135,7 @@ function pntsParse(data) {
return PntsParser.parse(data).then(result => ({ object3d: result.point }));
}

function configureTile(tile, layer, metadata, parent) {
export function configureTile(tile, layer, metadata, parent) {
tile.frustumCulled = false;
tile.layer = layer;

Expand Down
163 changes: 163 additions & 0 deletions test/3dtiles_unit_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/* global describe, it */
import proj4 from 'proj4';
import assert from 'assert';
import { Matrix4, Object3D } from 'three';
import Camera from '../src/Renderer/Camera';
import Coordinates from '../src/Core/Geographic/Coordinates';
import { computeNodeSSE } from '../src/Process/3dTilesProcessing';
import { $3dTilesIndex, configureTile } from '../src/Provider/3dTilesProvider';

function tilesetWithRegion(transformMatrix) {
const tileset = {
root: {
boundingVolume: {
region: [
-0.1, -0.1,
0.1, 0.1,
0, 0],
},
},
};
if (transformMatrix) {
tileset.root.transform = transformMatrix.elements;
}
return tileset;
}

function tilesetWithBox(transformMatrix) {
const tileset = {
root: {
boundingVolume: {
box: [
0, 0, 0,
1, 0, 0,
0, 1, 0,
0, 0, 1],
},
},
};
if (transformMatrix) {
tileset.root.transform = transformMatrix.elements;
}
return tileset;
}

function tilesetWithSphere(transformMatrix) {
const tileset = {
root: {
boundingVolume: {
sphere: [0, 0, 0, 1],
},
},
};
if (transformMatrix) {
tileset.root.transform = transformMatrix.elements;
}
return tileset;
}

describe('Distance computation using boundingVolume.region', function () {
const camera = new Camera('EPSG:4978', 100, 100);
camera.camera3D.position.copy(new Coordinates('EPSG:4326', 0, 0, 10000).as('EPSG:4978').xyz());
camera.camera3D.updateMatrixWorld(true);

it('should compute distance correctly', function () {
const tileset = tilesetWithRegion();
const tileIndex = new $3dTilesIndex(tileset, '');
const tile = new Object3D();
configureTile(tile, { }, tileIndex.index['0']);

computeNodeSSE(camera, tile);

assert.equal(tile.distance, camera.position().as('EPSG:4326').altitude());
});

it('should not be affected by transform', function () {
const m = new Matrix4().makeTranslation(0, 0, 10).multiply(
new Matrix4().makeScale(0.01, 0.01, 0.01));
const tileset = tilesetWithRegion(m);
const tileIndex = new $3dTilesIndex(tileset, '');
const tile = new Object3D();
configureTile(tile, { }, tileIndex.index['0']);

computeNodeSSE(camera, tile);

assert.equal(tile.distance, camera.position().as('EPSG:4326').altitude());
});
});

describe('Distance computation using boundingVolume.box', function () {
proj4.defs('EPSG:3946',
'+proj=lcc +lat_1=45.25 +lat_2=46.75 +lat_0=46 +lon_0=3 +x_0=1700000 +y_0=5200000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');

const camera = new Camera('EPSG:3946', 100, 100);
camera.camera3D.position.copy(new Coordinates('EPSG:3946', 0, 0, 100).xyz());
camera.camera3D.updateMatrixWorld(true);

it('should compute distance correctly', function () {
const tileset = tilesetWithBox();
const tileIndex = new $3dTilesIndex(tileset, '');

const tile = new Object3D();
configureTile(tile, { }, tileIndex.index['0']);

computeNodeSSE(camera, tile);

assert.equal(tile.distance, 100 - 1);
});

it('should affected by transform', function () {
const m = new Matrix4().makeTranslation(0, 0, 10).multiply(
new Matrix4().makeScale(0.01, 0.01, 0.01));
const tileset = tilesetWithBox(m);

const tileIndex = new $3dTilesIndex(tileset, '');

const tile = new Object3D();
configureTile(tile, { }, tileIndex.index['0']);

tile.updateMatrixWorld(true);

computeNodeSSE(camera, tile);

assert.equal(tile.distance, 100 - 1 * 0.01 - 10);
});
});

describe('Distance computation using boundingVolume.sphere', function () {
proj4.defs('EPSG:3946',
'+proj=lcc +lat_1=45.25 +lat_2=46.75 +lat_0=46 +lon_0=3 +x_0=1700000 +y_0=5200000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');

const camera = new Camera('EPSG:3946', 100, 100);
camera.camera3D.position.copy(new Coordinates('EPSG:3946', 0, 0, 100).xyz());
camera.camera3D.updateMatrixWorld(true);

it('should compute distance correctly', function () {
const tileset = tilesetWithSphere();
const tileIndex = new $3dTilesIndex(tileset, '');

const tile = new Object3D();
configureTile(tile, { }, tileIndex.index['0']);

computeNodeSSE(camera, tile);

assert.equal(tile.distance, 100 - 1);
});

it('should affected by transform', function () {
const m = new Matrix4().makeTranslation(0, 0, 10).multiply(
new Matrix4().makeScale(0.01, 0.01, 0.01));
const tileset = tilesetWithSphere(m);

const tileIndex = new $3dTilesIndex(tileset, '');

const tile = new Object3D();
configureTile(tile, { }, tileIndex.index['0']);

tile.updateMatrixWorld(true);

computeNodeSSE(camera, tile);

assert.equal(tile.distance, 100 - 1 * 0.01 - 10);
});
});

0 comments on commit e6986d8

Please sign in to comment.