diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 835d010b51..48747ddf04 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -45,6 +45,10 @@ The following people have contributed to iTowns. * [Diginove](http://diginove.com/index.php/fr/diginove-lexpertise-en-traitement-dimages/): * [Michel Benet](https://github.com/mbenevole) +* [Sogelink](https://www.sogelink.com/) + * [Kévin ETOURNEAU](https://github.com/ketourneau) + * [Alexis DELFORGES](https://github.com/pourfex) + The following organizations are the current maintainers of iTowns: * IGN (http://www.ign.fr) * Ciril Group (https://www.cirilgroup.com/) diff --git a/src/Layer/C3DTilesLayer.js b/src/Layer/C3DTilesLayer.js index 1d02d0f736..28d93578a6 100644 --- a/src/Layer/C3DTilesLayer.js +++ b/src/Layer/C3DTilesLayer.js @@ -3,7 +3,7 @@ import GeometryLayer from 'Layer/GeometryLayer'; import { init3dTilesLayer, pre3dTilesUpdate, process3dTilesNode } from 'Process/3dTilesProcessing'; import C3DTileset from 'Core/3DTiles/C3DTileset'; import C3DTExtensions from 'Core/3DTiles/C3DTExtensions'; -import { PNTS_MODE } from 'Renderer/PointsMaterial'; +import { PNTS_MODE, PNTS_SIZE_MODE } from 'Renderer/PointsMaterial'; // eslint-disable-next-line no-unused-vars import Style from 'Core/Style'; import C3DTFeature from 'Core/3DTiles/C3DTFeature'; @@ -70,6 +70,9 @@ class C3DTilesLayer extends GeometryLayer { * removed from the scene. * @param {C3DTExtensions} [config.registeredExtensions] 3D Tiles extensions managers registered for this tileset. * @param {String} [config.pntsMode= PNTS_MODE.COLOR] {@link PointsMaterials} Point cloud coloring mode. Only 'COLOR' or 'CLASSIFICATION' are possible. COLOR uses RGB colors of the points, CLASSIFICATION uses a classification property of the batch table to color points. + * @param {String} [config.pntsSizeMode= PNTS_SIZE_MODE.VALUE] {@link PointsMaterials} Point cloud size mode. Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance from point to camera. + * @param {Number} [config.pntsMinAttenuatedSize=3] Minimum scale used by 'ATTENUATED' size mode + * @param {Number} [config.pntsMaxAttenuatedSize=10] Maximum scale used by 'ATTENUATED' size mode * @param {Style} [config.style=null] - style used for this layer * @param {View} view The view */ @@ -84,13 +87,19 @@ class C3DTilesLayer extends GeometryLayer { this.pntsMode = PNTS_MODE.COLOR; this.classification = config.classification; - + this.pntsSizeMode = PNTS_SIZE_MODE.VALUE; + this.pntsMinAttenuatedSize = config.pntsMinAttenuatedSize || 3; + this.pntsMaxAttenuatedSize = config.pntsMaxAttenuatedSize || 10; if (config.pntsMode) { const exists = Object.values(PNTS_MODE).includes(config.pntsMode); if (!exists) { console.warn("The points cloud mode doesn't exist. Use 'COLOR' or 'CLASSIFICATION' instead."); } else { this.pntsMode = config.pntsMode; } } + if (config.pntsSizeMode) { + const exists = Object.values(PNTS_SIZE_MODE).includes(config.pntsSizeMode); + if (!exists) { console.warn("The points cloud size mode doesn't exist. Use 'VALUE' or 'ATTENUATED' instead."); } else { this.pntsSizeMode = config.pntsSizeMode; } + } /** @type {Style} */ this._style = config.style || null; diff --git a/src/Layer/PointCloudLayer.js b/src/Layer/PointCloudLayer.js index 9b15c71f3b..00dfcd563f 100644 --- a/src/Layer/PointCloudLayer.js +++ b/src/Layer/PointCloudLayer.js @@ -160,6 +160,7 @@ class PointCloudLayer extends GeometryLayer { this.material.opacity = this.opacity; this.material.transparent = this.opacity < 1; this.material.size = this.pointSize; + this.material.preSSE = context.camera.preSSE; if (this.material.updateUniforms) { this.material.updateUniforms(); } diff --git a/src/Layer/ReferencingLayerProperties.js b/src/Layer/ReferencingLayerProperties.js index aa7b73bbdd..a750b80e82 100644 --- a/src/Layer/ReferencingLayerProperties.js +++ b/src/Layer/ReferencingLayerProperties.js @@ -21,6 +21,21 @@ function ReferLayerProperties(material, layer) { get: () => material.layer.pntsMode, }); } + if (material.uniforms && material.uniforms.sizeMode != undefined) { + Object.defineProperty(material.uniforms.sizeMode, 'value', { + get: () => material.layer.pntsSizeMode, + }); + } + if (material.uniforms && material.uniforms.minAttenuatedSize != undefined) { + Object.defineProperty(material.uniforms.minAttenuatedSize, 'value', { + get: () => material.layer.pntsMinAttenuatedSize, + }); + } + if (material.uniforms && material.uniforms.maxAttenuatedSize != undefined) { + Object.defineProperty(material.uniforms.maxAttenuatedSize, 'value', { + get: () => material.layer.pntsMaxAttenuatedSize, + }); + } Object.defineProperty(material, 'wireframe', { get: () => material.layer.wireframe, diff --git a/src/Main.js b/src/Main.js index e54a21194f..ba0fa8e66e 100644 --- a/src/Main.js +++ b/src/Main.js @@ -19,7 +19,7 @@ export { VIEW_EVENTS } from 'Core/View'; export { default as FeatureProcessing } from 'Process/FeatureProcessing'; export { updateLayeredMaterialNodeImagery, updateLayeredMaterialNodeElevation } from 'Process/LayeredMaterialNodeProcessing'; export { default as OrientedImageCamera } from 'Renderer/OrientedImageCamera'; -export { default as PointsMaterial, PNTS_MODE, ClassificationScheme } from 'Renderer/PointsMaterial'; +export { default as PointsMaterial, PNTS_MODE, PNTS_SIZE_MODE, ClassificationScheme } from 'Renderer/PointsMaterial'; export { default as GlobeControls } from 'Controls/GlobeControls'; export { default as FlyControls } from 'Controls/FlyControls'; export { default as FirstPersonControls } from 'Controls/FirstPersonControls'; diff --git a/src/Provider/3dTilesProvider.js b/src/Provider/3dTilesProvider.js index 92bdc271bb..d6cc6f64fb 100644 --- a/src/Provider/3dTilesProvider.js +++ b/src/Provider/3dTilesProvider.js @@ -29,7 +29,14 @@ function pntsParse(data, layer) { return PntsParser.parse(data, layer.registeredExtensions).then((result) => { const material = layer.material ? layer.material.clone() : - new PointsMaterial({ size: 0.05, mode: layer.pntsMode, classification: layer.classification }); + new PointsMaterial({ + size: 0.05, + mode: layer.pntsMode, + classification: layer.classification, + sizeMode: layer.pntsSizeMode, + minAttenuatedSize: layer.pntsMinAttenuatedSize, + maxAttenuatedSize: layer.pntsMaxAttenuatedSize, + }); // refer material properties in the layer so when layers opacity and visibility is updated, the material is // automatically updated diff --git a/src/Renderer/PointsMaterial.js b/src/Renderer/PointsMaterial.js index 2d9e5f5866..3f824f643d 100644 --- a/src/Renderer/PointsMaterial.js +++ b/src/Renderer/PointsMaterial.js @@ -12,6 +12,11 @@ export const PNTS_MODE = { NORMAL: 3, }; +export const PNTS_SIZE_MODE = { + VALUE: 0, + ATTENUATED: 1, +}; + const white = new THREE.Color(1.0, 1.0, 1.0); /** @@ -59,6 +64,9 @@ class PointsMaterial extends THREE.RawShaderMaterial { * @param {THREE.Vector2} [options.intensityRange=new THREE.Vector2(0, 1)] intensity range. * @param {boolean} [options.applyOpacityClassication=false] apply opacity classification on all display mode. * @param {Classification} [options.classification] - define points classification. + * @param {number} [options.sizeMode=PNTS_SIZE_MODE.VALUE] point cloud size mode. Only 'VALUE' or 'ATTENUATED' are possible. VALUE use constant size, ATTENUATED compute size depending on distance from point to camera. + * @param {number} [options.minAttenuatedSize=3] minimum scale used by 'ATTENUATED' size mode + * @param {number} [options.maxAttenuatedSize=10] maximum scale used by 'ATTENUATED' size mode * @property {Classification} classification - points classification. * * @example @@ -74,6 +82,9 @@ class PointsMaterial extends THREE.RawShaderMaterial { const applyOpacityClassication = options.applyOpacityClassication == undefined ? false : options.applyOpacityClassication; const size = options.size || 0; const mode = options.mode || PNTS_MODE.COLOR; + const sizeMode = size === 0 ? PNTS_SIZE_MODE.ATTENUATED : (options.sizeMode || PNTS_SIZE_MODE.VALUE); + const minAttenuatedSize = options.minAttenuatedSize || 3; + const maxAttenuatedSize = options.maxAttenuatedSize || 10; delete options.orientedImageMaterial; delete options.intensityRange; @@ -81,6 +92,9 @@ class PointsMaterial extends THREE.RawShaderMaterial { delete options.applyOpacityClassication; delete options.size; delete options.mode; + delete options.sizeMode; + delete options.minAttenuatedSize; + delete options.maxAttenuatedSize; super(options); @@ -89,6 +103,7 @@ class PointsMaterial extends THREE.RawShaderMaterial { this.scale = options.scale || 0.05 * 0.5 / Math.tan(1.0 / 2.0); // autosizing scale CommonMaterial.setDefineMapping(this, 'PNTS_MODE', PNTS_MODE); + CommonMaterial.setDefineMapping(this, 'PNTS_SIZE_MODE', PNTS_SIZE_MODE); CommonMaterial.setUniformProperty(this, 'size', size); CommonMaterial.setUniformProperty(this, 'mode', mode); @@ -97,6 +112,10 @@ class PointsMaterial extends THREE.RawShaderMaterial { CommonMaterial.setUniformProperty(this, 'overlayColor', options.overlayColor || new THREE.Vector4(0, 0, 0, 0)); CommonMaterial.setUniformProperty(this, 'intensityRange', intensityRange); CommonMaterial.setUniformProperty(this, 'applyOpacityClassication', applyOpacityClassication); + CommonMaterial.setUniformProperty(this, 'sizeMode', sizeMode); + CommonMaterial.setUniformProperty(this, 'preSSE', 1.0); + CommonMaterial.setUniformProperty(this, 'minAttenuatedSize', minAttenuatedSize); + CommonMaterial.setUniformProperty(this, 'maxAttenuatedSize', maxAttenuatedSize); // add classification texture to apply classification lut. const data = new Uint8Array(256 * 4); @@ -214,6 +233,9 @@ class PointsMaterial extends THREE.RawShaderMaterial { this.transparent = source.transparent; this.size = source.size; this.mode = source.mode; + this.sizeMode = source.sizeMode; + this.minAttenuatedSize = source.minAttenuatedSize; + this.maxAttenuatedSize = source.maxAttenuatedSize; this.picking = source.picking; this.scale = source.scale; this.overlayColor.copy(source.overlayColor); diff --git a/src/Renderer/Shader/PointsVS.glsl b/src/Renderer/Shader/PointsVS.glsl index c36b57ee1d..019541fe6a 100644 --- a/src/Renderer/Shader/PointsVS.glsl +++ b/src/Renderer/Shader/PointsVS.glsl @@ -8,6 +8,7 @@ #include uniform float size; +uniform float preSSE; uniform bool picking; uniform int mode; @@ -20,6 +21,9 @@ attribute vec4 unique_id; attribute float intensity; attribute float classification; uniform sampler2D classificationLUT; +uniform int sizeMode; +uniform float minAttenuatedSize; +uniform float maxAttenuatedSize; #if defined(NORMAL_OCT16) attribute vec2 oct16Normal; @@ -90,7 +94,7 @@ void main() { vColor.rgb = vec3(i, i, i); } else if (mode == PNTS_MODE_NORMAL) { vColor.rgb = abs(normal); - } else if (mode ==PNTS_MODE_COLOR) { + } else if (mode == PNTS_MODE_COLOR) { // default to color mode vColor.rgb = mix(color, overlayColor.rgb, overlayColor.a); } @@ -99,10 +103,12 @@ void main() { #include #include - if (size > 0.) { + if (sizeMode == PNTS_SIZE_MODE_VALUE) { gl_PointSize = size; - } else { - gl_PointSize = clamp(-size / gl_Position.w, 3.0, 10.0); + } else if (sizeMode == PNTS_SIZE_MODE_ATTENUATED) { + gl_PointSize = size; + gl_PointSize *= (preSSE / -mvPosition.z); + gl_PointSize = clamp(gl_PointSize, minAttenuatedSize, maxAttenuatedSize); } #if defined(USE_TEXTURES_PROJECTIVE) diff --git a/utils/debug/3dTilesDebug.js b/utils/debug/3dTilesDebug.js index d9a13a57f7..d1db7cdc66 100644 --- a/utils/debug/3dTilesDebug.js +++ b/utils/debug/3dTilesDebug.js @@ -1,6 +1,7 @@ import * as THREE from 'three'; import View from 'Core/View'; import GeometryLayer from 'Layer/GeometryLayer'; +import { PNTS_SIZE_MODE } from 'Renderer/PointsMaterial'; import GeometryDebug from './GeometryDebug'; import OBBHelper from './OBBHelper'; @@ -112,4 +113,16 @@ export default function create3dTilesDebugUI(datDebugTool, view, _3dTileslayer) gui.add(_3dTileslayer, 'sseThreshold', 0, 100).name('sseThreshold').onChange(() => { view.notifyChange(view.camera.camera3D); }); + + gui.add(_3dTileslayer, 'pntsSizeMode', PNTS_SIZE_MODE).name('Pnts size mode').onChange(() => { + view.notifyChange(view.camera.camera3D); + }); + + gui.add(_3dTileslayer, 'pntsMinAttenuatedSize', 0, 15).name('Min attenuated size').onChange(() => { + view.notifyChange(view.camera.camera3D); + }); + + gui.add(_3dTileslayer, 'pntsMaxAttenuatedSize', 0, 15).name('Max attenuated size').onChange(() => { + view.notifyChange(view.camera.camera3D); + }); } diff --git a/utils/debug/PotreeDebug.js b/utils/debug/PotreeDebug.js index 40ecdac176..43f5ad5f2c 100644 --- a/utils/debug/PotreeDebug.js +++ b/utils/debug/PotreeDebug.js @@ -1,4 +1,4 @@ -import { PNTS_MODE } from 'Renderer/PointsMaterial'; +import { PNTS_MODE, PNTS_SIZE_MODE } from 'Renderer/PointsMaterial'; export default { initTools(view, layer, datUi) { @@ -26,6 +26,13 @@ export default { } styleUI.add(layer, 'opacity', 0, 1).name('Layer Opacity').onChange(update); styleUI.add(layer, 'pointSize', 0, 15).name('Point Size').onChange(update); + if (layer.material.sizeMode != undefined) { + styleUI.add(layer.material, 'sizeMode', PNTS_SIZE_MODE).name('Point size mode').onChange(() => { + update(); + }); + } + styleUI.add(layer.material, 'minAttenuatedSize', 0, 15).name('Min attenuated size').onChange(update); + styleUI.add(layer.material, 'maxAttenuatedSize', 0, 15).name('Max attenuated size').onChange(update); if (layer.material.picking != undefined) { styleUI.add(layer.material, 'picking').name('Display picking id').onChange(update); }