@kitware/vtk.js 32.8.1 → 32.9.1
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/Common/Core/URLExtract.js +2 -6
- package/Filters/Core/ThresholdPoints.d.ts +72 -0
- package/Filters/Core/ThresholdPoints.js +219 -0
- package/Filters/General/ContourTriangulator/helper.js +1 -1
- package/IO/Geometry/DracoReader.d.ts +4 -4
- package/IO/Geometry/DracoReader.js +154 -105
- package/IO/Geometry/GLTFImporter/Extensions.js +1 -1
- package/IO/Geometry/GLTFImporter.d.ts +5 -5
- package/IO/Geometry/GLTFImporter.js +2 -2
- package/IO/Geometry/IFCImporter.js +10 -7
- package/Rendering/Misc/CanvasView.js +4 -2
- package/Rendering/Misc/RemoteView.d.ts +9 -3
- package/Rendering/Misc/RemoteView.js +7 -3
- package/Rendering/OpenGL/Texture/supportsNorm16Linear.js +97 -0
- package/Rendering/OpenGL/Texture.js +18 -11
- package/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -27,12 +27,8 @@ function extractURLParameters() {
|
|
|
27
27
|
let query = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : window.location.search;
|
|
28
28
|
const summary = {};
|
|
29
29
|
const convert = castToNativeType ? toNativeType : identity;
|
|
30
|
-
const
|
|
31
|
-
.
|
|
32
|
-
.split('&'); // extract token pair
|
|
33
|
-
|
|
34
|
-
queryTokens.forEach(token => {
|
|
35
|
-
const [key, value] = token.split('=').map(s => decodeURIComponent(s));
|
|
30
|
+
const params = new URLSearchParams(query);
|
|
31
|
+
params.forEach((value, key) => {
|
|
36
32
|
if (key) {
|
|
37
33
|
summary[key] = value ? convert(value) : true;
|
|
38
34
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { vtkAlgorithm, vtkObject } from './../../interfaces';
|
|
2
|
+
|
|
3
|
+
export interface ThresholdCriteria {
|
|
4
|
+
arrayName: string;
|
|
5
|
+
fieldAssociation: string;
|
|
6
|
+
operation: string;
|
|
7
|
+
value: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
*/
|
|
13
|
+
export interface IThresholdPointsInitialValues {
|
|
14
|
+
criterias?: ThresholdCriteria[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type vtkThresholdPointsBase = vtkObject & vtkAlgorithm;
|
|
18
|
+
|
|
19
|
+
export interface vtkThresholdPoints extends vtkThresholdPointsBase {
|
|
20
|
+
/**
|
|
21
|
+
* Get the desired precision for the output types.
|
|
22
|
+
*/
|
|
23
|
+
getCriterias(): ThresholdCriteria[];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Set the desired precision for the output types.
|
|
27
|
+
* @param outputPointsPrecision
|
|
28
|
+
*/
|
|
29
|
+
setCriterias(criterias: ThresholdCriteria[]): boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param inData
|
|
34
|
+
* @param outData
|
|
35
|
+
*/
|
|
36
|
+
requestData(inData: any, outData: any): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Method used to decorate a given object (publicAPI+model) with vtkThresholdPoints characteristics.
|
|
41
|
+
*
|
|
42
|
+
* @param publicAPI object on which methods will be bounds (public)
|
|
43
|
+
* @param model object on which data structure will be bounds (protected)
|
|
44
|
+
* @param {IThresholdPointsInitialValues} [initialValues] (default: {})
|
|
45
|
+
*/
|
|
46
|
+
export function extend(
|
|
47
|
+
publicAPI: object,
|
|
48
|
+
model: object,
|
|
49
|
+
initialValues?: IThresholdPointsInitialValues
|
|
50
|
+
): void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Method used to create a new instance of vtkThresholdPoints
|
|
54
|
+
* @param {IThresholdPointsInitialValues} [initialValues] for pre-setting some of its content
|
|
55
|
+
*/
|
|
56
|
+
export function newInstance(
|
|
57
|
+
initialValues?: IThresholdPointsInitialValues
|
|
58
|
+
): vtkThresholdPoints;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* vtkThresholdPoints - extracts points whose scalar value satisfies threshold criterion
|
|
62
|
+
*
|
|
63
|
+
* vtkThresholdPoints is a filter that extracts points from a dataset that
|
|
64
|
+
* satisfy a threshold criterion. The criterion can take three forms:
|
|
65
|
+
* 1) greater than a particular value; 2) less than a particular value; or
|
|
66
|
+
* 3) between a particular value. The output of the filter is polygonal data.
|
|
67
|
+
*/
|
|
68
|
+
export declare const vtkThresholdPoints: {
|
|
69
|
+
newInstance: typeof newInstance;
|
|
70
|
+
extend: typeof extend;
|
|
71
|
+
};
|
|
72
|
+
export default vtkThresholdPoints;
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { m as macro } from '../../macros2.js';
|
|
2
|
+
import vtkCellArray from '../../Common/Core/CellArray.js';
|
|
3
|
+
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
4
|
+
import vtkPoints from '../../Common/Core/Points.js';
|
|
5
|
+
import vtkPolyData from '../../Common/DataModel/PolyData.js';
|
|
6
|
+
import { POLYDATA_FIELDS } from '../../Common/DataModel/PolyData/Constants.js';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
vtkErrorMacro
|
|
10
|
+
} = macro;
|
|
11
|
+
const OperationType = {
|
|
12
|
+
Below: 'Below',
|
|
13
|
+
Above: 'Above'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Function to perform binary search on a sorted array
|
|
17
|
+
function binarySearch(items, value) {
|
|
18
|
+
let firstIndex = 0;
|
|
19
|
+
let lastIndex = items.length - 1;
|
|
20
|
+
let middleIndex = Math.floor((lastIndex + firstIndex) / 2);
|
|
21
|
+
while (items[middleIndex] !== value && firstIndex < lastIndex) {
|
|
22
|
+
if (value < items[middleIndex]) {
|
|
23
|
+
lastIndex = middleIndex - 1;
|
|
24
|
+
} else if (value > items[middleIndex]) {
|
|
25
|
+
firstIndex = middleIndex + 1;
|
|
26
|
+
}
|
|
27
|
+
middleIndex = Math.floor((lastIndex + firstIndex) / 2);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
found: items[middleIndex] === value,
|
|
31
|
+
index: Math.max(items[middleIndex] < value ? middleIndex + 1 : middleIndex, 0)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function camelize(str) {
|
|
35
|
+
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, letter => letter.toUpperCase()).replace(/\s+/g, '');
|
|
36
|
+
}
|
|
37
|
+
// ----------------------------------------------------------------------------
|
|
38
|
+
// vtkThresholdPoints methods
|
|
39
|
+
// ----------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
function vtkThresholdPoints(publicAPI, model) {
|
|
42
|
+
// Set our classname
|
|
43
|
+
model.classHierarchy.push('vtkThresholdPoints');
|
|
44
|
+
publicAPI.requestData = (inData, outData) => {
|
|
45
|
+
const input = inData[0];
|
|
46
|
+
const output = vtkPolyData.newInstance();
|
|
47
|
+
outData[0] = output;
|
|
48
|
+
if (model.criterias.length === 0) {
|
|
49
|
+
output.shallowCopy(input);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const oldPoints = input.getPoints();
|
|
53
|
+
const oldPointCount = oldPoints.getNumberOfPoints();
|
|
54
|
+
const oldPointData = input.getPointData();
|
|
55
|
+
const oldPointsData = oldPoints.getData();
|
|
56
|
+
const newPointsData = macro.newTypedArray(input.getPoints().getDataType(), 3 * oldPointCount);
|
|
57
|
+
const oldArrays = [];
|
|
58
|
+
const newArraysData = [];
|
|
59
|
+
const numArrays = oldPointData.getNumberOfArrays();
|
|
60
|
+
for (let i = 0; i < numArrays; ++i) {
|
|
61
|
+
const oldArray = oldPointData.getArrayByIndex(i);
|
|
62
|
+
oldArrays.push(oldArray);
|
|
63
|
+
newArraysData.push(macro.newTypedArray(oldArray.getDataType(), oldPointCount * oldArray.getNumberOfComponents()));
|
|
64
|
+
}
|
|
65
|
+
const pointAcceptanceFunctions = model.criterias.map(criteria => {
|
|
66
|
+
let inputArray = null;
|
|
67
|
+
let component = 0;
|
|
68
|
+
let numberOfComponents = 1;
|
|
69
|
+
if (criteria.fieldAssociation === 'PointData') {
|
|
70
|
+
inputArray = oldArrays.find(oldArray => oldArray.getName() === criteria.arrayName);
|
|
71
|
+
numberOfComponents = inputArray.getNumberOfComponents();
|
|
72
|
+
} else if (criteria.fieldAssociation === 'Points') {
|
|
73
|
+
inputArray = oldPoints;
|
|
74
|
+
if (criteria.arrayName === 'z') {
|
|
75
|
+
component = 2;
|
|
76
|
+
} else {
|
|
77
|
+
component = criteria.arrayName === 'y' ? 1 : 0;
|
|
78
|
+
}
|
|
79
|
+
numberOfComponents = 3;
|
|
80
|
+
} else {
|
|
81
|
+
vtkErrorMacro('No field association');
|
|
82
|
+
}
|
|
83
|
+
const inputArrayData = inputArray.getData();
|
|
84
|
+
const operation = criteria.operation === OperationType.Below ? (a, b) => a < b : (a, b) => a > b;
|
|
85
|
+
const pointAcceptanceFunction = pointId => operation(inputArrayData[numberOfComponents * pointId + component], criteria.value);
|
|
86
|
+
return pointAcceptanceFunction;
|
|
87
|
+
});
|
|
88
|
+
const thresholdedPointIds = []; // sorted list
|
|
89
|
+
let newI = 0;
|
|
90
|
+
for (let i = 0; i < oldPointCount; ++i) {
|
|
91
|
+
const keepPoint = pointAcceptanceFunctions.reduce((keep, pointAcceptanceFunction) => keep && pointAcceptanceFunction(i), true);
|
|
92
|
+
if (keepPoint) {
|
|
93
|
+
let ii = 3 * i;
|
|
94
|
+
let newII = 3 * newI;
|
|
95
|
+
for (let c = 0; c < 3; ++c) {
|
|
96
|
+
newPointsData[newII++] = oldPointsData[ii++];
|
|
97
|
+
}
|
|
98
|
+
for (let j = 0; j < numArrays; ++j) {
|
|
99
|
+
const oldArrayData = oldArrays[j].getData();
|
|
100
|
+
const newArrayData = newArraysData[j];
|
|
101
|
+
const cc = oldArrays[j].getNumberOfComponents();
|
|
102
|
+
ii = cc * i;
|
|
103
|
+
newII = cc * newI;
|
|
104
|
+
for (let c = 0; c < cc; ++c) {
|
|
105
|
+
newArrayData[newII++] = oldArrayData[ii++];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
++newI;
|
|
109
|
+
} else {
|
|
110
|
+
thresholdedPointIds.push(i);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (thresholdedPointIds.length === 0) {
|
|
114
|
+
output.shallowCopy(input);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
output.setPoints(vtkPoints.newInstance({
|
|
118
|
+
values: newPointsData,
|
|
119
|
+
size: 3 * newI
|
|
120
|
+
}));
|
|
121
|
+
for (let i = 0; i < numArrays; ++i) {
|
|
122
|
+
const oldArray = oldArrays[i];
|
|
123
|
+
const newArray = vtkDataArray.newInstance({
|
|
124
|
+
name: oldArray.getName(),
|
|
125
|
+
values: newArraysData[i],
|
|
126
|
+
dataType: oldArray.getDataType(),
|
|
127
|
+
numberOfComponents: oldArray.getNumberOfComponents(),
|
|
128
|
+
size: newI * oldArray.getNumberOfComponents()
|
|
129
|
+
});
|
|
130
|
+
output.getPointData().addArray(newArray);
|
|
131
|
+
oldPointData.getAttributes(oldArray).forEach(attrType => {
|
|
132
|
+
output.getPointData().setAttribute(newArray, attrType);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
POLYDATA_FIELDS.forEach(cellType => {
|
|
136
|
+
const oldPolysData = input[`get${camelize(cellType)}`]().getData();
|
|
137
|
+
const newCellData = macro.newTypedArray(input.getPolys().getDataType(), oldPolysData.length);
|
|
138
|
+
const newPointIds = []; // first point starts at [1]
|
|
139
|
+
const firstPointIndex = cellType === 'verts' ? 0 : 1;
|
|
140
|
+
let numberOfPoints = 1;
|
|
141
|
+
let newP = 0;
|
|
142
|
+
for (let c = 0; c < oldPolysData.length; c += numberOfPoints + firstPointIndex) {
|
|
143
|
+
if (firstPointIndex === 1) {
|
|
144
|
+
// not for verts
|
|
145
|
+
numberOfPoints = oldPolysData[c];
|
|
146
|
+
}
|
|
147
|
+
let keepCell = true;
|
|
148
|
+
for (let p = firstPointIndex; p <= numberOfPoints; ++p) {
|
|
149
|
+
const {
|
|
150
|
+
found,
|
|
151
|
+
index
|
|
152
|
+
} = binarySearch(thresholdedPointIds, oldPolysData[c + p]);
|
|
153
|
+
if (found) {
|
|
154
|
+
keepCell = false;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
newPointIds[p] = oldPolysData[c + p] - index;
|
|
158
|
+
}
|
|
159
|
+
if (keepCell) {
|
|
160
|
+
newCellData[newP++] = numberOfPoints;
|
|
161
|
+
for (let p = firstPointIndex; p <= numberOfPoints;) {
|
|
162
|
+
newCellData[newP++] = newPointIds[p++];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
output[`set${camelize(cellType)}`](vtkCellArray.newInstance({
|
|
167
|
+
values: newCellData,
|
|
168
|
+
size: newP,
|
|
169
|
+
// it may shorter than original array if cells are not kept
|
|
170
|
+
dataType: input.getPolys().getDataType()
|
|
171
|
+
}));
|
|
172
|
+
});
|
|
173
|
+
outData[0] = output;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ----------------------------------------------------------------------------
|
|
178
|
+
|
|
179
|
+
function defaultValues(publicAPI, model) {
|
|
180
|
+
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
181
|
+
return {
|
|
182
|
+
criterias: [],
|
|
183
|
+
// arrayName: string, fieldAssociation: string, operation: string, value: number
|
|
184
|
+
...initialValues
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ----------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
function extend(publicAPI, model) {
|
|
191
|
+
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
192
|
+
Object.assign(model, defaultValues(publicAPI, model, initialValues));
|
|
193
|
+
|
|
194
|
+
// Build VTK API
|
|
195
|
+
macro.setGet(publicAPI, model, []);
|
|
196
|
+
macro.get(publicAPI, model, []);
|
|
197
|
+
macro.setGetArray(publicAPI, model, ['criterias']);
|
|
198
|
+
|
|
199
|
+
// Make this a VTK object
|
|
200
|
+
macro.obj(publicAPI, model);
|
|
201
|
+
macro.algo(publicAPI, model, 1, 1);
|
|
202
|
+
|
|
203
|
+
// Object specific methods
|
|
204
|
+
vtkThresholdPoints(publicAPI, model);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ----------------------------------------------------------------------------
|
|
208
|
+
|
|
209
|
+
const newInstance = macro.newInstance(extend, 'vtkThresholdPoints');
|
|
210
|
+
|
|
211
|
+
// ----------------------------------------------------------------------------
|
|
212
|
+
|
|
213
|
+
var index = {
|
|
214
|
+
newInstance,
|
|
215
|
+
extend,
|
|
216
|
+
OperationType
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export { index as default, extend, newInstance };
|
|
@@ -1747,11 +1747,11 @@ function vtkCCSCutHoleyPolys(polys, points, polyGroups, polyEdges, normal) {
|
|
|
1747
1747
|
// Do a quick search first, then do an exhaustive search.
|
|
1748
1748
|
let madeCut = 0;
|
|
1749
1749
|
let inner = 0;
|
|
1750
|
+
const cuts = [[0, 0], [0, 0]];
|
|
1750
1751
|
for (let exhaustive = 0; exhaustive < 2 && !madeCut; exhaustive++) {
|
|
1751
1752
|
for (let j = 1; j < polyGroup.length; j++) {
|
|
1752
1753
|
inner = innerBySize[j][1];
|
|
1753
1754
|
innerPolyId = polyGroup[inner];
|
|
1754
|
-
const cuts = [];
|
|
1755
1755
|
if (vtkCCSFindCuts(polys, polyGroup, outerPolyId, innerPolyId, points, normal, cuts, exhaustive)) {
|
|
1756
1756
|
vtkCCSMakeCuts(polys, polyEdges, outerPolyId, innerPolyId, points, cuts);
|
|
1757
1757
|
madeCut = 1;
|
|
@@ -118,15 +118,15 @@ export function newInstance(
|
|
|
118
118
|
): vtkDracoReader;
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
*
|
|
121
|
+
* Get the draco decoder
|
|
122
122
|
*/
|
|
123
123
|
export function getDracoDecoder(): any;
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
*
|
|
127
|
-
* @param
|
|
126
|
+
* Set the draco decoder
|
|
127
|
+
* @param dracoDecoder
|
|
128
128
|
*/
|
|
129
|
-
export function setDracoDecoder(
|
|
129
|
+
export function setDracoDecoder(dracoDecoder: any): Promise<any>;
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* Load the WASM decoder from url and set the decoderModule
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import DataAccessHelper from '../Core/DataAccessHelper.js';
|
|
2
1
|
import { m as macro } from '../../macros2.js';
|
|
2
|
+
import DataAccessHelper from '../Core/DataAccessHelper.js';
|
|
3
3
|
import vtkCellArray from '../../Common/Core/CellArray.js';
|
|
4
4
|
import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
5
5
|
import vtkPolyData from '../../Common/DataModel/PolyData.js';
|
|
6
|
+
import vtkPolyDataNormals from '../../Filters/Core/PolyDataNormals.js';
|
|
6
7
|
import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
|
|
7
8
|
|
|
8
9
|
// import 'vtk.js/Sources/IO/Core/DataAccessHelper/HttpDataAccessHelper'; // HTTP + zip
|
|
@@ -12,8 +13,7 @@ import '../Core/DataAccessHelper/LiteHttpDataAccessHelper.js';
|
|
|
12
13
|
const {
|
|
13
14
|
vtkErrorMacro
|
|
14
15
|
} = macro;
|
|
15
|
-
let decoderModule =
|
|
16
|
-
|
|
16
|
+
let decoderModule = null;
|
|
17
17
|
// ----------------------------------------------------------------------------
|
|
18
18
|
// static methods
|
|
19
19
|
// ----------------------------------------------------------------------------
|
|
@@ -46,8 +46,13 @@ function setWasmBinary(url, binaryName) {
|
|
|
46
46
|
xhr.send(null);
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set the Draco decoder module
|
|
52
|
+
* @param {*} dracoDecoder
|
|
53
|
+
*/
|
|
54
|
+
async function setDracoDecoder(dracoDecoder) {
|
|
55
|
+
decoderModule = await dracoDecoder({});
|
|
51
56
|
}
|
|
52
57
|
function getDracoDecoder() {
|
|
53
58
|
return decoderModule;
|
|
@@ -56,115 +61,141 @@ function getDracoDecoder() {
|
|
|
56
61
|
// ----------------------------------------------------------------------------
|
|
57
62
|
// vtkDracoReader methods
|
|
58
63
|
// ----------------------------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
function getDracoDataType(attributeType) {
|
|
65
|
+
switch (attributeType) {
|
|
66
|
+
case Float32Array:
|
|
67
|
+
return decoderModule.DT_FLOAT32;
|
|
68
|
+
case Int8Array:
|
|
69
|
+
return decoderModule.DT_INT8;
|
|
70
|
+
case Int16Array:
|
|
71
|
+
return decoderModule.DT_INT16;
|
|
72
|
+
case Int32Array:
|
|
73
|
+
return decoderModule.DT_INT32;
|
|
74
|
+
case Uint8Array:
|
|
75
|
+
return decoderModule.DT_UINT8;
|
|
76
|
+
case Uint16Array:
|
|
77
|
+
return decoderModule.DT_UINT16;
|
|
78
|
+
case Uint32Array:
|
|
79
|
+
return decoderModule.DT_UINT32;
|
|
80
|
+
default:
|
|
81
|
+
return decoderModule.DT_FLOAT32;
|
|
75
82
|
}
|
|
76
|
-
decoderModule.destroy(decoderBuffer);
|
|
77
|
-
decoderModule.destroy(decoder);
|
|
78
|
-
return dracoGeometry;
|
|
79
83
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Decode a single attribute
|
|
87
|
+
* @param {*} decoder The Draco decoder
|
|
88
|
+
* @param {*} dracoGeometry The geometry to decode
|
|
89
|
+
* @param {*} attributeName The name of the attribute
|
|
90
|
+
* @param {*} attributeType The type of the attribute
|
|
91
|
+
* @param {*} attribute The attribute to decode
|
|
92
|
+
* @returns object with name, array, itemSize
|
|
93
|
+
*/
|
|
94
|
+
function decodeAttribute(decoder, dracoGeometry, attributeName, attributeType, attribute) {
|
|
95
|
+
const numComponents = attribute.num_components();
|
|
96
|
+
const numPoints = dracoGeometry.num_points();
|
|
97
|
+
const numValues = numPoints * numComponents;
|
|
98
|
+
const byteLength = numValues * attributeType.BYTES_PER_ELEMENT;
|
|
99
|
+
const dataType = getDracoDataType(attributeType);
|
|
100
|
+
const ptr = decoderModule._malloc(byteLength);
|
|
101
|
+
decoder.GetAttributeDataArrayForAllPoints(dracoGeometry, attribute, dataType, byteLength, ptr);
|
|
102
|
+
|
|
103
|
+
// eslint-disable-next-line new-cap
|
|
104
|
+
const array = new attributeType(decoderModule.HEAPF32.buffer, ptr, numValues).slice();
|
|
105
|
+
decoderModule._free(ptr);
|
|
106
|
+
return {
|
|
107
|
+
name: attributeName,
|
|
108
|
+
array,
|
|
109
|
+
itemSize: numComponents
|
|
110
|
+
};
|
|
93
111
|
}
|
|
94
|
-
function getPolyDataFromDracoGeometry(dracoGeometry) {
|
|
95
|
-
const decoder = new decoderModule.Decoder();
|
|
96
112
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const
|
|
113
|
+
/**
|
|
114
|
+
* Decode the indices of the geometry
|
|
115
|
+
* @param {*} decoder The Draco decoder
|
|
116
|
+
* @param {*} dracoGeometry The geometry to decode
|
|
117
|
+
* @returns The indices array of the geometry
|
|
118
|
+
*/
|
|
119
|
+
function decodeIndices(decoder, dracoGeometry) {
|
|
120
|
+
const numFaces = dracoGeometry.num_faces();
|
|
121
|
+
const numIndices = numFaces * 3;
|
|
122
|
+
const byteLength = numIndices * 4;
|
|
123
|
+
const ptr = decoderModule._malloc(byteLength);
|
|
124
|
+
decoder.GetTrianglesUInt32Array(dracoGeometry, byteLength, ptr);
|
|
125
|
+
const indices = new Uint32Array(decoderModule.HEAPF32.buffer, ptr, numIndices).slice();
|
|
126
|
+
decoderModule._free(ptr);
|
|
127
|
+
return indices;
|
|
128
|
+
}
|
|
106
129
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Get the polyData from the Draco geometry
|
|
132
|
+
* @param {*} decoder The Draco decoder
|
|
133
|
+
* @param {*} dracoGeometry The geometry to decode
|
|
134
|
+
* @returns {vtkPolyData} The polyData of the geometry
|
|
135
|
+
*/
|
|
136
|
+
function getPolyDataFromDracoGeometry(decoder, dracoGeometry) {
|
|
137
|
+
const indices = decodeIndices(decoder, dracoGeometry);
|
|
138
|
+
const nCells = indices.length - 2;
|
|
139
|
+
const cells = vtkCellArray.newInstance();
|
|
140
|
+
cells.resize(4 * indices.length / 3);
|
|
141
|
+
for (let cellId = 0; cellId < nCells; cellId += 3) {
|
|
142
|
+
const cell = indices.slice(cellId, cellId + 3);
|
|
143
|
+
cells.insertNextCell(cell);
|
|
118
144
|
}
|
|
119
|
-
|
|
120
|
-
// Create polyData and add positions and indinces
|
|
121
|
-
const cellArray = vtkCellArray.newInstance({
|
|
122
|
-
values: indices
|
|
123
|
-
});
|
|
124
145
|
const polyData = vtkPolyData.newInstance({
|
|
125
|
-
polys:
|
|
146
|
+
polys: cells
|
|
126
147
|
});
|
|
127
|
-
polyData.getPoints().setData(positionArray);
|
|
128
|
-
|
|
129
|
-
// Look for other attributes
|
|
130
|
-
const pointData = polyData.getPointData();
|
|
131
148
|
|
|
132
|
-
//
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
149
|
+
// Look for attributes
|
|
150
|
+
const attributeIDs = {
|
|
151
|
+
points: 'POSITION',
|
|
152
|
+
normals: 'NORMAL',
|
|
153
|
+
scalars: 'COLOR',
|
|
154
|
+
tcoords: 'TEX_COORD'
|
|
155
|
+
};
|
|
156
|
+
Object.keys(attributeIDs).forEach(attributeName => {
|
|
157
|
+
const attributeType = Float32Array;
|
|
158
|
+
const attributeID = decoder.GetAttributeId(dracoGeometry, decoderModule[attributeIDs[attributeName]]);
|
|
159
|
+
if (attributeID === -1) return;
|
|
160
|
+
const attribute = decoder.GetAttribute(dracoGeometry, attributeID);
|
|
161
|
+
const attributeResult = decodeAttribute(decoder, dracoGeometry, attributeName, attributeType, attribute);
|
|
162
|
+
const pointData = polyData.getPointData();
|
|
163
|
+
switch (attributeName) {
|
|
164
|
+
case 'points':
|
|
165
|
+
polyData.getPoints().setData(attributeResult.array, attributeResult.itemSize);
|
|
166
|
+
break;
|
|
167
|
+
case 'normals':
|
|
168
|
+
pointData.setNormals(vtkDataArray.newInstance({
|
|
169
|
+
numberOfComponents: attributeResult.itemSize,
|
|
170
|
+
values: attributeResult.array,
|
|
171
|
+
name: 'Normals'
|
|
172
|
+
}));
|
|
173
|
+
break;
|
|
174
|
+
case 'scalars':
|
|
175
|
+
pointData.setScalars(vtkDataArray.newInstance({
|
|
176
|
+
numberOfComponents: attributeResult.itemSize,
|
|
177
|
+
values: attributeResult.array,
|
|
178
|
+
name: 'Scalars'
|
|
179
|
+
}));
|
|
180
|
+
break;
|
|
181
|
+
case 'tcoords':
|
|
182
|
+
pointData.setTCoords(vtkDataArray.newInstance({
|
|
183
|
+
numberOfComponents: attributeResult.itemSize,
|
|
184
|
+
values: attributeResult.array,
|
|
185
|
+
name: 'TCoords'
|
|
186
|
+
}));
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
155
190
|
|
|
156
|
-
//
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
name: 'Scalars'
|
|
164
|
-
});
|
|
165
|
-
pointData.setScalars(scalars);
|
|
191
|
+
// we will generate normals if they're missing
|
|
192
|
+
const hasNormals = polyData.getPointData().getNormals();
|
|
193
|
+
if (!hasNormals) {
|
|
194
|
+
const pdn = vtkPolyDataNormals.newInstance();
|
|
195
|
+
pdn.setInputData(polyData);
|
|
196
|
+
pdn.setComputePointNormals(true);
|
|
197
|
+
return pdn.getOutputData();
|
|
166
198
|
}
|
|
167
|
-
decoderModule.destroy(decoder);
|
|
168
199
|
return polyData;
|
|
169
200
|
}
|
|
170
201
|
function vtkDracoReader(publicAPI, model) {
|
|
@@ -235,9 +266,27 @@ function vtkDracoReader(publicAPI, model) {
|
|
|
235
266
|
return;
|
|
236
267
|
}
|
|
237
268
|
model.parseData = content;
|
|
238
|
-
const
|
|
239
|
-
const
|
|
269
|
+
const byteArray = new Int8Array(content);
|
|
270
|
+
const decoder = new decoderModule.Decoder();
|
|
271
|
+
const buffer = new decoderModule.DecoderBuffer();
|
|
272
|
+
buffer.Init(byteArray, byteArray.length);
|
|
273
|
+
const geometryType = decoder.GetEncodedGeometryType(buffer);
|
|
274
|
+
let dracoGeometry;
|
|
275
|
+
if (geometryType === decoderModule.TRIANGULAR_MESH) {
|
|
276
|
+
dracoGeometry = new decoderModule.Mesh();
|
|
277
|
+
const status = decoder.DecodeBufferToMesh(buffer, dracoGeometry);
|
|
278
|
+
if (!status.ok()) {
|
|
279
|
+
vtkErrorMacro(`Could not decode Draco file: ${status.error_msg()}`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
vtkErrorMacro('Wrong geometry type, expected mesh, got point cloud.');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
const polyData = getPolyDataFromDracoGeometry(decoder, dracoGeometry);
|
|
240
287
|
decoderModule.destroy(dracoGeometry);
|
|
288
|
+
decoderModule.destroy(buffer);
|
|
289
|
+
decoderModule.destroy(decoder);
|
|
241
290
|
model.output[0] = polyData;
|
|
242
291
|
};
|
|
243
292
|
publicAPI.requestData = () => {
|
|
@@ -91,7 +91,7 @@ function handleKHRLightsPunctual(extension, transformMatrix, model) {
|
|
|
91
91
|
*
|
|
92
92
|
* @param {object} extension - The KHR_draco_mesh_compression extension object.
|
|
93
93
|
*/
|
|
94
|
-
|
|
94
|
+
function handleKHRDracoMeshCompression(extension) {
|
|
95
95
|
const reader = vtkDracoReader.newInstance();
|
|
96
96
|
reader.parse(extension.bufferView);
|
|
97
97
|
return reader.getOutputData();
|
|
@@ -183,25 +183,25 @@ export interface vtkGLTFImporter extends vtkGLTFImporterBase {
|
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
185
|
* Set the camera id.
|
|
186
|
-
* @param cameraId
|
|
186
|
+
* @param cameraId The camera id.
|
|
187
187
|
*/
|
|
188
188
|
setCamera(cameraId: string): void;
|
|
189
189
|
|
|
190
190
|
/**
|
|
191
191
|
* Set the Draco decoder.
|
|
192
|
-
* @param
|
|
192
|
+
* @param dracoDecoder
|
|
193
193
|
*/
|
|
194
|
-
setDracoDecoder(
|
|
194
|
+
setDracoDecoder(dracoDecoder: any): void;
|
|
195
195
|
|
|
196
196
|
/**
|
|
197
197
|
* Set the vtk Renderer.
|
|
198
|
-
* @param renderer
|
|
198
|
+
* @param renderer The vtk Renderer.
|
|
199
199
|
*/
|
|
200
200
|
setRenderer(renderer: vtkRenderer): void;
|
|
201
201
|
|
|
202
202
|
/**
|
|
203
203
|
* Switch to a variant.
|
|
204
|
-
* @param variantIndex
|
|
204
|
+
* @param variantIndex The index of the variant to switch to.
|
|
205
205
|
*/
|
|
206
206
|
switchToVariant(variantIndex: number): void;
|
|
207
207
|
}
|
|
@@ -133,8 +133,8 @@ function vtkGLTFImporter(publicAPI, model) {
|
|
|
133
133
|
publicAPI.requestData = (inData, outData) => {
|
|
134
134
|
publicAPI.parse(model.parseData);
|
|
135
135
|
};
|
|
136
|
-
publicAPI.setDracoDecoder =
|
|
137
|
-
vtkDracoReader.setDracoDecoder(
|
|
136
|
+
publicAPI.setDracoDecoder = async dracoDecoder => {
|
|
137
|
+
await vtkDracoReader.setDracoDecoder(dracoDecoder);
|
|
138
138
|
};
|
|
139
139
|
publicAPI.importActors = () => {
|
|
140
140
|
// Add actors to renderer
|
|
@@ -8,6 +8,7 @@ import vtkPolyData from '../../Common/DataModel/PolyData.js';
|
|
|
8
8
|
import vtkCellArray from '../../Common/Core/CellArray.js';
|
|
9
9
|
import vtkAppendPolyData from '../../Filters/General/AppendPolyData.js';
|
|
10
10
|
import vtkMatrixBuilder from '../../Common/Core/MatrixBuilder.js';
|
|
11
|
+
import { mat3 } from 'gl-matrix';
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
14
|
vtkErrorMacro
|
|
@@ -82,10 +83,12 @@ function vtkIFCImporter(publicAPI, model) {
|
|
|
82
83
|
const colorArray = new Float32Array(vertices.length / 2);
|
|
83
84
|
if (userMatrix) {
|
|
84
85
|
const transformMatrix = vtkMatrixBuilder.buildFromRadian().setMatrix(userMatrix);
|
|
86
|
+
const normalMatrix = vtkMatrixBuilder.buildFromRadian().multiply3x3(mat3.fromMat4(mat3.create(), userMatrix));
|
|
85
87
|
for (let i = 0; i < vertices.length; i += 6) {
|
|
86
88
|
const point = [vertices[i], vertices[i + 1], vertices[i + 2]];
|
|
87
89
|
const normal = [vertices[i + 3], vertices[i + 4], vertices[i + 5]];
|
|
88
|
-
transformMatrix.apply(point)
|
|
90
|
+
transformMatrix.apply(point);
|
|
91
|
+
normalMatrix.apply(normal);
|
|
89
92
|
pointValues[i / 2] = point[0];
|
|
90
93
|
pointValues[i / 2 + 1] = point[1];
|
|
91
94
|
pointValues[i / 2 + 2] = point[2];
|
|
@@ -199,26 +202,26 @@ function vtkIFCImporter(publicAPI, model) {
|
|
|
199
202
|
publicAPI.importActors = renderer => {
|
|
200
203
|
if (model.mergeGeometries) {
|
|
201
204
|
const opaqueMeshes = meshes.filter(mesh => mesh.color.w === 1);
|
|
202
|
-
|
|
205
|
+
const oapd = vtkAppendPolyData.newInstance();
|
|
203
206
|
opaqueMeshes.forEach(mesh => {
|
|
204
207
|
const pd = createColoredPolyDataFromIFCMesh(mesh);
|
|
205
|
-
|
|
208
|
+
oapd.addInputData(pd);
|
|
206
209
|
});
|
|
207
210
|
let mapper = vtkMapper.newInstance();
|
|
208
211
|
mapper.setColorModeToDirectScalars();
|
|
209
|
-
mapper.setInputConnection(
|
|
212
|
+
mapper.setInputConnection(oapd.getOutputPort());
|
|
210
213
|
let actor = vtkActor.newInstance();
|
|
211
214
|
actor.setMapper(mapper);
|
|
212
215
|
renderer.addActor(actor);
|
|
213
216
|
const transparentMeshes = meshes.filter(mesh => mesh.color.w < 1);
|
|
214
|
-
|
|
217
|
+
const tapd = vtkAppendPolyData.newInstance();
|
|
215
218
|
transparentMeshes.forEach(mesh => {
|
|
216
219
|
const pd = createColoredPolyDataFromIFCMesh(mesh);
|
|
217
|
-
|
|
220
|
+
tapd.addInputData(pd);
|
|
218
221
|
});
|
|
219
222
|
mapper = vtkMapper.newInstance();
|
|
220
223
|
mapper.setColorModeToDirectScalars();
|
|
221
|
-
mapper.setInputConnection(
|
|
224
|
+
mapper.setInputConnection(tapd.getOutputPort());
|
|
222
225
|
actor = vtkActor.newInstance();
|
|
223
226
|
actor.setMapper(mapper);
|
|
224
227
|
actor.getProperty().setOpacity(0.5);
|
|
@@ -134,8 +134,10 @@ function extend(publicAPI, model) {
|
|
|
134
134
|
Object.assign(model, DEFAULT_VALUES, initialValues);
|
|
135
135
|
|
|
136
136
|
// Create internal instances
|
|
137
|
-
model.canvas
|
|
138
|
-
|
|
137
|
+
if (!model.canvas) {
|
|
138
|
+
model.canvas = document.createElement('canvas');
|
|
139
|
+
model.canvas.style.width = '100%';
|
|
140
|
+
}
|
|
139
141
|
|
|
140
142
|
// Create internal bgImage
|
|
141
143
|
model.bgImage = new Image();
|
|
@@ -13,21 +13,27 @@ interface IRemoteViewInitialValues {
|
|
|
13
13
|
rpcGestureEvent?: any;
|
|
14
14
|
rpcWheelEvent?: any;
|
|
15
15
|
viewStream?: vtkViewStream;
|
|
16
|
+
canvasElement?: HTMLCanvasElement;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export interface vtkRemoteView extends vtkObject {
|
|
19
20
|
/**
|
|
20
|
-
* Get container element
|
|
21
|
+
* Get container HTML element
|
|
21
22
|
*/
|
|
22
23
|
getContainer(): HTMLElement;
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
|
-
*
|
|
26
|
+
* Get vtkViewStream object
|
|
26
27
|
*/
|
|
27
28
|
getViewStream(): vtkViewStream;
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
|
-
*
|
|
31
|
+
* Get the canvas HTML element
|
|
32
|
+
*/
|
|
33
|
+
getCanvasElement(): HTMLCanvasElement;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get the vtkCanvasView object
|
|
31
37
|
*/
|
|
32
38
|
getCanvasView(): vtkCanvasView;
|
|
33
39
|
|
|
@@ -17,7 +17,9 @@ function vtkRemoteView(publicAPI, model) {
|
|
|
17
17
|
model.classHierarchy.push('vtkRemoteView');
|
|
18
18
|
|
|
19
19
|
// Constructor
|
|
20
|
-
model.canvasView = vtkCanvasView.newInstance(
|
|
20
|
+
model.canvasView = vtkCanvasView.newInstance({
|
|
21
|
+
canvas: model.canvasElement
|
|
22
|
+
});
|
|
21
23
|
model.interactorStyle = vtkInteractorStyleRemoteMouse.newInstance();
|
|
22
24
|
model.interactor = vtkRenderWindowInteractor.newInstance();
|
|
23
25
|
model.interactor.setView(model.canvasView);
|
|
@@ -60,6 +62,7 @@ function vtkRemoteView(publicAPI, model) {
|
|
|
60
62
|
model.viewStream.delete();
|
|
61
63
|
}
|
|
62
64
|
}, publicAPI.delete);
|
|
65
|
+
publicAPI.getCanvasElement = () => model.canvasView.getCanvas();
|
|
63
66
|
|
|
64
67
|
// --------------------------------------------------------------------------
|
|
65
68
|
// remote handing
|
|
@@ -194,7 +197,8 @@ const DEFAULT_VALUES = {
|
|
|
194
197
|
stillRatio: 1,
|
|
195
198
|
rpcMouseEvent: 'viewport.mouse.interaction',
|
|
196
199
|
rpcGestureEvent: null,
|
|
197
|
-
rpcWheelEvent: null
|
|
200
|
+
rpcWheelEvent: null,
|
|
201
|
+
canvasElement: null
|
|
198
202
|
};
|
|
199
203
|
|
|
200
204
|
// ----------------------------------------------------------------------------
|
|
@@ -203,7 +207,7 @@ function extend(publicAPI, model) {
|
|
|
203
207
|
let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
204
208
|
Object.assign(model, DEFAULT_VALUES, initialValues);
|
|
205
209
|
macro.obj(publicAPI, model, initialValues);
|
|
206
|
-
macro.get(publicAPI, model, ['container', 'viewStream', 'canvasView', 'interactor', 'interactorStyle', 'interactiveQuality', 'interactiveRatio', 'stillQuality', 'stillRatio']);
|
|
210
|
+
macro.get(publicAPI, model, ['container', 'viewStream', 'canvasView', 'interactor', 'interactorStyle', 'interactiveQuality', 'interactiveRatio', 'stillQuality', 'stillRatio', 'canvasElement']);
|
|
207
211
|
macro.setGet(publicAPI, model, ['session', 'rpcMouseEvent', 'rpcGestureEvent', 'rpcWheelEvent']);
|
|
208
212
|
|
|
209
213
|
// Object methods
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Even when the EXT_texture_norm16 extension is present, linear filtering
|
|
3
|
+
* might not be supported for normalized fixed point textures.
|
|
4
|
+
*
|
|
5
|
+
* This is a driver bug. See https://github.com/KhronosGroup/WebGL/issues/3706
|
|
6
|
+
* @return {boolean}
|
|
7
|
+
*/
|
|
8
|
+
function supportsNorm16Linear() {
|
|
9
|
+
try {
|
|
10
|
+
const canvasSize = 4;
|
|
11
|
+
const texWidth = 2;
|
|
12
|
+
const texHeight = 1;
|
|
13
|
+
const texData = new Int16Array([0, 2 ** 15 - 1]);
|
|
14
|
+
const pixelToCheck = [1, 1];
|
|
15
|
+
const canvas = document.createElement('canvas');
|
|
16
|
+
canvas.width = canvasSize;
|
|
17
|
+
canvas.height = canvasSize;
|
|
18
|
+
const gl = canvas.getContext('webgl2');
|
|
19
|
+
if (!gl) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const ext = gl.getExtension('EXT_texture_norm16');
|
|
23
|
+
if (!ext) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const vs = `#version 300 es
|
|
27
|
+
void main() {
|
|
28
|
+
gl_PointSize = ${canvasSize.toFixed(1)};
|
|
29
|
+
gl_Position = vec4(0, 0, 0, 1);
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
const fs = `#version 300 es
|
|
33
|
+
precision highp float;
|
|
34
|
+
precision highp int;
|
|
35
|
+
precision highp sampler2D;
|
|
36
|
+
|
|
37
|
+
uniform sampler2D u_image;
|
|
38
|
+
|
|
39
|
+
out vec4 color;
|
|
40
|
+
|
|
41
|
+
void main() {
|
|
42
|
+
vec4 intColor = texture(u_image, gl_PointCoord.xy);
|
|
43
|
+
color = vec4(vec3(intColor.rrr), 1);
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
|
47
|
+
gl.shaderSource(vertexShader, vs);
|
|
48
|
+
gl.compileShader(vertexShader);
|
|
49
|
+
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
|
53
|
+
gl.shaderSource(fragmentShader, fs);
|
|
54
|
+
gl.compileShader(fragmentShader);
|
|
55
|
+
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const program = gl.createProgram();
|
|
59
|
+
gl.attachShader(program, vertexShader);
|
|
60
|
+
gl.attachShader(program, fragmentShader);
|
|
61
|
+
gl.linkProgram(program);
|
|
62
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const tex = gl.createTexture();
|
|
66
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
67
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, ext.R16_SNORM_EXT, texWidth, texHeight, 0, gl.RED, gl.SHORT, texData);
|
|
68
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
69
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
70
|
+
gl.useProgram(program);
|
|
71
|
+
gl.drawArrays(gl.POINTS, 0, 1);
|
|
72
|
+
const pixel = new Uint8Array(4);
|
|
73
|
+
gl.readPixels(pixelToCheck[0], pixelToCheck[1], 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
|
|
74
|
+
const [r, g, b] = pixel;
|
|
75
|
+
const webglLoseContext = gl.getExtension('WEBGL_lose_context');
|
|
76
|
+
if (webglLoseContext) {
|
|
77
|
+
webglLoseContext.loseContext();
|
|
78
|
+
}
|
|
79
|
+
return r === g && g === b && r !== 0;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @type {boolean | undefined}
|
|
87
|
+
*/
|
|
88
|
+
let supportsNorm16LinearCache;
|
|
89
|
+
function supportsNorm16LinearCached() {
|
|
90
|
+
// Only create a canvas+texture+shaders the first time
|
|
91
|
+
if (supportsNorm16LinearCache === undefined) {
|
|
92
|
+
supportsNorm16LinearCache = supportsNorm16Linear();
|
|
93
|
+
}
|
|
94
|
+
return supportsNorm16LinearCache;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export { supportsNorm16LinearCached as default };
|
|
@@ -5,6 +5,7 @@ import vtkDataArray from '../../Common/Core/DataArray.js';
|
|
|
5
5
|
import { V as isPowerOfTwo, R as nearestPowerOfTwo } from '../../Common/Core/Math/index.js';
|
|
6
6
|
import vtkViewNode from '../SceneGraph/ViewNode.js';
|
|
7
7
|
import { registerOverride } from './ViewNodeFactory.js';
|
|
8
|
+
import supportsNorm16LinearCached from './Texture/supportsNorm16Linear.js';
|
|
8
9
|
|
|
9
10
|
const {
|
|
10
11
|
Wrap,
|
|
@@ -133,6 +134,12 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
133
134
|
publicAPI.activate();
|
|
134
135
|
}
|
|
135
136
|
};
|
|
137
|
+
const getNorm16Ext = () => {
|
|
138
|
+
if ((model.minificationFilter === Filter.LINEAR || model.magnificationFilter === Filter.LINEAR) && !supportsNorm16LinearCached()) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return model.oglNorm16Ext;
|
|
142
|
+
};
|
|
136
143
|
|
|
137
144
|
//----------------------------------------------------------------------------
|
|
138
145
|
publicAPI.destroyTexture = () => {
|
|
@@ -285,7 +292,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
285
292
|
publicAPI.getDefaultInternalFormat = (vtktype, numComps) => {
|
|
286
293
|
let result = 0;
|
|
287
294
|
// try default next
|
|
288
|
-
result = model._openGLRenderWindow.getDefaultTextureInternalFormat(vtktype, numComps,
|
|
295
|
+
result = model._openGLRenderWindow.getDefaultTextureInternalFormat(vtktype, numComps, getNorm16Ext(), publicAPI.useHalfFloat());
|
|
289
296
|
if (result) {
|
|
290
297
|
return result;
|
|
291
298
|
}
|
|
@@ -364,9 +371,9 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
364
371
|
return model.context.UNSIGNED_BYTE;
|
|
365
372
|
// prefer norm16 since that is accurate compared to
|
|
366
373
|
// half float which is not
|
|
367
|
-
case
|
|
374
|
+
case getNorm16Ext() && !useHalfFloat && VtkDataTypes.SHORT:
|
|
368
375
|
return model.context.SHORT;
|
|
369
|
-
case
|
|
376
|
+
case getNorm16Ext() && !useHalfFloat && VtkDataTypes.UNSIGNED_SHORT:
|
|
370
377
|
return model.context.UNSIGNED_SHORT;
|
|
371
378
|
// use half float type
|
|
372
379
|
case useHalfFloat && VtkDataTypes.SHORT:
|
|
@@ -659,7 +666,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
659
666
|
}
|
|
660
667
|
if (model._openGLRenderWindow.getWebgl2()) {
|
|
661
668
|
const webGLInfo = model._openGLRenderWindow.getGLInformations();
|
|
662
|
-
if (webGLInfo.RENDERER.value.match(/WebKit/gi) && navigator.platform.match(/Mac/gi) &&
|
|
669
|
+
if (webGLInfo.RENDERER.value.match(/WebKit/gi) && navigator.platform.match(/Mac/gi) && getNorm16Ext() && (dataType === VtkDataTypes.UNSIGNED_SHORT || dataType === VtkDataTypes.SHORT)) {
|
|
663
670
|
// Cannot use texStorage with EXT_texture_norm16 textures on Mac M1 GPU.
|
|
664
671
|
// No errors reported but the texture is unusable.
|
|
665
672
|
return false;
|
|
@@ -717,7 +724,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
717
724
|
if (flip) {
|
|
718
725
|
model.context.pixelStorei(model.context.UNPACK_FLIP_Y_WEBGL, false);
|
|
719
726
|
}
|
|
720
|
-
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * numComps * model._openGLRenderWindow.getDefaultTextureByteSize(dataType,
|
|
727
|
+
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * numComps * model._openGLRenderWindow.getDefaultTextureByteSize(dataType, getNorm16Ext(), publicAPI.useHalfFloat());
|
|
721
728
|
publicAPI.deactivate();
|
|
722
729
|
return true;
|
|
723
730
|
};
|
|
@@ -795,7 +802,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
795
802
|
h /= 2;
|
|
796
803
|
}
|
|
797
804
|
}
|
|
798
|
-
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * numComps * model._openGLRenderWindow.getDefaultTextureByteSize(dataType,
|
|
805
|
+
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * numComps * model._openGLRenderWindow.getDefaultTextureByteSize(dataType, getNorm16Ext(), publicAPI.useHalfFloat());
|
|
799
806
|
// generateMipmap must not be called here because we manually upload all levels
|
|
800
807
|
// if it is called, all levels will be overwritten
|
|
801
808
|
|
|
@@ -845,7 +852,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
845
852
|
if (model.generateMipmap) {
|
|
846
853
|
model.context.generateMipmap(model.target);
|
|
847
854
|
}
|
|
848
|
-
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * model.components * model._openGLRenderWindow.getDefaultTextureByteSize(dataType,
|
|
855
|
+
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * model.components * model._openGLRenderWindow.getDefaultTextureByteSize(dataType, getNorm16Ext(), publicAPI.useHalfFloat());
|
|
849
856
|
publicAPI.deactivate();
|
|
850
857
|
return true;
|
|
851
858
|
};
|
|
@@ -895,7 +902,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
895
902
|
if (model.generateMipmap) {
|
|
896
903
|
model.context.generateMipmap(model.target);
|
|
897
904
|
}
|
|
898
|
-
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * model.components * model._openGLRenderWindow.getDefaultTextureByteSize(VtkDataTypes.UNSIGNED_CHAR,
|
|
905
|
+
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * model.components * model._openGLRenderWindow.getDefaultTextureByteSize(VtkDataTypes.UNSIGNED_CHAR, getNorm16Ext(), publicAPI.useHalfFloat());
|
|
899
906
|
publicAPI.deactivate();
|
|
900
907
|
return true;
|
|
901
908
|
};
|
|
@@ -1013,7 +1020,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1013
1020
|
}
|
|
1014
1021
|
|
|
1015
1022
|
// Handle SHORT data type with EXT_texture_norm16 extension
|
|
1016
|
-
if (
|
|
1023
|
+
if (getNorm16Ext() && !useHalfFloat && dataType === VtkDataTypes.SHORT) {
|
|
1017
1024
|
for (let c = 0; c < numComps; ++c) {
|
|
1018
1025
|
model.volumeInfo.scale[c] = 32767.0; // Scale to [-1, 1] range
|
|
1019
1026
|
}
|
|
@@ -1022,7 +1029,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1022
1029
|
}
|
|
1023
1030
|
|
|
1024
1031
|
// Handle UNSIGNED_SHORT data type with EXT_texture_norm16 extension
|
|
1025
|
-
if (
|
|
1032
|
+
if (getNorm16Ext() && !useHalfFloat && dataType === VtkDataTypes.UNSIGNED_SHORT) {
|
|
1026
1033
|
for (let c = 0; c < numComps; ++c) {
|
|
1027
1034
|
model.volumeInfo.scale[c] = 65535.0; // Scale to [0, 1] range
|
|
1028
1035
|
}
|
|
@@ -1112,7 +1119,7 @@ function vtkOpenGLTexture(publicAPI, model) {
|
|
|
1112
1119
|
if (model.generateMipmap) {
|
|
1113
1120
|
model.context.generateMipmap(model.target);
|
|
1114
1121
|
}
|
|
1115
|
-
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * model.components * model._openGLRenderWindow.getDefaultTextureByteSize(dataTypeToUse,
|
|
1122
|
+
model.allocatedGPUMemoryInBytes = model.width * model.height * model.depth * model.components * model._openGLRenderWindow.getDefaultTextureByteSize(dataTypeToUse, getNorm16Ext(), publicAPI.useHalfFloat());
|
|
1116
1123
|
publicAPI.deactivate();
|
|
1117
1124
|
return true;
|
|
1118
1125
|
};
|
package/index.d.ts
CHANGED
|
@@ -54,6 +54,7 @@
|
|
|
54
54
|
/// <reference path="./Common/DataModel/Triangle.d.ts" />
|
|
55
55
|
/// <reference path="./Common/Transform/LandmarkTransform.d.ts" />
|
|
56
56
|
/// <reference path="./Common/Transform/Transform.d.ts" />
|
|
57
|
+
/// <reference path="./Filters/Core/ThresholdPoints.d.ts" />
|
|
57
58
|
/// <reference path="./Filters/General/AppendPolyData.d.ts" />
|
|
58
59
|
/// <reference path="./Filters/General/ClipClosedSurface.d.ts" />
|
|
59
60
|
/// <reference path="./Filters/General/ContourLoopExtraction.d.ts" />
|