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.
- package/dist/debug.js +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/itowns.js +1 -1
- package/dist/itowns.js.map +1 -1
- package/examples/3dtiles_25d.html +1 -1
- package/examples/3dtiles_basic.html +2 -2
- package/examples/3dtiles_batch_table.html +1 -3
- package/examples/3dtiles_pointcloud.html +9 -15
- package/examples/copc_simple_loader.html +1 -1
- package/examples/entwine_3d_loader.html +1 -1
- package/examples/entwine_simple_loader.html +1 -1
- package/examples/js/plugins/COGParser.js +84 -50
- package/examples/js/plugins/COGSource.js +7 -4
- package/examples/potree_25d_map.html +1 -1
- package/examples/potree_3d_map.html +1 -1
- package/examples/source_file_cog.html +22 -5
- package/lib/Layer/C3DTilesLayer.js +3 -1
- package/lib/Layer/PointCloudLayer.js +1 -1
- package/lib/Layer/ReferencingLayerProperties.js +4 -3
- package/lib/Provider/3dTilesProvider.js +1 -0
- package/lib/Renderer/PointsMaterial.js +14 -9
- package/package.json +1 -1
|
@@ -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
|
|
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
|
|
85
|
-
debug.create3dTilesDebugUI(menuGlobe.gui, view, $3dTilesLayerRequestVolume
|
|
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:
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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>
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
view.addLayer(eptLayer).then(onLayerReady);
|
|
105
105
|
|
|
106
106
|
eptLayer.whenReady
|
|
107
|
-
.then(() => debug.
|
|
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 =
|
|
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
|
-
*
|
|
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
|
|
94
|
-
* @param {number}
|
|
95
|
-
* @param {number}
|
|
96
|
-
* @param {number}
|
|
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,
|
|
100
|
-
const {
|
|
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
|
-
|
|
106
|
-
let
|
|
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 (!
|
|
116
|
-
|
|
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
|
-
|
|
121
|
-
break;
|
|
158
|
+
return new THREE.DataTexture(tmpBuffer, width, height, format, THREE.UnsignedByteType);
|
|
122
159
|
}
|
|
123
160
|
case THREE.FloatType: {
|
|
124
|
-
if (!
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
190
|
+
newBuffer[index + 3] = defaultAlpha; // A
|
|
151
191
|
}
|
|
152
192
|
|
|
153
|
-
return
|
|
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
|
|
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
|
|
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 =
|
|
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.
|
|
64
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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/
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|