Skip to content

Commit

Permalink
fix(GeoidLayer): transformation error on tileMesh.
Browse files Browse the repository at this point in the history
  • Loading branch information
gchoqueux committed Apr 13, 2022
1 parent 1527c64 commit 472e39c
Show file tree
Hide file tree
Showing 16 changed files with 116 additions and 39 deletions.
23 changes: 12 additions & 11 deletions src/Converter/Feature2Mesh.js
Expand Up @@ -22,21 +22,22 @@ const crsWGS84 = 'EPSG:4326';
class FeatureMesh extends THREE.Group {
#currentCrs;
#originalCrs;
#collection = new THREE.Group();
#place = new THREE.Group();
constructor(meshes, collection) {
super();
this.meshesCollection = new THREE.Group().add(...meshes);
this.meshesCollection.quaternion.copy(collection.quaternion);
this.meshesCollection.position.copy(collection.position);
this.meshesCollection.scale.copy(collection.scale);
this.meshesCollection.updateMatrix();
this.meshes = new THREE.Group().add(...meshes);
this.#collection = new THREE.Group().add(this.meshes);
this.#collection.quaternion.copy(collection.quaternion);
this.#collection.position.copy(collection.position);
this.#collection.scale.copy(collection.scale);
this.#collection.updateMatrix();

this.#originalCrs = collection.crs;
this.#currentCrs = this.#originalCrs;
this.extent = collection.extent;
this.place = new THREE.Group();
this.geoid = new THREE.Group();

this.add(this.place.add(this.geoid.add(this.meshesCollection)));
this.add(this.#place.add(this.#collection));
}

as(crs) {
Expand All @@ -52,7 +53,7 @@ class FeatureMesh extends THREE.Group {
// calculate the scale transformation to transform the feature.extent
// to feature.extent.as(crs)
coord.crs = Crs.formatToEPSG(this.#originalCrs);
extent.copy(this.extent).applyMatrix4(this.meshesCollection.matrix);
extent.copy(this.extent).applyMatrix4(this.#collection.matrix);
extent.as(coord.crs, extent);
extent.spatialEuclideanDimensions(dim_ref);
extent.planarDimensions(dim);
Expand All @@ -62,10 +63,10 @@ class FeatureMesh extends THREE.Group {

// Position and orientation
// remove original position
this.place.position.copy(this.meshesCollection.position).negate();
this.#place.position.copy(this.#collection.position).negate();

// get mesh coordinate
coord.setFromVector3(this.meshesCollection.position);
coord.setFromVector3(this.#collection.position);

// get method to calculate orientation
const crsInput = this.#originalCrs == 'EPSG:3857' ? crsWGS84 : this.#originalCrs;
Expand Down
5 changes: 4 additions & 1 deletion src/Converter/convertToTile.js
Expand Up @@ -8,6 +8,7 @@ import TileMesh from 'Core/TileMesh';
import LayeredMaterial from 'Renderer/LayeredMaterial';
import newTileGeometry from 'Core/Prefab/TileBuilder';
import ReferLayerProperties from 'Layer/ReferencingLayerProperties';
import { geoidLayerIsVisible } from 'Layer/GeoidLayer';

const dimensions = new THREE.Vector2();

Expand Down Expand Up @@ -79,8 +80,10 @@ export default {
setTileFromTiledLayer(tile, layer);

if (parent) {
tile.setBBoxZ({ min: parent.obb.z.min, max: parent.obb.z.max });
tile.geoidHeight = parent.geoidHeight;
const geoidHeight = geoidLayerIsVisible(layer) ? tile.geoidHeight : 0;
tile.setBBoxZ({ min: parent.obb.z.min, max: parent.obb.z.max, geoidHeight });
tile.material.geoidHeight = geoidHeight;
}

return tile;
Expand Down
2 changes: 2 additions & 0 deletions src/Core/TileMesh.js
@@ -1,5 +1,6 @@
import * as THREE from 'three';
import CRS from 'Core/Geographic/Crs';
import { geoidLayerIsVisible } from 'Layer/GeoidLayer';

/**
* A TileMesh is a THREE.Mesh with a geometricError and an OBB
Expand Down Expand Up @@ -59,6 +60,7 @@ class TileMesh extends THREE.Mesh {
* @param {number} [elevation.scale]
*/
setBBoxZ(elevation) {
elevation.geoidHeight = geoidLayerIsVisible(this.layer) ? this.geoidHeight : 0;
this.obb.updateZ(elevation);
if (this.horizonCullingPointElevationScaled) {
this.horizonCullingPointElevationScaled.setLength(this.obb.z.delta + this.horizonCullingPoint.length());
Expand Down
15 changes: 9 additions & 6 deletions src/Layer/GeoidLayer.js
Expand Up @@ -2,6 +2,10 @@ import Layer from 'Layer/Layer';
import LayerUpdateState from 'Layer/LayerUpdateState';


export function geoidLayerIsVisible(tilelayer) {
return tilelayer?.attachedLayers.filter(l => l.isGeoidLayer)[0]?.visible;
}

/**
* `GeoidLayer` is a specific `{@link Layer}` which supports geoid height data. When added to a `{@link View}`, it
* vertically translates each of the view's tiles by a proper geoid height value. For a given tile, the geoid height
Expand Down Expand Up @@ -39,10 +43,9 @@ class GeoidLayer extends Layer {
this.defineLayerProperty('visible', true);
}

updateNodeZ(node, parent) {
node.position.z += (this.visible ? 1 : -1) * (node.geoidHeight - parent.geoidHeight);
node.updateMatrix();
node.updateMatrixWorld(true);
updateNodeZ(node) {
node.material.geoidHeight = this.visible ? node.geoidHeight : 0;
node.obb.updateZ({ geoidHeight: node.material.geoidHeight });
}

update(context, layer, node, parent) {
Expand All @@ -60,7 +63,7 @@ class GeoidLayer extends Layer {
if (node.layerUpdateState[layer.id] === undefined) {
node.layerUpdateState[layer.id] = new LayerUpdateState();

const updateNodeZ = () => this.updateNodeZ(node, parent);
const updateNodeZ = () => this.updateNodeZ(node);
layer.addEventListener('visible-property-changed', updateNodeZ);
node.addEventListener('dispose', () => {
layer.removeEventListener('visible-property-changed', updateNodeZ);
Expand All @@ -81,7 +84,7 @@ class GeoidLayer extends Layer {
return this.getData(node.extent, extentsDestination).then(
(result) => {
node.geoidHeight = result.getHeightAtCoordinates(node.extent.center());
this.updateNodeZ(node, parent);
this.updateNodeZ(node);

node.layerUpdateState[layer.id].noMoreUpdatePossible();
},
Expand Down
11 changes: 4 additions & 7 deletions src/Process/FeatureProcessing.js
Expand Up @@ -2,13 +2,10 @@ import LayerUpdateState from 'Layer/LayerUpdateState';
import ObjectRemovalHelper from 'Process/ObjectRemovalHelper';
import handlingError from 'Process/handlerNodeError';
import Coordinates from 'Core/Geographic/Coordinates';
import { geoidLayerIsVisible } from 'Layer/GeoidLayer';

const coord = new Coordinates('EPSG:4326', 0, 0, 0);

function geoidLayerIsVisible(layer) {
return layer.parent?.attachedLayers.filter(l => l.isGeoidLayer)[0]?.visible;
}

export default {
update(context, layer, node) {
if (!node.parent && node.children.length) {
Expand All @@ -27,8 +24,8 @@ export default {
node.link.forEach((f) => {
if (f.layer?.id == layer.id) {
f.layer.object3d.add(f);
f.geoid.position.z = geoidLayerIsVisible(layer) ? node.geoidHeight : 0;
f.geoid.updateMatrixWorld();
f.meshes.position.z = geoidLayerIsVisible(layer.parent) ? node.geoidHeight : 0;
f.meshes.updateMatrixWorld();
}
});
return;
Expand Down Expand Up @@ -65,7 +62,7 @@ export default {
featureMeshes.forEach((featureMesh) => {
if (featureMesh) {
featureMesh.as(context.view.referenceCrs);
featureMesh.geoid.position.z = geoidLayerIsVisible(layer) ? node.geoidHeight : 0;
featureMesh.meshes.position.z = geoidLayerIsVisible(layer.parent) ? node.geoidHeight : 0;
featureMesh.updateMatrixWorld();

if (layer.onMeshCreated) {
Expand Down
2 changes: 2 additions & 0 deletions src/Renderer/LayeredMaterial.js
Expand Up @@ -145,6 +145,8 @@ class LayeredMaterial extends THREE.RawShaderMaterial {
CommonMaterial.setUniformProperty(this, 'overlayColor', new THREE.Color(1.0, 0.3, 0.0));
CommonMaterial.setUniformProperty(this, 'objectId', 0);

CommonMaterial.setUniformProperty(this, 'geoidHeight', 0.0);

// > 0 produces gaps,
// < 0 causes oversampling of textures
// = 0 causes sampling artefacts due to bad estimation of texture-uv gradients
Expand Down
7 changes: 5 additions & 2 deletions src/Renderer/OBB.js
Expand Up @@ -67,6 +67,7 @@ class OBB extends THREE.Object3D {
* @param {number} [elevation.min] The minimum of oriented bounding box
* @param {number} [elevation.max] The maximum of oriented bounding box
* @param {number} [elevation.scale] The scale of oriented bounding box Z axis
* @param {number} [elevation.geoidHeight] The geoid height added to ellipsoid.
*/
updateZ(elevation = {}) {
this.z.min = elevation.min ?? this.z.min;
Expand All @@ -75,8 +76,10 @@ class OBB extends THREE.Object3D {
this.z.scale = elevation.scale > 0 ? elevation.scale : this.z.scale;
this.z.delta = Math.abs(this.z.max - this.z.min) * this.z.scale;

this.box3D.min.z = this.natBox.min.z + this.z.min * this.z.scale;
this.box3D.max.z = this.natBox.max.z + this.z.max * this.z.scale;
const geoidHeight = elevation.geoidHeight || 0;

this.box3D.min.z = this.natBox.min.z + this.z.min * this.z.scale + geoidHeight;
this.box3D.max.z = this.natBox.max.z + this.z.max * this.z.scale + geoidHeight;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/Renderer/Shader/Chunk/elevation_pars_vertex.glsl
Expand Up @@ -11,6 +11,7 @@
uniform sampler2D elevationTextures[NUM_VS_TEXTURES];
uniform vec4 elevationOffsetScales[NUM_VS_TEXTURES];
uniform int elevationTextureCount;
uniform float geoidHeight;

highp float decode32(highp vec4 rgba) {
highp float Sign = 1.0 - step(128.0,rgba[0])*2.0;
Expand Down
1 change: 1 addition & 0 deletions src/Renderer/Shader/Chunk/geoid_vertex.glsl
@@ -0,0 +1 @@
transformed += geoidHeight * normal;
2 changes: 2 additions & 0 deletions src/Renderer/Shader/ShaderChunk.js
Expand Up @@ -2,6 +2,7 @@ import * as THREE from 'three';
import color_layers_pars_fragment from './Chunk/color_layers_pars_fragment.glsl';
import elevation_pars_vertex from './Chunk/elevation_pars_vertex.glsl';
import elevation_vertex from './Chunk/elevation_vertex.glsl';
import geoid_vertex from './Chunk/geoid_vertex.glsl';
import fog_fragment from './Chunk/fog_fragment.glsl';
import fog_pars_fragment from './Chunk/fog_pars_fragment.glsl';
import lighting_fragment from './Chunk/lighting_fragment.glsl';
Expand Down Expand Up @@ -29,6 +30,7 @@ const itownsShaderChunk = {
custom_header_colorLayer,
elevation_pars_vertex,
elevation_vertex,
geoid_vertex,
fog_fragment,
fog_pars_fragment,
lighting_fragment,
Expand Down
1 change: 1 addition & 0 deletions src/Renderer/Shader/TileVS.glsl
Expand Up @@ -24,6 +24,7 @@ void main() {

#include <begin_vertex>
#include <itowns/elevation_vertex>
#include <itowns/geoid_vertex>
#include <project_vertex>
#include <logdepthbuf_vertex>
vHighPrecisionZW = gl_Position.zw;
Expand Down
10 changes: 5 additions & 5 deletions test/unit/dataSourceProvider.js
Expand Up @@ -226,7 +226,7 @@ describe('Provide in Sources', function () {

featureLayer.update(context, featureLayer, tile);
DataSourceProvider.executeCommand(context.scheduler.commands[0]).then((features) => {
assert.equal(features[0].meshesCollection.children.length, 4);
assert.equal(features[0].meshes.children.length, 4);
done();
});
});
Expand All @@ -247,10 +247,10 @@ describe('Provide in Sources', function () {
featureLayer.source.onLayerAdded({ out: featureLayer });
featureLayer.update(context, featureLayer, tile);
DataSourceProvider.executeCommand(context.scheduler.commands[0]).then((features) => {
assert.ok(features[0].meshesCollection.children[0].isMesh);
assert.ok(features[0].meshesCollection.children[1].isPoints);
assert.equal(features[0].meshesCollection.children[0].children.length, 0);
assert.equal(features[0].meshesCollection.children[1].children.length, 0);
assert.ok(features[0].meshes.children[0].isMesh);
assert.ok(features[0].meshes.children[1].isPoints);
assert.equal(features[0].meshes.children[0].children.length, 0);
assert.equal(features[0].meshes.children[1].children.length, 0);
assert.equal(featureCountByCb, 2);
done();
});
Expand Down
6 changes: 3 additions & 3 deletions test/unit/feature2mesh.js
Expand Up @@ -32,7 +32,7 @@ describe('Feature2Mesh', function () {

it('rect mesh area should match geometry extent', () =>
parsed.then((collection) => {
const mesh = Feature2Mesh.convert()(collection).meshesCollection;
const mesh = Feature2Mesh.convert()(collection).meshes;
const extentSize = collection.extent.planarDimensions();

assert.equal(
Expand All @@ -42,7 +42,7 @@ describe('Feature2Mesh', function () {

it('square mesh area should match geometry extent minus holes', () =>
parsed.then((collection) => {
const mesh = Feature2Mesh.convert()(collection).meshesCollection;
const mesh = Feature2Mesh.convert()(collection).meshes;

const noHoleArea = computeAreaOfMesh(mesh.children[0]);
const holeArea = computeAreaOfMesh(mesh.children[1]);
Expand All @@ -55,7 +55,7 @@ describe('Feature2Mesh', function () {

it('convert points, lines and mesh', () =>
parsed2.then((collection) => {
const mesh = Feature2Mesh.convert()(collection).meshesCollection;
const mesh = Feature2Mesh.convert()(collection).meshes;
assert.equal(mesh.children[0].type, 'Points');
assert.equal(mesh.children[1].type, 'Line');
assert.equal(mesh.children[2].type, 'Mesh');
Expand Down
4 changes: 2 additions & 2 deletions test/unit/featuregeometrylayer.js
Expand Up @@ -107,8 +107,8 @@ describe('Layer with Feature process', function () {

it('parsing error without proj4 should be inferior to 1e-5 meter', function (done) {
Promise.all([ariegeNoProj4.whenReady, ariege.whenReady]).then(() => {
const meshNoProj4 = ariegeNoProj4.object3d.children[0].meshesCollection.children[0];
const mesh = ariege.object3d.children[0].meshesCollection.children[0];
const meshNoProj4 = ariegeNoProj4.object3d.children[0].meshes.children[0];
const mesh = ariege.object3d.children[0].meshes.children[0];
const array = mesh.geometry.attributes.position.array;
const arrayNoProj4 = meshNoProj4.geometry.attributes.position.array;
const vMeshNoProj4 = new THREE.Vector3();
Expand Down
4 changes: 2 additions & 2 deletions test/unit/featuregeometrylayererror.js
Expand Up @@ -89,8 +89,8 @@ files.forEach((geojson, i) => {

it(`parsing error without proj4 should be inferior to ${max_error} meter`, function (done) {
Promise.all([layerNoProj4.whenReady, layerProj4.whenReady]).then(() => {
const meshNoProj4 = layerNoProj4.object3d.children[0].meshesCollection.children[0];
const mesh = layerProj4.object3d.children[0].meshesCollection.children[0];
const meshNoProj4 = layerNoProj4.object3d.children[0].meshes.children[0];
const mesh = layerProj4.object3d.children[0].meshes.children[0];
const array = mesh.geometry.attributes.position.array;
const arrayNoProj4 = meshNoProj4.geometry.attributes.position.array;
const vectorNoProj4 = new THREE.Vector3();
Expand Down
61 changes: 61 additions & 0 deletions test/unit/geoidlayer.js
@@ -0,0 +1,61 @@
import * as THREE from 'three';
import assert from 'assert';
import GeoidLayer from 'Layer/GeoidLayer';
import FileSource from 'Source/FileSource';
import Coordinates from 'Core/Geographic/Coordinates';
import GlobeView from 'Core/Prefab/GlobeView';
import HttpsProxyAgent from 'https-proxy-agent';
import Extent from 'Core/Geographic/Extent';
import OBB from 'Renderer/OBB';
import TileMesh from 'Core/TileMesh';
import Renderer from './bootstrap';

describe('GlobeView', function () {
const renderer = new Renderer();
const placement = { coord: new Coordinates('EPSG:4326', 4.631512, 43.675626), range: 3919 };
const view = new GlobeView(renderer.domElement, placement, { renderer });
const url = 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/altitude-conversion-grids/RAF20_float.gtx';


const geoidSource = new FileSource({
url,
crs: 'EPSG:4326',
format: 'application/gtx',
networkOptions: process.env.HTTPS_PROXY ? { agent: new HttpsProxyAgent(process.env.HTTPS_PROXY) } : {},
});
// Specify the type geoid height data are encoded with. See GTXParser documentation at
// http://www.itowns-project.org/itowns/docs/#api/Parser/GTXParser for more.
geoidSource.dataType = 'float';

// Create a Layer to support geoid height data and add it to the view.
const geoidLayer = new GeoidLayer('geoid', {
source: geoidSource,
});

const context = {};

const extent = new Extent('EPSG:4326', 4.1, 4.3, 48.1, 48.3);
const geom = new THREE.BufferGeometry();
geom.OBB = new OBB(new THREE.Vector3(), new THREE.Vector3(1, 1, 1));
const tile = new TileMesh(geom, new THREE.Material(), view.tileLayer, extent, 9);
tile.parent = {};

it('add geoid layer', function (done) {
view.addLayer(geoidLayer).then(() => {
done();
});
});

it('update geoid layer', function (done) {
geoidLayer.whenReady.then(() => {
// console.log('geoidLayer');
// console.log('geoidLayer', );
// geoidLayer.update();
geoidLayer.update(context, geoidLayer, tile, {}).then(() => {
assert.equal(tile.geoidHeight, 45.72800064087844);
done();
// console.log('tile', tile.geoidHeight);
});
});
});
});

0 comments on commit 472e39c

Please sign in to comment.