itowns 2.43.2-next.3 → 2.43.2-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -114,7 +114,7 @@
114
114
  var menu = new GuiTools('menuDiv', view);
115
115
  var d = new debug.Debug(view, menu.gui);
116
116
  debug.createTileDebugUI(menu.gui, view, view.tileLayer, d);
117
- debug.create3dTilesDebugUI(menu.gui, view, $3dTilesLayer, d);
117
+ debug.create3dTilesDebugUI(menu.gui, view, $3dTilesLayer);
118
118
  </script>
119
119
  </body>
120
120
  </html>
@@ -81,8 +81,8 @@
81
81
  // Add the UI Debug
82
82
  var d = new debug.Debug(view, menuGlobe.gui);
83
83
  debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer, d);
84
- debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerDiscreteLOD, d);
85
- debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerRequestVolume, d);
84
+ debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerDiscreteLOD);
85
+ debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerRequestVolume);
86
86
  d.zoom = function() {
87
87
  view.camera3D.position.set(1215013.9, -4736315.5, 4081597.5);
88
88
  view.camera3D.quaternion.set(0.9108514448729665, 0.13456816437801225, 0.1107206134840362, 0.3741416847378546);
@@ -80,9 +80,7 @@
80
80
  // Add a debug UI
81
81
  var d = new debug.Debug(view, menuGlobe.gui);
82
82
  debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer, d);
83
- debug.create3dTilesDebugUI(menuGlobe.gui, view,
84
- $3dTilesLayerBTHierarchy,
85
- d);
83
+ debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerBTHierarchy);
86
84
  </script>
87
85
  </body>
88
86
  </html>
@@ -58,14 +58,15 @@
58
58
  }
59
59
  // Create a new Layer 3d-tiles For Pointcloud
60
60
  // -------------------------------------------
61
+ var $3dTilesSource = new itowns.C3DTilesSource({
62
+ url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/pnts-sete-2021-0756_6256/tileset.json',
63
+ });
61
64
  var $3dTilesLayerSetePC = new itowns.C3DTilesLayer('3d-tiles-sete', {
62
65
  name: 'SetePC',
63
66
  sseThreshold: 5,
64
67
  pntsMode: itowns.PNTS_MODE.CLASSIFICATION,
65
68
  pntsShape : itowns.PNTS_SHAPE.CIRCLE,
66
- source: new itowns.C3DTilesSource({
67
- url: 'https://raw.githubusercontent.com/iTowns/iTowns2-sample-data/master/pointclouds/pnts-sete-2021-0756_6256/tileset.json',
68
- }),
69
+ source: $3dTilesSource,
69
70
  }, view);
70
71
 
71
72
  $3dTilesLayerSetePC.addEventListener(
@@ -84,18 +85,11 @@
84
85
 
85
86
  // Add the UI Debug
86
87
  var d = new debug.Debug(view, menuGlobe.gui);
87
- debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer, d);
88
- debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerSetePC, d);
89
-
90
- d.switch = function() {
91
- switchMode();
92
- }
93
- menuGlobe.gui.add(d, 'switch').name('Mode Switch');
94
-
95
-
96
-
97
-
98
-
88
+ $3dTilesLayerSetePC.whenReady
89
+ .then(() => {
90
+ debug.createTileDebugUI(menuGlobe.gui, view, view.tileLayer, d);
91
+ debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerSetePC);
92
+ });
99
93
  </script>
100
94
  </body>
101
95
  </html>
@@ -109,7 +109,7 @@
109
109
  pointBudget: 3000000,
110
110
  });
111
111
  view.addLayer(layer).then(onLayerReady);
112
- debug.PotreeDebug.initTools(view, layer, gui);
112
+ debug.PointCloudDebug.initTools(view, layer, gui);
113
113
  }
114
114
 
115
115
  function loadAutzen() {
@@ -98,7 +98,7 @@
98
98
 
99
99
  itowns.View.prototype.addLayer.call(view, eptLayer).then(onLayerReady);
100
100
 
101
- debug.PotreeDebug.initTools(view, eptLayer, debugGui);
101
+ debug.PointCloudDebug.initTools(view, eptLayer, debugGui);
102
102
  }
103
103
 
104
104
  readEPTURL();
@@ -104,7 +104,7 @@
104
104
  view.addLayer(eptLayer).then(onLayerReady);
105
105
 
106
106
  eptLayer.whenReady
107
- .then(() => debug.PotreeDebug.initTools(view, eptLayer, debugGui));
107
+ .then(() => debug.PointCloudDebug.initTools(view, eptLayer, debugGui));
108
108
 
109
109
  function dblClickHandler(event) {
110
110
  var pick = view.pickObjectsAt(event, 5, eptLayer);
@@ -22,10 +22,8 @@
22
22
  * @returns {GeoTIFFLevel} The selected zoom level.
23
23
  */
24
24
  function selectLevel(source, requestExtent, requestWidth, requestHeight) {
25
- // Number of images = original + overviews if any
26
- const cropped = requestExtent.clone().intersect(source.extent);
27
25
  // Dimensions of the requested extent
28
- const extentDimension = cropped.planarDimensions();
26
+ const extentDimension = requestExtent.clone().planarDimensions();
29
27
 
30
28
  const targetResolution = Math.min(
31
29
  extentDimension.x / requestWidth,
@@ -87,70 +85,112 @@ function makeWindowFromExtent(source, extent, resolution) {
87
85
  }
88
86
 
89
87
  /**
90
- * Creates a texture from the pixel buffer(s).
88
+ * Reads raster data from the image as RGB.
89
+ * The result is always an interleaved typed array.
90
+ * Colorspaces other than RGB will be transformed to RGB, color maps expanded.
91
+ *
92
+ * @param {Source} source The COGSource.
93
+ * @param {GeoTIFFLevel} level The GeoTIFF level to read
94
+ * @param {number[]} viewport The image region to read.
95
+ * @returns {Promise<TypedArray[]>} The raster data
96
+ */
97
+ async function readRGB(source, level, viewport) {
98
+ try {
99
+ // TODO possible optimization: instead of letting geotiff.js crop and resample
100
+ // the tiles into the desired region, we could use image.getTileOrStrip() to
101
+ // read individual tiles (aka blocks) and make a texture per block. This way,
102
+ // there would not be multiple concurrent reads for the same block, and we would not
103
+ // waste time resampling the blocks since resampling is already done in the composer.
104
+ // We would create more textures, but it could be worth it.
105
+ return await level.image.readRGB({
106
+ window: viewport,
107
+ pool: source.pool,
108
+ width: source.tileWidth,
109
+ height: source.tileHeight,
110
+ resampleMethod: source.resampleMethod,
111
+ enableAlpha: true,
112
+ interleave: true,
113
+ });
114
+ } catch (error) {
115
+ if (error.toString() === 'AggregateError: Request failed') {
116
+ // Problem with the source that is blocked by another fetch
117
+ // (request failed in readRasters). See the conversations in
118
+ // https://github.com/geotiffjs/geotiff.js/issues/218
119
+ // https://github.com/geotiffjs/geotiff.js/issues/221
120
+ // https://github.com/geotiffjs/geotiff.js/pull/224
121
+ // Retry until it is not blocked.
122
+ // TODO retry counter
123
+ await new Promise((resolve) => {
124
+ setTimeout(resolve, 100);
125
+ });
126
+ return readRGB(level, viewport, source);
127
+ }
128
+ throw error;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Creates a texture from the pixel buffer
91
134
  *
92
135
  * @param {Object} source The COGSource
93
- * @param {THREE.TypedArray | THREE.TypedArray[]} buffers The buffers (one buffer per band)
94
- * @param {number} buffers.width
95
- * @param {number} buffers.height
96
- * @param {number} buffers.byteLength
136
+ * @param {THREE.TypedArray[]} buffer The pixel buffer
137
+ * @param {number} buffer.width
138
+ * @param {number} buffer.height
139
+ * @param {number} buffer.byteLength
97
140
  * @returns {THREE.DataTexture} The generated texture.
98
141
  */
99
- function createTexture(source, buffers) {
100
- const { width, height, byteLength } = buffers;
142
+ function createTexture(source, buffer) {
143
+ const { byteLength } = buffer;
144
+ const width = source.tileWidth;
145
+ const height = source.tileHeight;
101
146
  const pixelCount = width * height;
102
147
  const targetDataType = source.dataType;
103
148
  const format = THREE.RGBAFormat;
104
149
  const channelCount = 4;
105
- let texture;
106
- let data;
107
-
108
- // Check if it's a RGBA buffer
109
- if (pixelCount * channelCount === byteLength) {
110
- data = buffers;
111
- }
150
+ const isRGBA = pixelCount * channelCount === byteLength;
151
+ let tmpBuffer = buffer;
112
152
 
113
153
  switch (targetDataType) {
114
154
  case THREE.UnsignedByteType: {
115
- if (!data) {
116
- // We convert RGB buffer to RGBA
117
- const newBuffers = new Uint8ClampedArray(pixelCount * channelCount);
118
- data = convertToRGBA(buffers, newBuffers, source.defaultAlpha);
155
+ if (!isRGBA) {
156
+ tmpBuffer = convertToRGBA(tmpBuffer, new Uint8ClampedArray(pixelCount * channelCount), source.defaultAlpha);
119
157
  }
120
- texture = new THREE.DataTexture(data, width, height, format, THREE.UnsignedByteType);
121
- break;
158
+ return new THREE.DataTexture(tmpBuffer, width, height, format, THREE.UnsignedByteType);
122
159
  }
123
160
  case THREE.FloatType: {
124
- if (!data) {
125
- // We convert RGB buffer to RGBA
126
- const newBuffers = new Float32Array(pixelCount * channelCount);
127
- data = convertToRGBA(buffers, newBuffers, source.defaultAlpha / 255);
161
+ if (!isRGBA) {
162
+ tmpBuffer = convertToRGBA(tmpBuffer, new Float32Array(pixelCount * channelCount), source.defaultAlpha / 255);
128
163
  }
129
- texture = new THREE.DataTexture(data, width, height, format, THREE.FloatType);
130
- break;
164
+ return new THREE.DataTexture(tmpBuffer, width, height, format, THREE.FloatType);
131
165
  }
132
166
  default:
133
167
  throw new Error('unsupported data type');
134
168
  }
135
-
136
- return texture;
137
169
  }
138
170
 
139
- function convertToRGBA(buffers, newBuffers, defaultAlpha) {
140
- const { width, height } = buffers;
171
+ /**
172
+ * Convert RGB pixel buffer to RGBA pixel buffer
173
+ *
174
+ * @param {THREE.TypedArray[]} buffer The RGB pixel buffer
175
+ * @param {THREE.TypedArray[]} newBuffer The empty RGBA pixel buffer
176
+ * @param {number} defaultAlpha Default alpha value
177
+ * @returns {THREE.DataTexture} The generated texture.
178
+ */
179
+ function convertToRGBA(buffer, newBuffer, defaultAlpha) {
180
+ const { width, height } = buffer;
141
181
 
142
182
  for (let i = 0; i < width * height; i++) {
143
183
  const oldIndex = i * 3;
144
184
  const index = i * 4;
145
185
  // Copy RGB from original buffer
146
- newBuffers[index + 0] = buffers[oldIndex + 0]; // R
147
- newBuffers[index + 1] = buffers[oldIndex + 1]; // G
148
- newBuffers[index + 2] = buffers[oldIndex + 2]; // B
186
+ newBuffer[index + 0] = buffer[oldIndex + 0]; // R
187
+ newBuffer[index + 1] = buffer[oldIndex + 1]; // G
188
+ newBuffer[index + 2] = buffer[oldIndex + 2]; // B
149
189
  // Add alpha to new buffer
150
- newBuffers[index + 3] = defaultAlpha; // A
190
+ newBuffer[index + 3] = defaultAlpha; // A
151
191
  }
152
192
 
153
- return newBuffers;
193
+ return newBuffer;
154
194
  }
155
195
 
156
196
  /**
@@ -195,20 +235,14 @@ const COGParser = (function _() {
195
235
  */
196
236
  parse: async function _(data, options) {
197
237
  const source = options.in;
198
- const nodeExtent = data.extent.as(source.crs);
199
- const level = selectLevel(source, nodeExtent, source.tileWidth, source.tileHeight);
200
- const viewport = makeWindowFromExtent(source, nodeExtent, level.resolution);
201
-
202
- const buffers = await level.image.readRGB({
203
- window: viewport,
204
- pool: source.pool,
205
- enableAlpha: true,
206
- interleave: true,
207
- });
238
+ const tileExtent = options.extent.as(source.crs);
208
239
 
209
- const texture = createTexture(source, buffers);
240
+ const level = selectLevel(source, tileExtent, source.tileWidth, source.tileHeight);
241
+ const viewport = makeWindowFromExtent(source, tileExtent, level.resolution);
242
+ const rgbBuffer = await readRGB(source, level, viewport);
243
+ const texture = createTexture(source, rgbBuffer);
210
244
  texture.flipY = true;
211
- texture.extent = data.extent;
245
+ texture.extent = options.extent;
212
246
  texture.needsUpdate = true;
213
247
  texture.magFilter = THREE.LinearFilter;
214
248
  texture.minFilter = THREE.LinearFilter;
@@ -15,6 +15,9 @@
15
15
  * @property {string} url - The URL of the COG.
16
16
  * @property {GeoTIFF.Pool} pool - Pool use to decode GeoTiff.
17
17
  * @property {number} defaultAlpha - Alpha byte value used if no alpha is present in COG. Default value is 255.
18
+ * @property {number} tileWidth - Tile width in pixels. Default value use 'geotiff.getTileWidth()'.
19
+ * @property {number} tileHeight - Tile height in pixels. Default value use 'geotiff.getTileHeight()'.
20
+ * @property {number} resampleMethod - The desired resampling method. Default is 'nearest'.
18
21
  *
19
22
  * @example
20
23
  * // Create the source
@@ -59,9 +62,9 @@ class COGSource extends itowns.Source {
59
62
  this.firstImage = await geotiff.getImage();
60
63
  this.origin = this.firstImage.getOrigin();
61
64
  this.dataType = this.selectDataType(this.firstImage.getSampleFormat(), this.firstImage.getBitsPerSample());
62
-
63
- this.tileWidth = this.firstImage.getTileWidth();
64
- this.tileHeight = this.firstImage.getTileHeight();
65
+ this.tileWidth = source.tileWidth || this.firstImage.getTileWidth();
66
+ this.tileHeight = source.tileHeight || this.firstImage.getTileHeight();
67
+ this.resampleMethod = source.resampleMethod || 'nearest';
65
68
 
66
69
  // Compute extent
67
70
  const [minX, minY, maxX, maxY] = this.firstImage.getBoundingBox();
@@ -80,7 +83,7 @@ class COGSource extends itowns.Source {
80
83
  .then(image => this.makeLevel(image, image.getResolution(this.firstImage)));
81
84
  promises.push(promise);
82
85
  }
83
- this.levels.push(await Promise.all(promises));
86
+ this.levels = this.levels.concat(await Promise.all(promises));
84
87
  });
85
88
  }
86
89
 
@@ -102,7 +102,7 @@
102
102
  potreeLayer.root.bbox.getSize(size);
103
103
  potreeLayer.root.bbox.getCenter(lookAt);
104
104
 
105
- debug.PotreeDebug.initTools(view, potreeLayer, debugGui);
105
+ debug.PointCloudDebug.initTools(view, potreeLayer, debugGui);
106
106
 
107
107
  view.camera3D.far = 2.0 * size.length();
108
108
 
@@ -71,7 +71,7 @@
71
71
 
72
72
  // add potreeLayer to scene
73
73
  function onLayerReady() {
74
- debug.PotreeDebug.initTools(view, potreeLayer, debugGui);
74
+ debug.PointCloudDebug.initTools(view, potreeLayer, debugGui);
75
75
 
76
76
  // update stats window
77
77
  var info = document.getElementById('info');
@@ -36,6 +36,7 @@
36
36
  itowns.proj4.defs('EPSG:2154', '+proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
37
37
 
38
38
  var viewerDiv = document.getElementById('viewerDiv');
39
+ var view;
39
40
 
40
41
  function readCOGURL() {
41
42
  var url = document.getElementById('cog_url').value || new URLSearchParams(window.location.search).get('geotiff');
@@ -47,7 +48,7 @@
47
48
  }
48
49
 
49
50
  function loadRGBSample() {
50
- document.getElementById('cog_url').value = 'https://cdn.jsdelivr.net/gh/bloc-in-bloc/iTowns2-sample-data@add-cog-sample/cog/orvault.tif';
51
+ document.getElementById('cog_url').value = 'https://cdn.jsdelivr.net/gh/iTowns/iTowns2-sample-data/cog/nantes/nantes.tif';
51
52
  readCOGURL();
52
53
  }
53
54
 
@@ -56,17 +57,33 @@
56
57
  readCOGURL();
57
58
  }
58
59
 
59
- function loadCOG(url, crs) {
60
+ function removeAllChildNodes(parent) {
61
+ while (parent.firstChild) {
62
+ parent.removeChild(parent.firstChild);
63
+ }
64
+ }
65
+
66
+ function loadCOG(url) {
60
67
  // create a source from a Cloud Optimized GeoTiff
61
68
  var cogSource = new COGSource({
62
69
  url: url,
63
- crs: "EPSG:2154"
70
+ crs: "EPSG:2154",
71
+ // Default resample method is 'nearest', 'bilinear' looks better when zoomed
72
+ resampleMethod: 'bilinear'
64
73
  });
65
74
 
66
75
  cogSource.whenReady.then(() => {
67
- var view = new itowns.PlanarView(viewerDiv, cogSource.extent, { disableSkirt: true, placement: { tilt: 90 } });
76
+ if (view !== undefined) {
77
+ view.dispose(true);
78
+ removeAllChildNodes(viewerDiv);
79
+ }
80
+ view = new itowns.PlanarView(viewerDiv, cogSource.extent, {
81
+ // Default maxSubdivisionLevel is 5, so with huge geotiff it's not enough to see details when zoomed
82
+ maxSubdivisionLevel: 10,
83
+ disableSkirt: true,
84
+ placement: { tilt: 90 }
85
+ });
68
86
  setupLoadingScreen(viewerDiv, view);
69
- new itowns.PlanarControls(view, {});
70
87
  var cogLayer = new itowns.ColorLayer('cog', {
71
88
  source: cogSource,
72
89
  });
@@ -92,7 +92,9 @@ class C3DTilesLayer extends GeometryLayer {
92
92
  * @param {Number} [config.cleanupDelay=1000] The time (in ms) after which a tile content (and its children) are
93
93
  * removed from the scene.
94
94
  * @param {C3DTExtensions} [config.registeredExtensions] 3D Tiles extensions managers registered for this tileset.
95
- * @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.
95
+ * @param {String} [config.pntsMode= PNTS_MODE.COLOR] {@link PointsMaterials} Point cloud coloring mode.
96
+ * Only 'COLOR' or 'CLASSIFICATION' are possible. COLOR uses RGB colors of the points,
97
+ * CLASSIFICATION uses a classification property of the batch table to color points.
96
98
  * @param {String} [config.pntsShape= PNTS_SHAPE.CIRCLE] Point cloud point shape. Only 'CIRCLE' or 'SQUARE' are possible.
97
99
  * @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.
98
100
  * @param {Number} [config.pntsMinAttenuatedSize=3] Minimum scale used by 'ATTENUATED' size mode
@@ -176,7 +176,7 @@ class PointCloudLayer extends GeometryLayer {
176
176
  if (this.material) {
177
177
  this.material.visible = this.visible;
178
178
  this.material.opacity = this.opacity;
179
- this.material.transparent = this.opacity < 1;
179
+ this.material.transparent = this.opacity < 1 || this.material.userData.needTransparency[this.material.mode];
180
180
  this.material.size = this.pointSize;
181
181
  this.material.scale = context.camera.preSSE;
182
182
  if (this.material.updateUniforms) {
@@ -1,6 +1,5 @@
1
1
  // next step is move these properties to Style class
2
2
  // and hide transparent mechanism
3
-
4
3
  function ReferLayerProperties(material, layer) {
5
4
  if (layer && layer.isGeometryLayer) {
6
5
  let transparent = material.transparent;
@@ -44,9 +43,11 @@ function ReferLayerProperties(material, layer) {
44
43
  });
45
44
  Object.defineProperty(material, 'transparent', {
46
45
  get: () => {
47
- if (transparent != material.layer.opacity < 1.0) {
46
+ var _material$userData$ne;
47
+ const needTransparency = ((_material$userData$ne = material.userData.needTransparency) === null || _material$userData$ne === void 0 ? void 0 : _material$userData$ne[material.mode]) || material.layer.opacity < 1.0;
48
+ if (transparent != needTransparency) {
48
49
  material.needsUpdate = true;
49
- transparent = material.layer.opacity < 1.0;
50
+ transparent = needTransparency;
50
51
  }
51
52
  return transparent;
52
53
  }
@@ -114,6 +114,7 @@ function executeCommand(command) {
114
114
  } else if (magic == 'b3dm') {
115
115
  func = supportedFormats.b3dm;
116
116
  } else if (magic == 'pnts') {
117
+ layer.hasPnts = true;
117
118
  func = supportedFormats.pnts;
118
119
  } else if (magic == 'glTF') {
119
120
  func = supportedFormats.gltf;
@@ -1,6 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  /* babel-plugin-inline-import './Shader/PointsVS.glsl' */
3
- const PointsVS = "#include <itowns/precision_qualifier>\n#if defined(USE_TEXTURES_PROJECTIVE)\n#include <itowns/projective_texturing_pars_vertex>\n#endif\n#include <common>\n#include <logdepthbuf_pars_vertex>\n\n#define NB_CLASS 8.\n\nuniform float size;\nuniform float scale;\n\nuniform bool picking;\nuniform int mode;\nuniform float opacity;\nuniform vec4 overlayColor;\n\nuniform vec2 elevationRange;\nuniform vec2 intensityRange;\nuniform vec2 angleRange;\n\nuniform bool applyOpacityClassication;\n\nuniform sampler2D classificationTexture;\nuniform sampler2D discreteTexture;\nuniform sampler2D gradientTexture;\nuniform int sizeMode;\nuniform float minAttenuatedSize;\nuniform float maxAttenuatedSize;\n\nattribute vec3 color;\nattribute vec2 range;\nattribute vec4 unique_id;\nattribute float intensity;\nattribute float classification;\nattribute float pointSourceID;\n\nattribute float returnNumber;\nattribute float numberOfReturns;\nattribute float scanAngle;\n\n#if defined(NORMAL_OCT16)\nattribute vec2 oct16Normal;\n#elif defined(NORMAL_SPHEREMAPPED)\nattribute vec2 sphereMappedNormal;\n#endif\n\nvarying vec4 vColor;\n\n// see https://web.archive.org/web/20150303053317/http://lgdv.cs.fau.de/get/1602\n// and implementation in PotreeConverter (BINPointReader.cpp) and potree (BinaryDecoderWorker.js)\n#if defined(NORMAL_OCT16)\nvec3 decodeOct16Normal(vec2 encodedNormal) {\n vec2 nNorm = 2. * (encodedNormal / 255.) - 1.;\n vec3 n;\n n.z = 1. - abs(nNorm.x) - abs(nNorm.y);\n if (n.z >= 0.) {\n n.x = nNorm.x;\n n.y = nNorm.y;\n } else {\n n.x = sign(nNorm.x) - sign(nNorm.x) * sign(nNorm.y) * nNorm.y;\n n.y = sign(nNorm.y) - sign(nNorm.y) * sign(nNorm.x) * nNorm.x;\n }\n return normalize(n);\n}\n#elif defined(NORMAL_SPHEREMAPPED)\n// see http://aras-p.info/texts/CompactNormalStorage.html method #4\n// or see potree's implementation in BINPointReader.cpp\nvec3 decodeSphereMappedNormal(vec2 encodedNormal) {\n vec2 fenc = 2. * encodedNormal / 255. - 1.;\n float f = dot(fenc,fenc);\n float g = 2. * sqrt(1. - f);\n vec3 n;\n n.xy = fenc * g;\n n.z = 1. - 2. * f;\n return n;\n}\n#endif\n\nvoid main() {\n\n#if defined(NORMAL_OCT16)\n vec3 normal = decodeOct16Normal(oct16Normal);\n#elif defined(NORMAL_SPHEREMAPPED)\n vec3 normal = decodeSphereMappedNormal(sphereMappedNormal);\n#elif defined(NORMAL)\n // nothing to do\n#else\n // default to color\n vec3 normal = color;\n#endif\n\n if (picking) {\n vColor = unique_id;\n } else {\n vColor.a = opacity;\n if (applyOpacityClassication || mode == PNTS_MODE_CLASSIFICATION) {\n vec2 uv = vec2(classification/255., 0.5);\n vColor = texture2D(classificationTexture, uv);\n vColor.a *= opacity;\n }\n\n if (mode == PNTS_MODE_NORMAL) {\n vColor.rgb = abs(normal);\n } else if (mode == PNTS_MODE_COLOR) {\n // default to color mode\n vColor.rgb = mix(color, overlayColor.rgb, overlayColor.a);\n } else if (mode == PNTS_MODE_RETURN_NUMBER) {\n vec2 uv = vec2(returnNumber/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_RETURN_TYPE) {\n float returnType;\n if (returnNumber > numberOfReturns) {\n returnType = 4.;\n } else if (returnNumber == 1.) {\n if (numberOfReturns == 1.) {\n // single\n returnType = 0.;\n } else {\n // first\n returnType = 1.;\n }\n } else {\n if (returnNumber == numberOfReturns) {\n // last\n returnType = 3.;\n } else {\n // intermediate\n returnType = 2.;\n }\n }\n vec2 uv = vec2(returnType/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_RETURN_COUNT) {\n vec2 uv = vec2(numberOfReturns/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_POINT_SOURCE_ID) {\n vec2 uv = vec2(mod(pointSourceID, NB_CLASS)/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_SCAN_ANGLE) {\n float i = (scanAngle - angleRange.x) / (angleRange.y - angleRange.x);\n vec2 uv = vec2(i, (1. - i));\n vColor = texture2D(gradientTexture, uv);\n } else if (mode == PNTS_MODE_INTENSITY) {\n float i = (intensity - intensityRange.x) / (intensityRange.y - intensityRange.x);\n vec2 uv = vec2(i, (1. - i));\n vColor = texture2D(gradientTexture, uv);\n } else if (mode == PNTS_MODE_ELEVATION) {\n float i = (position.z - elevationRange.x) / (elevationRange.y - elevationRange.x);\n vec2 uv = vec2(i, (1. - i));\n vColor = texture2D(gradientTexture, uv);\n }\n }\n\n #include <begin_vertex>\n #include <project_vertex>\n\n gl_PointSize = size;\n\n if (sizeMode == PNTS_SIZE_MODE_ATTENUATED) {\n bool isPerspective = isPerspectiveMatrix(projectionMatrix);\n\n if (isPerspective) {\n gl_PointSize *= scale / -mvPosition.z;\n gl_PointSize = clamp(gl_PointSize, minAttenuatedSize, maxAttenuatedSize);\n }\n }\n\n#if defined(USE_TEXTURES_PROJECTIVE)\n #include <itowns/projective_texturing_vertex>\n#endif\n #include <logdepthbuf_vertex>\n}\n";
3
+ const PointsVS = "#include <itowns/precision_qualifier>\n#if defined(USE_TEXTURES_PROJECTIVE)\n#include <itowns/projective_texturing_pars_vertex>\n#endif\n#include <common>\n#include <logdepthbuf_pars_vertex>\n\n#define NB_CLASS 8.\n\nuniform float size;\nuniform float scale;\n\nuniform bool picking;\nuniform int mode;\nuniform float opacity;\nuniform vec4 overlayColor;\n\nuniform vec2 elevationRange;\nuniform vec2 intensityRange;\nuniform vec2 angleRange;\n\nuniform sampler2D classificationTexture;\nuniform sampler2D discreteTexture;\nuniform sampler2D gradientTexture;\nuniform int sizeMode;\nuniform float minAttenuatedSize;\nuniform float maxAttenuatedSize;\n\nattribute vec3 color;\nattribute vec2 range;\nattribute vec4 unique_id;\nattribute float intensity;\nattribute float classification;\nattribute float pointSourceID;\n\nattribute float returnNumber;\nattribute float numberOfReturns;\nattribute float scanAngle;\n\n#if defined(NORMAL_OCT16)\nattribute vec2 oct16Normal;\n#elif defined(NORMAL_SPHEREMAPPED)\nattribute vec2 sphereMappedNormal;\n#endif\n\nvarying vec4 vColor;\n\n// see https://web.archive.org/web/20150303053317/http://lgdv.cs.fau.de/get/1602\n// and implementation in PotreeConverter (BINPointReader.cpp) and potree (BinaryDecoderWorker.js)\n#if defined(NORMAL_OCT16)\nvec3 decodeOct16Normal(vec2 encodedNormal) {\n vec2 nNorm = 2. * (encodedNormal / 255.) - 1.;\n vec3 n;\n n.z = 1. - abs(nNorm.x) - abs(nNorm.y);\n if (n.z >= 0.) {\n n.x = nNorm.x;\n n.y = nNorm.y;\n } else {\n n.x = sign(nNorm.x) - sign(nNorm.x) * sign(nNorm.y) * nNorm.y;\n n.y = sign(nNorm.y) - sign(nNorm.y) * sign(nNorm.x) * nNorm.x;\n }\n return normalize(n);\n}\n#elif defined(NORMAL_SPHEREMAPPED)\n// see http://aras-p.info/texts/CompactNormalStorage.html method #4\n// or see potree's implementation in BINPointReader.cpp\nvec3 decodeSphereMappedNormal(vec2 encodedNormal) {\n vec2 fenc = 2. * encodedNormal / 255. - 1.;\n float f = dot(fenc,fenc);\n float g = 2. * sqrt(1. - f);\n vec3 n;\n n.xy = fenc * g;\n n.z = 1. - 2. * f;\n return n;\n}\n#endif\n\nvoid main() {\n\n#if defined(NORMAL_OCT16)\n vec3 normal = decodeOct16Normal(oct16Normal);\n#elif defined(NORMAL_SPHEREMAPPED)\n vec3 normal = decodeSphereMappedNormal(sphereMappedNormal);\n#elif defined(NORMAL)\n // nothing to do\n#else\n // default to color\n vec3 normal = color;\n#endif\n\n if (picking) {\n vColor = unique_id;\n } else {\n vColor.a = 1.0;\n if (mode == PNTS_MODE_CLASSIFICATION) {\n vec2 uv = vec2(classification/255., 0.5);\n vColor = texture2D(classificationTexture, uv);\n } else if (mode == PNTS_MODE_NORMAL) {\n vColor.rgb = abs(normal);\n } else if (mode == PNTS_MODE_COLOR) {\n // default to color mode\n vColor.rgb = mix(color, overlayColor.rgb, overlayColor.a);\n } else if (mode == PNTS_MODE_RETURN_NUMBER) {\n vec2 uv = vec2(returnNumber/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_RETURN_TYPE) {\n float returnType;\n if (returnNumber > numberOfReturns) {\n returnType = 4.;\n } else if (returnNumber == 1.) {\n if (numberOfReturns == 1.) {\n // single\n returnType = 0.;\n } else {\n // first\n returnType = 1.;\n }\n } else {\n if (returnNumber == numberOfReturns) {\n // last\n returnType = 3.;\n } else {\n // intermediate\n returnType = 2.;\n }\n }\n vec2 uv = vec2(returnType/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_RETURN_COUNT) {\n vec2 uv = vec2(numberOfReturns/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_POINT_SOURCE_ID) {\n vec2 uv = vec2(mod(pointSourceID, NB_CLASS)/255., 0.5);\n vColor = texture2D(discreteTexture, uv);\n } else if (mode == PNTS_MODE_SCAN_ANGLE) {\n float i = (scanAngle - angleRange.x) / (angleRange.y - angleRange.x);\n vec2 uv = vec2(i, (1. - i));\n vColor = texture2D(gradientTexture, uv);\n } else if (mode == PNTS_MODE_INTENSITY) {\n float i = (intensity - intensityRange.x) / (intensityRange.y - intensityRange.x);\n vec2 uv = vec2(i, (1. - i));\n vColor = texture2D(gradientTexture, uv);\n } else if (mode == PNTS_MODE_ELEVATION) {\n float i = (position.z - elevationRange.x) / (elevationRange.y - elevationRange.x);\n vec2 uv = vec2(i, (1. - i));\n vColor = texture2D(gradientTexture, uv);\n }\n\n vColor.a *= opacity;\n }\n\n #include <begin_vertex>\n #include <project_vertex>\n\n gl_PointSize = size;\n\n if (sizeMode == PNTS_SIZE_MODE_ATTENUATED) {\n bool isPerspective = isPerspectiveMatrix(projectionMatrix);\n\n if (isPerspective) {\n gl_PointSize *= scale / -mvPosition.z;\n gl_PointSize = clamp(gl_PointSize, minAttenuatedSize, maxAttenuatedSize);\n }\n }\n\n#if defined(USE_TEXTURES_PROJECTIVE)\n #include <itowns/projective_texturing_vertex>\n#endif\n #include <logdepthbuf_vertex>\n}\n";
4
4
  /* babel-plugin-inline-import './Shader/PointsFS.glsl' */
5
5
  const PointsFS = "#include <itowns/precision_qualifier>\n#include <logdepthbuf_pars_fragment>\n#if defined(USE_TEXTURES_PROJECTIVE)\n#include <itowns/projective_texturing_pars_fragment>\n#endif\n\nvarying vec4 vColor;\nuniform bool picking;\nuniform int shape;\n\nvoid main() {\n #include <logdepthbuf_fragment>\n //square shape does not require any change.\n if (shape == PNTS_SHAPE_CIRCLE) {\n //circular rendering in glsl\n if ((length(gl_PointCoord - 0.5) > 0.5) || (vColor.a == 0.0)) {\n discard;\n }\n }\n\n#if defined(USE_TEXTURES_PROJECTIVE)\n vec4 color = vColor;\n if (!picking) {\n #pragma unroll_loop\n for (int i = 0; i < ORIENTED_IMAGES_COUNT; i++) {\n color = projectiveTextureColor(projectiveTextureCoords[ ORIENTED_IMAGES_COUNT - 1 - i ], projectiveTextureDistortion[ ORIENTED_IMAGES_COUNT - 1 - i ], projectiveTexture[ ORIENTED_IMAGES_COUNT - 1 - i ], mask[ORIENTED_IMAGES_COUNT - 1 - i], color);\n }\n gl_FragColor = vec4(color.rgb, color.a * opacity);\n } else {\n gl_FragColor = color;\n }\n#else\n gl_FragColor = vColor;\n#endif\n}\n";
6
6
  import ShaderUtils from "./Shader/ShaderUtils.js";
@@ -127,7 +127,7 @@ export const ClassificationScheme = {
127
127
  visible: true,
128
128
  name: 'default',
129
129
  color: new THREE.Color(0.3, 0.6, 0.6),
130
- opacity: 0.5
130
+ opacity: 1.0
131
131
  }
132
132
  }
133
133
  };
@@ -185,7 +185,7 @@ const DiscreteScheme = {
185
185
  visible: true,
186
186
  name: 'default',
187
187
  color: white,
188
- opacity: 0.5
188
+ opacity: 1.0
189
189
  }
190
190
  }
191
191
  };
@@ -220,6 +220,7 @@ function generateGradientTexture(gradient) {
220
220
  return texture;
221
221
  }
222
222
  function recomputeTexture(scheme, texture, nbClass) {
223
+ let needTransparency;
223
224
  const data = texture.image.data;
224
225
  const width = texture.image.width;
225
226
  if (!nbClass) {
@@ -250,8 +251,10 @@ function recomputeTexture(scheme, texture, nbClass) {
250
251
  data[j + 1] = parseInt(255 * color.g, 10);
251
252
  data[j + 2] = parseInt(255 * color.b, 10);
252
253
  data[j + 3] = visible ? parseInt(255 * opacity, 10) : 0;
254
+ needTransparency = needTransparency || opacity < 1;
253
255
  }
254
256
  texture.needsUpdate = true;
257
+ return needTransparency;
255
258
  }
256
259
  class PointsMaterial extends THREE.ShaderMaterial {
257
260
  /**
@@ -264,7 +267,6 @@ class PointsMaterial extends THREE.ShaderMaterial {
264
267
  * @param {THREE.Vector2} [options.intensityRange=new THREE.Vector2(1, 65536)] intensity range.
265
268
  * @param {THREE.Vector2} [options.elevationRange=new THREE.Vector2(0, 1000)] elevation range.
266
269
  * @param {THREE.Vector2} [options.angleRange=new THREE.Vector2(-90, 90)] scan angle range.
267
- * @param {boolean} [options.applyOpacityClassication=false] apply opacity classification on all display mode.
268
270
  * @param {Scheme} [options.classification] LUT for point classification colorization.
269
271
  * @param {Scheme} [options.discreteScheme] LUT for other discret point values colorization.
270
272
  * @param {string} [options.gradient] Descrition of the gradient to use for continuous point values.
@@ -293,7 +295,6 @@ class PointsMaterial extends THREE.ShaderMaterial {
293
295
  const oiMaterial = options.orientedImageMaterial;
294
296
  const classificationScheme = options.classification || ClassificationScheme.DEFAULT;
295
297
  const discreteScheme = options.discreteScheme || DiscreteScheme.DEFAULT;
296
- const applyOpacityClassication = options.applyOpacityClassication == undefined ? false : options.applyOpacityClassication;
297
298
  const size = options.size || 0;
298
299
  const mode = options.mode || PNTS_MODE.COLOR;
299
300
  const shape = options.shape || PNTS_SHAPE.CIRCLE;
@@ -313,7 +314,6 @@ class PointsMaterial extends THREE.ShaderMaterial {
313
314
  delete options.orientedImageMaterial;
314
315
  delete options.classification;
315
316
  delete options.discreteScheme;
316
- delete options.applyOpacityClassication;
317
317
  delete options.size;
318
318
  delete options.mode;
319
319
  delete options.shape;
@@ -322,6 +322,7 @@ class PointsMaterial extends THREE.ShaderMaterial {
322
322
  delete options.maxAttenuatedSize;
323
323
  delete options.gradient;
324
324
  super(options);
325
+ this.userData.needTransparency = {};
325
326
  this.gradients = gradients;
326
327
  this.gradientTexture = new THREE.CanvasTexture();
327
328
  this.vertexShader = PointsVS;
@@ -339,7 +340,6 @@ class PointsMaterial extends THREE.ShaderMaterial {
339
340
  CommonMaterial.setUniformProperty(this, 'intensityRange', intensityRange);
340
341
  CommonMaterial.setUniformProperty(this, 'elevationRange', elevationRange);
341
342
  CommonMaterial.setUniformProperty(this, 'angleRange', angleRange);
342
- CommonMaterial.setUniformProperty(this, 'applyOpacityClassication', applyOpacityClassication);
343
343
  CommonMaterial.setUniformProperty(this, 'sizeMode', sizeMode);
344
344
  CommonMaterial.setUniformProperty(this, 'scale', scale);
345
345
  CommonMaterial.setUniformProperty(this, 'minAttenuatedSize', minAttenuatedSize);
@@ -390,14 +390,19 @@ class PointsMaterial extends THREE.ShaderMaterial {
390
390
  }
391
391
  }
392
392
  recomputeClassification() {
393
- recomputeTexture(this.classificationScheme, this.classificationTexture, 32);
393
+ const needTransparency = recomputeTexture(this.classificationScheme, this.classificationTexture, 32);
394
+ this.userData.needTransparency[PNTS_MODE.CLASSIFICATION] = needTransparency;
394
395
  this.dispatchEvent({
395
396
  type: 'material_property_changed',
396
397
  target: this.uniforms
397
398
  });
398
399
  }
399
400
  recomputeDiscreteTexture() {
400
- recomputeTexture(this.discreteScheme, this.discreteTexture);
401
+ const needTransparency = recomputeTexture(this.discreteScheme, this.discreteTexture);
402
+ this.userData.needTransparency[PNTS_MODE.RETURN_NUMBER] = needTransparency;
403
+ this.userData.needTransparency[PNTS_MODE.RETURN_TYPE] = needTransparency;
404
+ this.userData.needTransparency[PNTS_MODE.RETURN_COUNT] = needTransparency;
405
+ this.userData.needTransparency[PNTS_MODE.POINT_SOURCE_ID] = needTransparency;
401
406
  this.dispatchEvent({
402
407
  type: 'material_property_changed',
403
408
  target: this.uniforms
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itowns",
3
- "version": "2.43.2-next.3",
3
+ "version": "2.43.2-next.5",
4
4
  "description": "A JS/WebGL framework for 3D geospatial data visualization",
5
5
  "type": "module",
6
6
  "main": "lib/Main.js",