@kitware/vtk.js 25.1.3 → 25.2.0

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.
@@ -5,7 +5,7 @@ import { RGBColor, Vector3 } from './../../types';
5
5
  export enum LIGHT_TYPES {
6
6
  'HeadLight',
7
7
  'CameraLight',
8
- 'SceneLight'
8
+ 'SceneLight',
9
9
  }
10
10
 
11
11
  export interface ILightInitialValues {
@@ -17,6 +17,7 @@ export interface ILightInitialValues {
17
17
  positional?: boolean;
18
18
  exponent?: number;
19
19
  coneAngle?: number;
20
+ coneFalloff?: number;
20
21
  attenuationValues?: number[];
21
22
  lightType?: LIGHT_TYPES;
22
23
  shadowAttenuation?: number;
@@ -54,6 +55,15 @@ export interface vtkLight extends vtkObject {
54
55
  */
55
56
  getConeAngle(): number;
56
57
 
58
+ /**
59
+ * Get the lighting falloff angle of a positional light in degrees.
60
+ * This is the angle that sets how much the cone will be extended to
61
+ * smooth the border. A value of 0 indicates that the spot light will
62
+ * have a completely sharp edge (this does not mean completely sharp
63
+ * lighting, just that the border will be sharp).
64
+ */
65
+ getConeFalloff(): number;
66
+
57
67
  /**
58
68
  * Set the position and focal point of a light based on elevation and azimuth.
59
69
  * The light is moved so it is shining from the given angle. Angles are
@@ -174,6 +184,16 @@ export interface vtkLight extends vtkObject {
174
184
  */
175
185
  setConeAngle(coneAngle: number): boolean;
176
186
 
187
+ /**
188
+ * Set the lighting falloff angle of a positional light in degrees.
189
+ * This is the angle that sets how much the cone will be extended to
190
+ * smooth the border. A value of 0 indicates that the spot light will
191
+ * have a completely sharp edge (this does not mean completely sharp
192
+ * lighting, just that the border will be sharp).
193
+ * @param {Number} coneFalloff The cone falloff angle.
194
+ */
195
+ setConeFalloff(coneFalloff: number): boolean;
196
+
177
197
  /**
178
198
  * Set the position and focal point of a light based on elevation and
179
199
  * azimuth. The light is moved so it is shining from the given angle. Angles
@@ -184,6 +204,20 @@ export interface vtkLight extends vtkObject {
184
204
  */
185
205
  setDirectionAngle(elevation: number, azimuth: number): boolean;
186
206
 
207
+ /**
208
+ * Set the direction vector of the light from X, Y, and Z values
209
+ * @param {Number} x The x coordinate.
210
+ * @param {Number} y The y coordinate.
211
+ * @param {Number} z The z coordinate.
212
+ */
213
+ setDirection(x: number, y: number, z: number): boolean;
214
+
215
+ /**
216
+ * Set the direction vector of the light from X, Y, and Z values
217
+ * @param {Vector3} direction
218
+ */
219
+ setDirection(direction: Vector3): boolean;
220
+
187
221
  /**
188
222
  * Set the exponent of the cosine used in positional lighting.
189
223
  * @param {Number} exponent The exponent of the cosine.
@@ -299,7 +333,7 @@ export function extend(publicAPI: object, model: object, initialValues?: ILightI
299
333
  /**
300
334
  * Method use to create a new instance of vtkLight with the focal point at the origin and its position
301
335
  * set to [0, 0, 1]. The light is a SceneLight, its color is white, intensity=1, the light is turned on,
302
- * positional lighting is off, coneAngle=30, AttenuationValues=[1, 0, 0], exponent=1 and the transformMatrix is null.
336
+ * positional lighting is off, coneAngle=30, coneFalloff=5, AttenuationValues=[1, 0, 0], exponent=1 and the transformMatrix is null.
303
337
  * @param {ILightInitialValues} [initialValues] for pre-setting some of its content
304
338
  */
305
339
  export function newInstance(initialValues?: ILightInitialValues): vtkLight;
@@ -33,14 +33,19 @@ function vtkLight(publicAPI, model) {
33
33
 
34
34
  publicAPI.getDirection = function () {
35
35
  if (model.directionMTime < model.mtime) {
36
- model.direction[0] = model.focalPoint[0] - model.position[0];
37
- model.direction[1] = model.focalPoint[1] - model.position[1];
38
- model.direction[2] = model.focalPoint[2] - model.position[2];
36
+ vec3.sub(model.direction, model.focalPoint, model.position);
39
37
  normalize(model.direction);
40
38
  model.directionMTime = model.mtime;
41
39
  }
42
40
 
43
41
  return model.direction;
42
+ }; // Sets the direction from a vec3 instead of a focal point
43
+
44
+
45
+ publicAPI.setDirection = function (directionVector) {
46
+ var newFocalPoint = new Float64Array(3);
47
+ vec3.sub(newFocalPoint, model.position, directionVector);
48
+ model.focalPoint = newFocalPoint;
44
49
  };
45
50
 
46
51
  publicAPI.setDirectionAngle = function (elevation, azimuth) {
@@ -89,6 +94,7 @@ var DEFAULT_VALUES = {
89
94
  positional: false,
90
95
  exponent: 1,
91
96
  coneAngle: 30,
97
+ coneFalloff: 5,
92
98
  attenuationValues: [1, 0, 0],
93
99
  transformMatrix: null,
94
100
  lightType: 'SceneLight',
@@ -102,7 +108,7 @@ function extend(publicAPI, model) {
102
108
  Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API
103
109
 
104
110
  macro.obj(publicAPI, model);
105
- macro.setGet(publicAPI, model, ['intensity', 'switch', 'positional', 'exponent', 'coneAngle', 'transformMatrix', 'lightType', 'shadowAttenuation', 'attenuationValues']);
111
+ macro.setGet(publicAPI, model, ['intensity', 'switch', 'positional', 'exponent', 'coneAngle', 'coneFalloff', 'transformMatrix', 'lightType', 'shadowAttenuation', 'attenuationValues']);
106
112
  macro.setGetArray(publicAPI, model, ['color', 'position', 'focalPoint', 'attenuationValues'], 3); // Object methods
107
113
 
108
114
  vtkLight(publicAPI, model);
@@ -1,15 +1,27 @@
1
1
  import { vtkObject } from './../../interfaces';
2
2
  import { RGBColor } from './../../types';
3
3
  import { Interpolation, Representation, Shading } from './Property/Constants';
4
+ import { vtkTexture } from './Texture';
4
5
 
5
6
  export interface IPropertyInitialValues {
6
7
  color?: RGBColor;
7
8
  ambientColor?: RGBColor;
8
9
  diffuseColor?: RGBColor;
9
10
  specularColor?: RGBColor;
11
+ diffuseTexture?: vtkTexture;
12
+ metallicTexture?: vtkTexture;
13
+ roughnessTexture?: vtkTexture;
14
+ normalTexture?: vtkTexture;
15
+ ambientOcclusionTexture?: vtkTexture;
16
+ emissionTexture?: vtkTexture;
10
17
  edgeColor?: RGBColor;
11
18
  ambient?: number;
12
19
  diffuse?: number;
20
+ metallic?: number;
21
+ roughness?: number;
22
+ normalStrength?: number;
23
+ emission?: number;
24
+ baseIOR?: number;
13
25
  specular?: number;
14
26
  specularPower?: number;
15
27
  opacity?: number;
@@ -161,6 +173,36 @@ export interface vtkProperty extends vtkObject {
161
173
  */
162
174
  getSpecular(): number;
163
175
 
176
+ /**
177
+ * Get the roughness coefficient.
178
+ * @default 1
179
+ */
180
+ getRoughness(): number;
181
+
182
+ /**
183
+ * Get the metallic coefficient.
184
+ * @default 0
185
+ */
186
+ getMetallic(): number;
187
+
188
+ /**
189
+ * Get the index of refraction.
190
+ * @default 0
191
+ */
192
+ getBaseIOR(): number;
193
+
194
+ /**
195
+ * Get the strength of the normal map.
196
+ * @default 1
197
+ */
198
+ getNormalStrength(): number;
199
+
200
+ /**
201
+ * Get the emission coefficient.
202
+ * @default 0
203
+ */
204
+ getEmission(): number;
205
+
164
206
  /**
165
207
  * Get the specular surface color.
166
208
  * @return {RGBColor} Array of RGB color.
@@ -178,6 +220,36 @@ export interface vtkProperty extends vtkObject {
178
220
  */
179
221
  getSpecularPower(): number;
180
222
 
223
+ /**
224
+ * Get the diffuse texture.
225
+ */
226
+ getDiffuseTexture(): vtkTexture;
227
+
228
+ /**
229
+ * Get the metallic texture.
230
+ */
231
+ getMetallicTexture(): vtkTexture;
232
+
233
+ /**
234
+ * Get the roughness texture.
235
+ */
236
+ getRoughnessTexture(): vtkTexture;
237
+
238
+ /**
239
+ * Get the normal texture.
240
+ */
241
+ getNormalTexture(): vtkTexture;
242
+
243
+ /**
244
+ * Get the ambient occlusion texture.
245
+ */
246
+ getAmbientOcclusionTexture(): vtkTexture;
247
+
248
+ /**
249
+ * Get the emission texture.
250
+ */
251
+ getEmissionTexture(): vtkTexture;
252
+
181
253
  /**
182
254
  * Set the ambient lighting coefficient.
183
255
  * @param {Number} ambient The ambient lighting coefficient.
@@ -385,6 +457,18 @@ export interface vtkProperty extends vtkObject {
385
457
  */
386
458
  setSpecular(specular: number): boolean;
387
459
 
460
+ /**
461
+ * Set the normal map strength.
462
+ * @param {Boolean} normal
463
+ */
464
+ setNormalStrength(normalStrength: number): boolean;
465
+
466
+ /**
467
+ * Set the ambient occlusion map strength.
468
+ * @param {Boolean} emission
469
+ */
470
+ setEmission(emission: number): boolean;
471
+
388
472
  /**
389
473
  * Set the specular surface color.
390
474
  * @param {Number} r Defines the red component (between 0 and 1)
@@ -410,6 +494,42 @@ export interface vtkProperty extends vtkObject {
410
494
  * @param {Number} specularPower
411
495
  */
412
496
  setSpecularPower(specularPower: number): boolean;
497
+
498
+ /**
499
+ * Set the diffuse texture.
500
+ * @param {vtkTexture} diffuseTexture
501
+ */
502
+ setDiffuseTexture(diffuseTexture: vtkTexture): boolean;
503
+
504
+ /**
505
+ * Set the metallic texture.
506
+ * @param {vtkTexture} metallicTexture
507
+ */
508
+ setMetallicTexture(metallicTexture: vtkTexture): boolean;
509
+
510
+ /**
511
+ * Set the roughness texture.
512
+ * @param {vtkTexture} roughnessTexture
513
+ */
514
+ setRoughnessTexture(roughnessTexture: vtkTexture): boolean;
515
+
516
+ /**
517
+ * Set the normal texture.
518
+ * @param {vtkTexture} normalTexture
519
+ */
520
+ setNormalTexture(normalTexture: vtkTexture): boolean;
521
+
522
+ /**
523
+ * Set the ambient occlusion texture.
524
+ * @param {vtkTexture} ambientOcclusionTexture
525
+ */
526
+ setAmbientOcclusionTexture(ambientOcclusionTexture: vtkTexture): boolean;
527
+
528
+ /**
529
+ * Set the emission texture.
530
+ * @param {vtkTexture} emissionTexture
531
+ */
532
+ setEmissionTexture(emissionTexture: vtkTexture): boolean;
413
533
  }
414
534
 
415
535
  /**
@@ -58,6 +58,17 @@ function vtkProperty(publicAPI, model) {
58
58
  return [].concat(model.color);
59
59
  };
60
60
 
61
+ publicAPI.setSpecularPower = function (specularPower) {
62
+ var roughness = 1 / Math.max(1.0, specularPower);
63
+
64
+ if (model.roughness !== roughness || model.specularPower !== specularPower) {
65
+ model.specularPower = specularPower; // Specular power still needs to be set as long as webgl is using it (otherwise testShaderReplacementsClear fails)
66
+
67
+ model.roughness = roughness;
68
+ publicAPI.modified();
69
+ }
70
+ };
71
+
61
72
  publicAPI.addShaderVariable = notImplemented('AddShaderVariable');
62
73
 
63
74
  publicAPI.setInterpolationToFlat = function () {
@@ -104,6 +115,11 @@ var DEFAULT_VALUES = {
104
115
  edgeColor: [0, 0, 0],
105
116
  ambient: 0,
106
117
  diffuse: 1,
118
+ metallic: 0,
119
+ roughness: 0.6,
120
+ normalStrength: 1,
121
+ emission: 1,
122
+ baseIOR: 1.45,
107
123
  specular: 0,
108
124
  specularPower: 1,
109
125
  opacity: 1,
@@ -124,7 +140,7 @@ function extend(publicAPI, model) {
124
140
  Object.assign(model, DEFAULT_VALUES, initialValues); // Build VTK API
125
141
 
126
142
  macro.obj(publicAPI, model);
127
- macro.setGet(publicAPI, model, ['lighting', 'interpolation', 'ambient', 'diffuse', 'specular', 'specularPower', 'opacity', 'edgeVisibility', 'lineWidth', 'pointSize', 'backfaceCulling', 'frontfaceCulling', 'representation']);
143
+ macro.setGet(publicAPI, model, ['lighting', 'interpolation', 'ambient', 'diffuse', 'metallic', 'roughness', 'normalStrength', 'emission', 'baseIOR', 'specular', 'specularPower', 'opacity', 'edgeVisibility', 'lineWidth', 'pointSize', 'backfaceCulling', 'frontfaceCulling', 'representation', 'diffuseTexture', 'metallicTexture', 'roughnessTexture', 'normalTexture', 'ambientOcclusionTexture', 'emissionTexture']);
128
144
  macro.setGetArray(publicAPI, model, ['ambientColor', 'specularColor', 'diffuseColor', 'edgeColor'], 3); // Object methods
129
145
 
130
146
  vtkProperty(publicAPI, model);
@@ -72,6 +72,37 @@ function vtkTexture(publicAPI, model) {
72
72
 
73
73
  publicAPI.modified();
74
74
  };
75
+
76
+ publicAPI.getDimensionality = function () {
77
+ var width = 0;
78
+ var height = 0;
79
+ var depth = 1;
80
+
81
+ if (publicAPI.getInputData()) {
82
+ var data = publicAPI.getInputData();
83
+ width = data.getDimensions()[0];
84
+ height = data.getDimensions()[1];
85
+ depth = data.getDimensions()[2];
86
+ }
87
+
88
+ if (model.jsImageData) {
89
+ width = model.jsImageData.width;
90
+ height = model.jsImageData.height;
91
+ }
92
+
93
+ if (model.canvas) {
94
+ width = model.canvas.width;
95
+ height = model.canvas.height;
96
+ }
97
+
98
+ if (model.image) {
99
+ width = model.image.width;
100
+ height = model.image.height;
101
+ }
102
+
103
+ var dimensionality = (width > 1) + (height > 1) + (depth > 1);
104
+ return dimensionality;
105
+ };
75
106
  } // ----------------------------------------------------------------------------
76
107
  // Object factory
77
108
  // ----------------------------------------------------------------------------
@@ -18,7 +18,7 @@ var ScalarMode = vtkMapper.ScalarMode;
18
18
  var CoordinateSystem = vtkProp.CoordinateSystem;
19
19
  var DisplayLocation = vtkProperty2D.DisplayLocation;
20
20
  var vtkWebGPUPolyDataVS = "\n//VTK::Renderer::Dec\n\n//VTK::Color::Dec\n\n//VTK::Normal::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Select::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n@vertex\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : vertexOutput;\n\n var vertex: vec4<f32> = vertexBC;\n\n //VTK::Color::Impl\n\n //VTK::Normal::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::Select::Impl\n\n //VTK::Position::Impl\n\n return output;\n}\n";
21
- var vtkWebGPUPolyDataFS = "\n//VTK::Renderer::Dec\n\n//VTK::Color::Dec\n\n// optional surface normal declaration\n//VTK::Normal::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::Select::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : fragmentOutput;\n\n var ambientColor: vec4<f32> = mapperUBO.AmbientColor;\n var diffuseColor: vec4<f32> = mapperUBO.DiffuseColor;\n var opacity: f32 = mapperUBO.Opacity;\n\n //VTK::Color::Impl\n\n //VTK::Normal::Impl\n\n //VTK::Light::Impl\n\n var computedColor: vec4<f32> = vec4<f32>(ambientColor.rgb * mapperUBO.AmbientIntensity\n + diffuse * mapperUBO.DiffuseIntensity\n + specular * mapperUBO.SpecularIntensity,\n opacity);\n\n //VTK::TCoord::Impl\n\n //VTK::Select::Impl\n\n if (computedColor.a == 0.0) { discard; };\n\n //VTK::Position::Impl\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n";
21
+ var vtkWebGPUPolyDataFS = "\nstruct PBRData {\n diffuse: vec3<f32>,\n specular: vec3<f32>,\n}\n\n// Dot product with the max already in it\nfn mdot(a: vec3<f32>, b: vec3<f32>) -> f32 {\n return max(0.0, dot(a, b));\n}\n\n// Lambertian diffuse model\nfn lambertDiffuse(base: vec3<f32>, N: vec3<f32>, L: vec3<f32>) -> vec3<f32> {\n var pi: f32 = 3.14159265359; \n var NdotL: f32 = mdot(N, L);\n NdotL = pow(NdotL, 1.5);\n return (base/pi)*NdotL;\n}\n\n// Yasuhiro Fujii improvement on the Oren-Nayar model\n// https://mimosa-pudica.net/improved-oren-nayar.html\n// p is surface color, o is roughness\nfn fujiiOrenNayar(p: vec3<f32>, o: f32, N: vec3<f32>, L: vec3<f32>, V: vec3<f32>) -> vec3<f32> {\n var invpi: f32 = 0.31830988618; // 1/pi\n\n var o2 = o*o;\n var NdotL: f32 = mdot(N, L);\n NdotL = pow(NdotL, 1.5); // Less physically accurate, but hides the \"seams\" between lights better\n\n var NdotV: f32 = mdot(N, V);\n var LdotV: f32 = mdot(L, V);\n\n var s: f32 = LdotV - NdotL*NdotV;\n var t: f32 = mix(1, max(NdotL, NdotV), step(0, s)); // Mix with step is the equivalent of an if statement\n var A: vec3<f32> = 0.5*(o2 / (o2 + 0.33)) + 0.17*p*(o2 / (o2 + 0.13));\n A = invpi*(1 - A);\n var B: f32 = 0.45*(o2 / (o2 + 0.09));\n B = invpi*B;\n\n return p*NdotL*(A + B*(s/t));\n}\n\n// Fresnel portion of BRDF (IOR only, simplified)\nfn schlickFresnelIOR(V: vec3<f32>, N: vec3<f32>, ior: f32, k: f32) -> f32 {\n var NdotV: f32 = mdot(V, N);\n // var R0: f32 = pow((ior - 1.0) / (ior + 1.0), 2); // 1.0 is about the ior of air, and it is assumed that light will be traveling through air\n var F0: f32 = (pow((ior - 1.0), 2) + k*k) / (pow((ior + 1.0), 2) + k*k); // This takes into account the roughness, whic the other one does not\n return F0 + (1 - F0) * pow((1-NdotV), 5); \n}\n\n// Fresnel portion of BRDF (Color ior, better)\nfn schlickFresnelRGB(V: vec3<f32>, N: vec3<f32>, F0: vec3<f32>) -> vec3<f32> {\n var NdotV: f32 = mdot(V, N);\n return F0 + (1 - F0) * pow((1-NdotV), 5); \n}\n\n// Normal portion of BRDF\n// https://learnopengl.com/PBR/Theory\n// Trowbridge-Reitz GGX functions: normal, halfway, roughness^2\nfn trGGX(N: vec3<f32>, H: vec3<f32>, a: f32) -> f32 {\n var pi: f32 = 3.14159265359; \n\n var a2: f32 = a*a;\n var NdotH = mdot(N, H);\n var NdotH2 = NdotH*NdotH;\n \n var denom: f32 = NdotH2 * (a2 - 1.0) + 1.0;\n\n return a2 / max((pi*denom*denom), 0.000001);\n}\n\n// A VERY bad approximation of anisotropy. Real anisotropic calculations require tangent and bitangent\nfn anisotrophicTrGGX(N: vec3<f32>, H: vec3<f32>, O: vec3<f32>, s: f32, a: f32) -> f32 {\n var Op: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(O) * s, 0.)).xyz;\n\n var ggx1: f32 = trGGX(N + Op*s, H, a);\n var ggx2: f32 = trGGX(N - Op*s, H, a);\n return (0.5 * ggx1 + 0.5 * ggx2);\n}\n\n// Geometry portion of BRDF\nfn schlickGGX(N: vec3<f32>, X: vec3<f32>, k: f32) -> f32 {\n var NdotX = mdot(N, X);\n return NdotX / max(0.000001, (NdotX*(1-k) + k));\n}\n\nfn smithSurfaceRoughness(N: vec3<f32>, V: vec3<f32>, L: vec3<f32>, k: f32) -> f32 {\n var ggx1: f32 = max(0.01, schlickGGX(N, V, k)); // Prevents void zones at the cost of some accuracy\n var ggx2: f32 = schlickGGX(N, L, k);\n return ggx1*ggx2;\n}\n\n// BRDF Combination\nfn cookTorrance(D: f32, F: f32, G: f32, N: vec3<f32>, V: vec3<f32>, L: vec3<f32>) -> f32 {\n var num: f32 = D*F*G;\n var denom: f32 = 4*mdot(V, N)*mdot(L, N);\n\n return num / max(denom, 0.000001);\n}\n\n// Different lighting calculations for different light sources\nfn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, metallic: f32, direction: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData { \n var L: vec3<f32> = normalize(direction); // Light Vector\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2;\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); // Fresnel term is replaced with 1 because it is added later\n var incoming: vec3<f32> = color;\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5);\n\n var specular: vec3<f32> = brdf*incoming*angle;\n // Oren-Nayar gives a clay-like effect when fully rough which some people may not want, so it might be better to give a separate\n // control property for the diffuse vs specular roughness\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V); \n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// TODO: find some way to reduce the number of arguments going in here\nfn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {\n var L: vec3<f32> = normalize(position - fragPos); // Light Vector\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n var dist = distance(position, fragPos);\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2; // could also be pow(alpha + 1.0, 2) / 8\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); \n var incoming: vec3<f32> = color * (1. / (dist*dist));\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly \"seams\" bewteen light sources\n\n var specular: vec3<f32> = brdf*incoming*angle;\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);\n\n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// For a reason unknown to me, spheres dont seem to behave propperly with head-on spot lights\nfn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, direction: vec3<f32>, cones: vec2<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {\n var L: vec3<f32> = normalize(position - fragPos);\n var H: vec3<f32> = normalize(L + V); // Halfway Vector\n var dist = distance(position, fragPos);\n\n var alpha = roughness*roughness;\n var k: f32 = alpha*alpha / 2; // could also be pow(alpha + 1.0, 2) / 8\n\n var D: f32 = trGGX(N, H, alpha); // Distribution\n // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel\n var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry\n\n var brdf: f32 = cookTorrance(D, 1, G, N, V, L); \n \n // Cones.x is the inner phi and cones.y is the outer phi\n var theta: f32 = mdot(normalize(direction), L);\n var epsilon: f32 = cones.x - cones.y;\n var intensity: f32 = (theta - cones.y) / epsilon;\n intensity = clamp(intensity, 0.0, 1.0);\n intensity /= dist*dist;\n\n var incoming: vec3<f32> = color * intensity;\n\n var angle: f32 = mdot(L, N);\n angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly \"seams\" bewteen light sources\n\n var specular: vec3<f32> = brdf*incoming*angle;\n var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);\n\n // Stores the specular and diffuse separately to allow for finer post processing\n // Could also be done (propably more properly) with a struct\n var out = PBRData(diffuse, specular);\n \n return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)\n}\n\n// Environment mapping stuff\n// Takes in a vector and converts it to an equivalent coordinate in a rectilinear texture. Should be replaced with cubemaps at some point\nfn vecToRectCoord(dir: vec3<f32>) -> vec2<f32> {\n var tau: f32 = 6.28318530718;\n var out: vec2<f32> = vec2<f32>(0.);\n\n out.x = atan2(dir.z, dir.x) / tau;\n out.x += 0.5;\n\n out.y = (dir.y * .5) + .5;\n\n return out;\n}\n\n//VTK::Renderer::Dec\n\n//VTK::Color::Dec\n\n//VTK::TCoord::Dec\n\n// optional surface normal declaration\n//VTK::Normal::Dec\n\n//VTK::Select::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : fragmentOutput;\n\n // Temporary ambient, diffuse, and opacity\n var ambientColor: vec4<f32> = mapperUBO.AmbientColor;\n var diffuseColor: vec4<f32> = mapperUBO.DiffuseColor;\n var opacity: f32 = mapperUBO.Opacity;\n\n // This should be declared somewhere else\n var _diffuseMap: vec4<f32> = vec4<f32>(1);\n var _roughnessMap: vec4<f32> = vec4<f32>(1);\n var _metallicMap: vec4<f32> = vec4<f32>(1);\n var _normalMap: vec4<f32> = vec4<f32>(0, 0, 1, 0); // normal map was setting off the normal vector detection in fragment\n var _ambientOcclusionMap: vec4<f32> = vec4<f32>(0);\n var _emissionMap: vec4<f32> = vec4<f32>(0);\n\n //VTK::Color::Impl\n\n //VTK::TCoord::Impl\n\n //VTK::Normal::Impl\n\n var computedColor: vec4<f32> = vec4<f32>(diffuseColor.rgb, 1.);\n\n //VTK::Light::Impl\n\n //VTK::Select::Impl\n\n if (computedColor.a == 0.0) { discard; };\n\n //VTK::Position::Impl\n\n //VTK::RenderEncoder::Impl\n\n return output;\n}\n";
22
22
 
23
23
  function isEdges(hash) {
24
24
  // edge pipelines have "edge" in them
@@ -72,6 +72,9 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
72
72
  var utime = model.UBO.getSendTime();
73
73
 
74
74
  if (publicAPI.getMTime() > utime || ppty.getMTime() > utime || model.renderable.getMTime() > utime) {
75
+ var _ppty$getEdgeColorByR;
76
+
77
+ // Matricies
75
78
  var keyMats = model.WebGPUActor.getKeyMatrices(model.WebGPURenderer);
76
79
  model.UBO.setArray('BCWCMatrix', keyMats.bcwc);
77
80
  model.UBO.setArray('BCSCMatrix', keyMats.bcsc);
@@ -79,25 +82,42 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
79
82
 
80
83
  if (model.is2D) {
81
84
  model.UBO.setValue('ZValue', model.WebGPUActor.getRenderable().getProperty().getDisplayLocation() === DisplayLocation.FOREGROUND ? 1.0 : 0.0);
82
- var aColor = ppty.getColorByReference();
85
+
86
+ var _aColor = ppty.getColorByReference();
87
+
83
88
  model.UBO.setValue('AmbientIntensity', 1.0);
84
- model.UBO.setArray('AmbientColor', [aColor[0], aColor[1], aColor[2], 1.0]);
89
+ model.UBO.setArray('DiffuseColor', [_aColor[0], _aColor[1], _aColor[2], 1.0]);
85
90
  model.UBO.setValue('DiffuseIntensity', 0.0);
86
91
  model.UBO.setValue('SpecularIntensity', 0.0);
87
92
  } else {
88
- var _aColor = ppty.getAmbientColorByReference();
93
+ // Base Colors
94
+ var _aColor2 = ppty.getAmbientColorByReference();
89
95
 
90
96
  model.UBO.setValue('AmbientIntensity', ppty.getAmbient());
91
- model.UBO.setArray('AmbientColor', [_aColor[0], _aColor[1], _aColor[2], 1.0]);
97
+ model.UBO.setArray('AmbientColor', [_aColor2[0], _aColor2[1], _aColor2[2], 1.0]);
92
98
  model.UBO.setValue('DiffuseIntensity', ppty.getDiffuse());
93
- _aColor = ppty.getDiffuseColorByReference();
94
- model.UBO.setArray('DiffuseColor', [_aColor[0], _aColor[1], _aColor[2], 1.0]);
99
+ _aColor2 = ppty.getDiffuseColorByReference();
100
+ model.UBO.setArray('DiffuseColor', [_aColor2[0], _aColor2[1], _aColor2[2], 1.0]); // Roughness
101
+
102
+ model.UBO.setValue('Roughness', ppty.getRoughness());
103
+ model.UBO.setValue('BaseIOR', ppty.getBaseIOR()); // Metallic
104
+
105
+ model.UBO.setValue('Metallic', ppty.getMetallic()); // Normal
106
+
107
+ model.UBO.setValue('NormalStrength', ppty.getNormalStrength()); // Emission
108
+
109
+ model.UBO.setValue('Emission', ppty.getEmission()); // Specular
110
+
95
111
  model.UBO.setValue('SpecularIntensity', ppty.getSpecular());
96
- model.UBO.setValue('SpecularPower', ppty.getSpecularPower());
97
- _aColor = ppty.getSpecularColorByReference();
98
- model.UBO.setArray('SpecularColor', [_aColor[0], _aColor[1], _aColor[2], 1.0]);
99
- _aColor = ppty.getEdgeColorByReference();
100
- model.UBO.setArray('EdgeColor', [_aColor[0], _aColor[1], _aColor[2], 1.0]);
112
+ _aColor2 = ppty.getSpecularColorByReference();
113
+ model.UBO.setArray('SpecularColor', [_aColor2[0], _aColor2[1], _aColor2[2], 1.0]);
114
+ } // Edge and line rendering
115
+
116
+
117
+ var aColor = (_ppty$getEdgeColorByR = ppty.getEdgeColorByReference) === null || _ppty$getEdgeColorByR === void 0 ? void 0 : _ppty$getEdgeColorByR.call(ppty);
118
+
119
+ if (aColor) {
120
+ model.UBO.setArray('EdgeColor', [aColor[0], aColor[1], aColor[2], 1.0]);
101
121
  }
102
122
 
103
123
  model.UBO.setValue('LineWidth', ppty.getLineWidth());
@@ -130,10 +150,11 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
130
150
  publicAPI.replaceShaderPosition = function (hash, pipeline, vertexInput) {
131
151
  var vDesc = pipeline.getShaderDescription('vertex');
132
152
  vDesc.addBuiltinOutput('vec4<f32>', '@builtin(position) Position');
153
+ if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');
133
154
  var code = vDesc.getCode();
134
155
 
135
156
  if (model.useRendererMatrix) {
136
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', [' var pCoord: vec4<f32> = rendererUBO.SCPCMatrix*mapperUBO.BCSCMatrix*vertexBC;', '//VTK::Position::Impl']).result;
157
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', [' var pCoord: vec4<f32> = rendererUBO.SCPCMatrix*mapperUBO.BCSCMatrix*vertexBC;', ' output.vertexVC = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.xyz, 1.0);', '//VTK::Position::Impl']).result;
137
158
 
138
159
  if (model.forceZValue) {
139
160
  code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', ['pCoord = vec4<f32>(pCoord.xyz/pCoord.w, 1.0);', 'pCoord.z = mapperUBO.ZValue;', '//VTK::Position::Impl']).result;
@@ -161,16 +182,38 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
161
182
 
162
183
  publicAPI.replaceShaderNormal = function (hash, pipeline, vertexInput) {
163
184
  var normalBuffer = vertexInput.getBuffer('normalMC');
185
+ var actor = model.WebGPUActor.getRenderable();
164
186
 
165
187
  if (normalBuffer) {
166
188
  var vDesc = pipeline.getShaderDescription('vertex');
167
- vDesc.addOutput('vec3<f32>', 'normalVC', normalBuffer.getArrayInformation()[0].interpolation);
189
+
190
+ if (!vDesc.hasOutput('normalVC')) {
191
+ vDesc.addOutput('vec3<f32>', 'normalVC', normalBuffer.getArrayInformation()[0].interpolation);
192
+ }
193
+
194
+ if (!vDesc.hasOutput('tangentVC')) {
195
+ vDesc.addOutput('vec3<f32>', 'tangentVC', normalBuffer.getArrayInformation()[0].interpolation);
196
+ }
197
+
198
+ if (!vDesc.hasOutput('bitangentVC')) {
199
+ vDesc.addOutput('vec3<f32>', 'bitangentVC', normalBuffer.getArrayInformation()[0].interpolation);
200
+ }
201
+
168
202
  var code = vDesc.getCode();
169
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' output.normalVC = normalize((rendererUBO.WCVCNormals * mapperUBO.MCWCNormals * normalMC).xyz);']).result;
203
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' output.normalVC = normalize((rendererUBO.WCVCNormals * mapperUBO.MCWCNormals * normalMC).xyz);', // This is just an approximation, but it happens to work extremely well
204
+ // It only works well for normals that are head on and not super angled though
205
+ // Definitely needs to be replaced
206
+ ' var c1: vec3<f32> = cross(output.normalVC, vec3<f32>(0, 0, 1));', ' var c2: vec3<f32> = cross(output.normalVC, vec3<f32>(0, 1, 0));', ' var tangent: vec3<f32> = mix(c1, c2, distance(c1, c2));', ' output.tangentVC = normalize(tangent);', ' output.bitangentVC = normalize(cross(output.normalVC, tangent));']).result;
170
207
  vDesc.setCode(code);
171
208
  var fDesc = pipeline.getShaderDescription('fragment');
172
209
  code = fDesc.getCode();
173
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' var normal: vec3<f32> = input.normalVC;', ' if (!input.frontFacing) { normal = -normal; }']).result;
210
+
211
+ if (actor.getProperty().getNormalTexture()) {
212
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' var normal: vec3<f32> = input.normalVC;', ' if (!input.frontFacing) { normal = -normal; }', ' var tangent: vec3<f32> = input.tangentVC;', ' var bitangent: vec3<f32> = input.bitangentVC;', ' var TCVCMatrix: mat3x3<f32> = mat3x3<f32>(', ' tangent.x, bitangent.x, normal.x,', ' tangent.y, bitangent.y, normal.y,', ' tangent.z, bitangent.z, normal.z,', ' );', ' normal = TCVCMatrix * (_normalMap.xyz * 2 - 1);', ' normal = mix(input.normalVC, normal, mapperUBO.NormalStrength);', ' normal = normalize(normal);']).result;
213
+ } else {
214
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Normal::Impl', [' var normal: vec3<f32> = input.normalVC;', ' if (!input.frontFacing) { normal = -normal; }', ' normal = normalize(normal);']).result;
215
+ }
216
+
174
217
  fDesc.setCode(code);
175
218
  }
176
219
  };
@@ -179,14 +222,24 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
179
222
  // fragment shader code. That is the lighting trigger.
180
223
 
181
224
  publicAPI.replaceShaderLight = function (hash, pipeline, vertexInput) {
225
+ if (hash.includes('sel')) return;
226
+ var vDesc = pipeline.getShaderDescription('vertex');
227
+ if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');
182
228
  var fDesc = pipeline.getShaderDescription('fragment');
183
- var code = fDesc.getCode();
184
-
185
- if (code.includes('var normal')) {
186
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', [' var df: f32 = max(0.0, normal.z);', ' var sf: f32 = pow(df, mapperUBO.SpecularPower);', ' var diffuse: vec3<f32> = df * diffuseColor.rgb;', ' var specular: vec3<f32> = sf * mapperUBO.SpecularColor.rgb * mapperUBO.SpecularColor.a;']).result;
187
- fDesc.setCode(code);
229
+ var code = fDesc.getCode(); // Code that runs if the fragment shader includes normals
230
+
231
+ if (code.includes('var normal:') && model.useRendererMatrix && !isEdges(hash) && !model.is2D && !hash.includes('sel')) {
232
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', [// Constants
233
+ ' var pi: f32 = 3.14159265359;', // Vectors needed for light calculations
234
+ ' var fragPos: vec3<f32> = vec3<f32>(input.vertexVC.xyz);', ' var V: vec3<f32> = mix(normalize(-fragPos), vec3<f32>(0, 0, 1), f32(rendererUBO.cameraParallel)); // View Vector', // Values needed for light calculations
235
+ ' var baseColor: vec3<f32> = _diffuseMap.rgb * diffuseColor.rgb;', ' var roughness: f32 = max(0.000001, mapperUBO.Roughness * _roughnessMap.r);', // Need to have a different way of sampling greyscale values aside from .r
236
+ ' var metallic: f32 = mapperUBO.Metallic * _metallicMap.r;', ' var alpha: f32 = roughness*roughness;', ' var ior: f32 = mapperUBO.BaseIOR;', ' var k: f32 = alpha*alpha / 2;', // Split diffuse and specular components
237
+ ' var diffuse: vec3<f32> = vec3<f32>(0.);', ' var specular: vec3<f32> = vec3<f32>(0.);', ' var emission: vec3<f32> = _emissionMap.rgb * mapperUBO.Emission;', // Summing diffuse and specular components of directional lights
238
+ ' {', ' var i: i32 = 0;', ' loop {', ' if !(i < rendererUBO.LightCount) { break; }', ' switch (i32(rendererLightSSBO.values[i].LightData.x)) {', ' // Point Light', ' case 0 {', ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;', ' var pos: vec3<f32> = (rendererLightSSBO.values[i].LightPos).xyz;', ' var calculated: PBRData = calcPointLight(normal, V, fragPos, ior, roughness, metallic, pos, color, baseColor);', ' diffuse += max(vec3<f32>(0), calculated.diffuse);', ' specular += max(vec3<f32>(0), calculated.specular);', ' }', ' // Directional light', ' case 1 {', ' var dir: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz;', ' dir = normalize(dir);', ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;', ' var calculated: PBRData = calcDirectionalLight(normal, V, ior, roughness, metallic, dir, color, baseColor); // diffuseColor.rgb needs to be fixed with a more dynamic diffuse color', ' diffuse += max(vec3<f32>(0), calculated.diffuse);', ' specular += max(vec3<f32>(0), calculated.specular);', ' }', ' // Spot Light', ' case 2 {', ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;', ' var pos: vec3<f32> = (rendererLightSSBO.values[i].LightPos).xyz;', ' var dir: vec3<f32> = (rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz;', ' dir = normalize(dir);', ' var cones: vec2<f32> = vec2<f32>(rendererLightSSBO.values[i].LightData.y, rendererLightSSBO.values[i].LightData.z);', ' var calculated: PBRData = calcSpotLight(normal, V, fragPos, ior, roughness, metallic, pos, dir, cones, color, baseColor);', ' diffuse += max(vec3<f32>(0), calculated.diffuse);', ' specular += max(vec3<f32>(0), calculated.specular);', ' }', ' default { continue; }', ' }', ' continuing { i++; }', ' }', ' }', // Final variables for combining specular and diffuse
239
+ ' var fresnel: f32 = schlickFresnelIOR(V, normal, ior, k); // Fresnel', ' fresnel = min(1, fresnel);', ' // This could be controlled with its own variable (that isnt base color) for better artistic control', ' var fresnelMetallic: vec3<f32> = schlickFresnelRGB(V, normal, baseColor); // Fresnel for metal, takes color into account', ' var kS: vec3<f32> = mix(vec3<f32>(fresnel), fresnelMetallic, metallic);', ' kS = min(vec3<f32>(1), kS);', ' var kD: vec3<f32> = (1.0 - kS) * (1.0 - metallic);', ' var PBR: vec3<f32> = mapperUBO.DiffuseIntensity*kD*diffuse + kS*specular;', ' PBR += emission;', ' computedColor = vec4<f32>(PBR, mapperUBO.Opacity);']).result;
240
+ fDesc.setCode(code); // If theres no normals, just set the specular color to be flat
188
241
  } else {
189
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', [' var diffuse: vec3<f32> = diffuseColor.rgb;', ' var specular: vec3<f32> = mapperUBO.SpecularColor.rgb * mapperUBO.SpecularColor.a;']).result;
242
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Light::Impl', [' var diffuse: vec3<f32> = diffuseColor.rgb;', ' var specular: vec3<f32> = mapperUBO.SpecularColor.rgb * mapperUBO.SpecularColor.a;', ' computedColor = vec4<f32>(diffuse, mapperUBO.Opacity);']).result;
190
243
  fDesc.setCode(code);
191
244
  }
192
245
  };
@@ -194,6 +247,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
194
247
  model.shaderReplacements.set('replaceShaderLight', publicAPI.replaceShaderLight);
195
248
 
196
249
  publicAPI.replaceShaderColor = function (hash, pipeline, vertexInput) {
250
+ // By default, set the colors to be flat
197
251
  if (isEdges(hash)) {
198
252
  var _fDesc = pipeline.getShaderDescription('fragment');
199
253
 
@@ -204,15 +258,18 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
204
258
  _fDesc.setCode(_code);
205
259
 
206
260
  return;
207
- }
261
+ } // If there's no vertex color buffer return the shader as is
262
+
208
263
 
209
264
  var colorBuffer = vertexInput.getBuffer('colorVI');
210
- if (!colorBuffer) return;
265
+ if (!colorBuffer) return; // Modifies the vertex shader to include the vertex colors and interpolation in the outputs
266
+
211
267
  var vDesc = pipeline.getShaderDescription('vertex');
212
268
  vDesc.addOutput('vec4<f32>', 'color', colorBuffer.getArrayInformation()[0].interpolation);
213
269
  var code = vDesc.getCode();
214
270
  code = vtkWebGPUShaderCache.substitute(code, '//VTK::Color::Impl', [' output.color = colorVI;']).result;
215
- vDesc.setCode(code);
271
+ vDesc.setCode(code); // Sets the fragment shader to accept the color inputs from the vertex shader
272
+
216
273
  var fDesc = pipeline.getShaderDescription('fragment');
217
274
  code = fDesc.getCode();
218
275
  code = vtkWebGPUShaderCache.substitute(code, '//VTK::Color::Impl', ['ambientColor = input.color;', 'diffuseColor = input.color;', 'opacity = mapperUBO.Opacity * input.color.a;']).result;
@@ -222,27 +279,70 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
222
279
  model.shaderReplacements.set('replaceShaderColor', publicAPI.replaceShaderColor);
223
280
 
224
281
  publicAPI.replaceShaderTCoord = function (hash, pipeline, vertexInput) {
282
+ var _actor$getProperty$ge, _actor$getProperty, _actor$getProperty$ge2, _actor$getProperty$ge4, _actor$getProperty3, _actor$getProperty$ge5, _actor$getProperty$ge6, _actor$getProperty4, _actor$getProperty$ge7, _actor$getProperty$ge8, _actor$getProperty5, _actor$getProperty$ge9, _actor$getProperty$ge10, _actor$getProperty6, _actor$getProperty$ge11, _actor$getProperty$ge12, _actor$getProperty7, _actor$getProperty$ge13;
283
+
225
284
  if (!vertexInput.hasAttribute('tcoord')) return;
226
285
  var vDesc = pipeline.getShaderDescription('vertex');
227
286
  var tcoords = vertexInput.getBuffer('tcoord');
228
287
  var numComp = vtkWebGPUTypes.getNumberOfComponentsFromBufferFormat(tcoords.getArrayInformation()[0].format);
229
288
  var code = vDesc.getCode();
230
289
  vDesc.addOutput("vec".concat(numComp, "<f32>"), 'tcoordVS');
231
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', [' output.tcoordVS = tcoord;']).result;
290
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', [' output.tcoordVS = tcoord;' // Ensure that UV coordinates are always between 0-1
291
+ ]).result;
232
292
  vDesc.setCode(code);
293
+ if (model.is2D) return;
233
294
  var fDesc = pipeline.getShaderDescription('fragment');
234
- code = fDesc.getCode(); // todo handle multiple textures? Blend multiply ?
295
+ code = fDesc.getCode();
296
+ var actor = model.WebGPUActor.getRenderable();
235
297
 
236
- if (model.textures.length) {
237
- var tdims = model.textures[0].getDimensions();
298
+ var checkDims = function checkDims(texture) {
299
+ if (!texture) return false;
300
+ var dims = texture.getDimensionality();
301
+ return dims === numComp;
302
+ };
238
303
 
239
- if (tdims === numComp) {
240
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', ['var tcolor: vec4<f32> = textureSample(Texture0, Texture0Sampler, input.tcoordVS);', 'computedColor = computedColor*tcolor;']).result;
241
- } else {
242
- console.log("mismatched texture coord dimension ".concat(numComp, " with texture map dimension ").concat(tdims));
304
+ var usedTextures = [];
305
+
306
+ if ((_actor$getProperty$ge = (_actor$getProperty = actor.getProperty()).getDiffuseTexture) !== null && _actor$getProperty$ge !== void 0 && (_actor$getProperty$ge2 = _actor$getProperty$ge.call(_actor$getProperty)) !== null && _actor$getProperty$ge2 !== void 0 && _actor$getProperty$ge2.getImageLoaded() || actor.getTextures()[0] || model.colorTexture) {
307
+ var _actor$getProperty$ge3, _actor$getProperty2;
308
+
309
+ if ( // Chained or statements here are questionable
310
+ checkDims((_actor$getProperty$ge3 = (_actor$getProperty2 = actor.getProperty()).getDiffuseTexture) === null || _actor$getProperty$ge3 === void 0 ? void 0 : _actor$getProperty$ge3.call(_actor$getProperty2)) || checkDims(actor.getTextures()[0]) || checkDims(model.colorTexture)) {
311
+ usedTextures.push('_diffuseMap = textureSample(DiffuseTexture, DiffuseTextureSampler, input.tcoordVS);');
312
+ }
313
+ }
314
+
315
+ if ((_actor$getProperty$ge4 = (_actor$getProperty3 = actor.getProperty()).getRoughnessTexture) !== null && _actor$getProperty$ge4 !== void 0 && (_actor$getProperty$ge5 = _actor$getProperty$ge4.call(_actor$getProperty3)) !== null && _actor$getProperty$ge5 !== void 0 && _actor$getProperty$ge5.getImageLoaded()) {
316
+ if (checkDims(actor.getProperty().getRoughnessTexture())) {
317
+ usedTextures.push('_roughnessMap = textureSample(RoughnessTexture, RoughnessTextureSampler, input.tcoordVS);');
318
+ }
319
+ }
320
+
321
+ if ((_actor$getProperty$ge6 = (_actor$getProperty4 = actor.getProperty()).getMetallicTexture) !== null && _actor$getProperty$ge6 !== void 0 && (_actor$getProperty$ge7 = _actor$getProperty$ge6.call(_actor$getProperty4)) !== null && _actor$getProperty$ge7 !== void 0 && _actor$getProperty$ge7.getImageLoaded()) {
322
+ if (checkDims(actor.getProperty().getMetallicTexture())) {
323
+ usedTextures.push('_metallicMap = textureSample(MetallicTexture, MetallicTextureSampler, input.tcoordVS);');
324
+ }
325
+ }
326
+
327
+ if ((_actor$getProperty$ge8 = (_actor$getProperty5 = actor.getProperty()).getNormalTexture) !== null && _actor$getProperty$ge8 !== void 0 && (_actor$getProperty$ge9 = _actor$getProperty$ge8.call(_actor$getProperty5)) !== null && _actor$getProperty$ge9 !== void 0 && _actor$getProperty$ge9.getImageLoaded()) {
328
+ if (checkDims(actor.getProperty().getNormalTexture())) {
329
+ usedTextures.push('_normalMap = textureSample(NormalTexture, NormalTextureSampler, input.tcoordVS);');
243
330
  }
244
331
  }
245
332
 
333
+ if ((_actor$getProperty$ge10 = (_actor$getProperty6 = actor.getProperty()).getAmbientOcclusionTexture) !== null && _actor$getProperty$ge10 !== void 0 && (_actor$getProperty$ge11 = _actor$getProperty$ge10.call(_actor$getProperty6)) !== null && _actor$getProperty$ge11 !== void 0 && _actor$getProperty$ge11.getImageLoaded()) {
334
+ if (checkDims(actor.getProperty().getAmbientOcclusionTexture())) {
335
+ usedTextures.push('_ambientOcclusionMap = textureSample(AmbientOcclusionTexture, AmbientOcclusionTextureSampler, input.tcoordVS);');
336
+ }
337
+ }
338
+
339
+ if ((_actor$getProperty$ge12 = (_actor$getProperty7 = actor.getProperty()).getEmissionTexture) !== null && _actor$getProperty$ge12 !== void 0 && (_actor$getProperty$ge13 = _actor$getProperty$ge12.call(_actor$getProperty7)) !== null && _actor$getProperty$ge13 !== void 0 && _actor$getProperty$ge13.getImageLoaded()) {
340
+ if (checkDims(actor.getProperty().getEmissionTexture())) {
341
+ usedTextures.push('_emissionMap = textureSample(EmissionTexture, EmissionTextureSampler, input.tcoordVS);');
342
+ }
343
+ }
344
+
345
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::TCoord::Impl', usedTextures).result;
246
346
  fDesc.setCode(code);
247
347
  };
248
348
 
@@ -309,7 +409,8 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
309
409
  default:
310
410
  return 'line-list';
311
411
  }
312
- };
412
+ }; // TODO: calculate tangents
413
+
313
414
 
314
415
  publicAPI.buildVertexInput = function () {
315
416
  var _model$renderable$get, _model$renderable;
@@ -379,7 +480,9 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
379
480
 
380
481
  if (!model.is2D && ( // no lighting on Property2D
381
482
  usage === BufferUsage.Triangles || usage === BufferUsage.Strips)) {
382
- var normals = pd.getPointData().getNormals();
483
+ var normals = pd.getPointData().getNormals(); // https://vtk.org/doc/nightly/html/classvtkPolyDataTangents.html
484
+ // Need to find some way of using precomputed tangents (or computing new ones)
485
+
383
486
  var _buffRequest2 = {
384
487
  format: 'snorm8x4',
385
488
  indexBuffer: indexBuffer,
@@ -466,7 +569,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
466
569
  };
467
570
 
468
571
  publicAPI.updateTextures = function () {
469
- var _model$renderable$get2, _model$renderable2;
572
+ var _model$renderable$get2, _model$renderable2, _actor$getProperty$ge14, _actor$getProperty8, _actor$getProperty$ge15, _actor$getProperty9, _actor$getProperty$ge16, _actor$getProperty10, _actor$getProperty$ge17, _actor$getProperty11, _actor$getProperty$ge18, _actor$getProperty12, _actor$getProperty$ge19, _actor$getProperty13, _renderer$getBackgrou;
470
573
 
471
574
  // we keep track of new and used textures so
472
575
  // that we can clean up any unused textures so we don't hold onto them
@@ -483,28 +586,69 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
483
586
  }
484
587
 
485
588
  model.colorTexture.setInputData(idata);
486
- newTextures.push(model.colorTexture);
589
+ newTextures.push(['Diffuse', model.colorTexture]);
487
590
  } // actor textures?
488
591
 
489
592
 
490
593
  var actor = model.WebGPUActor.getRenderable();
491
- var textures = actor.getTextures();
594
+ var renderer = model.WebGPURenderer.getRenderable(); // Reusing the old code for new and old textures, just loading in from properties instead of actor.getTextures()
595
+
596
+ var textures = []; // Feels like there should be a better way than individually adding all
597
+
598
+ if ((_actor$getProperty$ge14 = (_actor$getProperty8 = actor.getProperty()).getDiffuseTexture) !== null && _actor$getProperty$ge14 !== void 0 && _actor$getProperty$ge14.call(_actor$getProperty8)) {
599
+ var pair = ['Diffuse', actor.getProperty().getDiffuseTexture()];
600
+ textures.push(pair);
601
+ }
602
+
603
+ if (actor.getTextures()[0]) {
604
+ var _pair = ['Diffuse', actor.getTextures()[0]];
605
+ textures.push(_pair);
606
+ }
607
+
608
+ if ((_actor$getProperty$ge15 = (_actor$getProperty9 = actor.getProperty()).getRoughnessTexture) !== null && _actor$getProperty$ge15 !== void 0 && _actor$getProperty$ge15.call(_actor$getProperty9)) {
609
+ var _pair2 = ['Roughness', actor.getProperty().getRoughnessTexture()];
610
+ textures.push(_pair2);
611
+ }
612
+
613
+ if ((_actor$getProperty$ge16 = (_actor$getProperty10 = actor.getProperty()).getMetallicTexture) !== null && _actor$getProperty$ge16 !== void 0 && _actor$getProperty$ge16.call(_actor$getProperty10)) {
614
+ var _pair3 = ['Metallic', actor.getProperty().getMetallicTexture()];
615
+ textures.push(_pair3);
616
+ }
617
+
618
+ if ((_actor$getProperty$ge17 = (_actor$getProperty11 = actor.getProperty()).getNormalTexture) !== null && _actor$getProperty$ge17 !== void 0 && _actor$getProperty$ge17.call(_actor$getProperty11)) {
619
+ var _pair4 = ['Normal', actor.getProperty().getNormalTexture()];
620
+ textures.push(_pair4);
621
+ }
622
+
623
+ if ((_actor$getProperty$ge18 = (_actor$getProperty12 = actor.getProperty()).getAmbientOcclusionTexture) !== null && _actor$getProperty$ge18 !== void 0 && _actor$getProperty$ge18.call(_actor$getProperty12)) {
624
+ var _pair5 = ['AmbientOcclusion', actor.getProperty().getAmbientOcclusionTexture()];
625
+ textures.push(_pair5);
626
+ }
627
+
628
+ if ((_actor$getProperty$ge19 = (_actor$getProperty13 = actor.getProperty()).getEmissionTexture) !== null && _actor$getProperty$ge19 !== void 0 && _actor$getProperty$ge19.call(_actor$getProperty13)) {
629
+ var _pair6 = ['Emission', actor.getProperty().getEmissionTexture()];
630
+ textures.push(_pair6);
631
+ }
632
+
633
+ if ((_renderer$getBackgrou = renderer.getBackgroundTexture) !== null && _renderer$getBackgrou !== void 0 && _renderer$getBackgrou.call(renderer)) {
634
+ var _pair7 = ['Background', renderer.getBackgroundTexture()];
635
+ textures.push(_pair7);
636
+ }
492
637
 
493
638
  for (var i = 0; i < textures.length; i++) {
494
- if (textures[i].getInputData() || textures[i].getJsImageData() || textures[i].getCanvas()) {
639
+ if (textures[i][1].getInputData() || textures[i][1].getJsImageData() || textures[i][1].getCanvas()) {
495
640
  newTextures.push(textures[i]);
496
641
  }
497
642
 
498
- if (textures[i].getImage() && textures[i].getImageLoaded()) {
643
+ if (textures[i][1].getImage() && textures[i][1].getImageLoaded()) {
499
644
  newTextures.push(textures[i]);
500
645
  }
501
646
  }
502
647
 
503
- var usedCount = 0;
504
-
505
648
  for (var _i = 0; _i < newTextures.length; _i++) {
506
- var srcTexture = newTextures[_i];
507
- var newTex = model.device.getTextureManager().getTextureForVTKTexture(srcTexture);
649
+ var srcTexture = newTextures[_i][1];
650
+ var textureName = newTextures[_i][0];
651
+ var newTex = model.device.getTextureManager().getTextureForVTKTexture(srcTexture); // Generates hash
508
652
 
509
653
  if (newTex.getReady()) {
510
654
  // is this a new texture
@@ -512,7 +656,6 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
512
656
 
513
657
  for (var t = 0; t < model.textures.length; t++) {
514
658
  if (model.textures[t] === newTex) {
515
- usedCount++;
516
659
  found = true;
517
660
  usedTextures[t] = true;
518
661
  }
@@ -520,7 +663,7 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
520
663
 
521
664
  if (!found) {
522
665
  usedTextures[model.textures.length] = true;
523
- var tview = newTex.createView("Texture".concat(usedCount++));
666
+ var tview = newTex.createView("".concat(textureName, "Texture"));
524
667
  model.textures.push(newTex);
525
668
  model.textureViews.push(tview);
526
669
  var interpolate = srcTexture.getInterpolate() ? 'linear' : 'nearest';
@@ -623,14 +766,15 @@ var DEFAULT_VALUES = {
623
766
  }; // ----------------------------------------------------------------------------
624
767
 
625
768
  function extend(publicAPI, model) {
626
- var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
627
- Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance
769
+ var initiaLalues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
770
+ Object.assign(model, DEFAULT_VALUES, initiaLalues); // Inheritance
628
771
 
629
- vtkWebGPUSimpleMapper.extend(publicAPI, model, initialValues);
772
+ vtkWebGPUSimpleMapper.extend(publicAPI, model, initiaLalues);
630
773
  model.fragmentShaderTemplate = vtkWebGPUPolyDataFS;
631
774
  model.vertexShaderTemplate = vtkWebGPUPolyDataVS;
632
775
  model._tmpMat3 = mat3.identity(new Float64Array(9));
633
- model._tmpMat4 = mat4.identity(new Float64Array(16));
776
+ model._tmpMat4 = mat4.identity(new Float64Array(16)); // UBO
777
+
634
778
  model.UBO = vtkWebGPUUniformBuffer.newInstance({
635
779
  label: 'mapperUBO'
636
780
  });
@@ -643,12 +787,21 @@ function extend(publicAPI, model) {
643
787
  model.UBO.addEntry('SpecularColor', 'vec4<f32>');
644
788
  model.UBO.addEntry('AmbientIntensity', 'f32');
645
789
  model.UBO.addEntry('DiffuseIntensity', 'f32');
790
+ model.UBO.addEntry('Roughness', 'f32');
791
+ model.UBO.addEntry('Metallic', 'f32');
792
+ model.UBO.addEntry('Ambient', 'f32');
793
+ model.UBO.addEntry('Normal', 'f32');
794
+ model.UBO.addEntry('Emission', 'f32');
795
+ model.UBO.addEntry('NormalStrength', 'f32');
796
+ model.UBO.addEntry('BaseIOR', 'f32');
646
797
  model.UBO.addEntry('SpecularIntensity', 'f32');
647
798
  model.UBO.addEntry('LineWidth', 'f32');
648
799
  model.UBO.addEntry('Opacity', 'f32');
649
- model.UBO.addEntry('SpecularPower', 'f32');
650
800
  model.UBO.addEntry('ZValue', 'f32');
651
- model.UBO.addEntry('PropID', 'u32'); // Build VTK API
801
+ model.UBO.addEntry('PropID', 'u32');
802
+ model.UBO.addEntry('ClipNear', 'f32');
803
+ model.UBO.addEntry('ClipFar', 'f32');
804
+ model.UBO.addEntry('Time', 'u32'); // Build VTK API
652
805
 
653
806
  setGet(publicAPI, model, ['cellArray', 'currentInput', 'cellOffset', 'is2D', 'primitiveType', 'renderEncoder']);
654
807
  model.textures = []; // Object methods
@@ -29,8 +29,9 @@ function vtkWebGPUGlyph3DCellArrayMapper(publicAPI, model) {
29
29
  var vDesc = pipeline.getShaderDescription('vertex');
30
30
  vDesc.addBuiltinInput('u32', '@builtin(instance_index) instanceIndex');
31
31
  vDesc.addBuiltinOutput('vec4<f32>', '@builtin(position) Position');
32
+ if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec3<f32>', 'vertexVC');
32
33
  var code = vDesc.getCode();
33
- code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', [' output.Position = rendererUBO.SCPCMatrix*mapperUBO.BCSCMatrix', ' *glyphSSBO.values[input.instanceIndex].matrix', ' *vertexBC;']).result;
34
+ code = vtkWebGPUShaderCache.substitute(code, '//VTK::Position::Impl', [' var vertexSC: vec4<f32> = mapperUBO.BCSCMatrix*glyphSSBO.values[input.instanceIndex].matrix*vertexBC;', ' output.vertexVC = (rendererUBO.SCVCMatrix*vertexSC).xyz;', ' output.Position = rendererUBO.SCPCMatrix*vertexSC;']).result;
34
35
  vDesc.setCode(code);
35
36
  };
36
37
 
@@ -1,18 +1,41 @@
1
1
  import { mat4, vec3 } from 'gl-matrix';
2
2
  import { newInstance as newInstance$1, obj, get, getArray, setGet, vtkDebugMacro as vtkDebugMacro$1 } from '../../macros.js';
3
+ import { r as radiansFromDegrees } from '../../Common/Core/Math/index.js';
3
4
  import vtkViewNode from '../SceneGraph/ViewNode.js';
4
5
  import vtkWebGPUBindGroup from './BindGroup.js';
5
6
  import vtkWebGPUFullScreenQuad from './FullScreenQuad.js';
7
+ import vtkWebGPUStorageBuffer from './StorageBuffer.js';
6
8
  import vtkWebGPUUniformBuffer from './UniformBuffer.js';
7
9
  import { registerOverride } from './ViewNodeFactory.js';
8
10
 
9
11
  var vtkDebugMacro = vtkDebugMacro$1;
10
- var clearFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = mapperUBO.BackgroundColor;\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n"; // ----------------------------------------------------------------------------
12
+ var clearFragTemplate = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::TCoord::Dec\n\n//VTK::RenderEncoder::Dec\n\n//VTK::IOStructs::Dec\n\n@fragment\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output: fragmentOutput;\n\n var computedColor: vec4<f32> = mapperUBO.BackgroundColor;\n\n //VTK::RenderEncoder::Impl\n return output;\n}\n"; // Light type index gives either 0, 1, or 2 which indicates what type of light there is.
13
+ // While technically, there are only spot and directional lights, within the CellArrayMapper
14
+ // there is a third, positional light. It is technically just a variant of a spot light with
15
+ // a cone angle of 90 or above, however certain calculations can be skipped if it is treated
16
+ // separately.
17
+ // The mappings are shown below:
18
+ // 0 -> positional light
19
+ // 1 -> directional light
20
+ // 2 -> spot light
21
+
22
+ function getLightTypeIndex(light) {
23
+ if (light.getPositional()) {
24
+ if (light.getConeAngle() >= 90) {
25
+ return 0;
26
+ }
27
+
28
+ return 2;
29
+ }
30
+
31
+ return 1;
32
+ } // ----------------------------------------------------------------------------
11
33
  // vtkWebGPURenderer methods
12
34
  // ----------------------------------------------------------------------------
13
35
 
14
36
  /* eslint-disable no-bitwise */
15
37
 
38
+
16
39
  function vtkWebGPURenderer(publicAPI, model) {
17
40
  // Set our className
18
41
  model.classHierarchy.push('vtkWebGPURenderer'); // Builds myself.
@@ -108,6 +131,7 @@ function vtkWebGPURenderer(publicAPI, model) {
108
131
  model.UBO.setArray('SCVCMatrix', keyMats.scvc);
109
132
  model.UBO.setArray('VCPCMatrix', keyMats.vcpc);
110
133
  model.UBO.setArray('WCVCNormals', keyMats.normalMatrix);
134
+ model.UBO.setValue('LightCount', model.renderable.getLights().length);
111
135
  var tsize = publicAPI.getYInvertedTiledSizeAndOrigin();
112
136
  model.UBO.setArray('viewportSize', [tsize.usize, tsize.vsize]);
113
137
  model.UBO.setValue('cameraParallel', model.camera.getParallelProjection());
@@ -118,6 +142,79 @@ function vtkWebGPURenderer(publicAPI, model) {
118
142
  }
119
143
  };
120
144
 
145
+ publicAPI.updateSSBO = function () {
146
+ var lights = model.renderable.getLights();
147
+ var keyMats = model.webgpuCamera.getKeyMatrices(publicAPI);
148
+ var lightTimeString = "".concat(model.renderable.getMTime());
149
+
150
+ for (var i = 0; i < lights.length; i++) {
151
+ lightTimeString += lights[i].getMTime();
152
+ }
153
+
154
+ if (lightTimeString !== model.lightTimeString) {
155
+ var lightPosArray = new Float32Array(lights.length * 4);
156
+ var lightDirArray = new Float32Array(lights.length * 4);
157
+ var lightColorArray = new Float32Array(lights.length * 4);
158
+ var lightTypeArray = new Float32Array(lights.length * 4);
159
+
160
+ for (var _i = 0; _i < lights.length; _i++) {
161
+ var offset = _i * 4; // Position
162
+
163
+ var viewCoordinatePosition = lights[_i].getPosition();
164
+
165
+ vec3.transformMat4(viewCoordinatePosition, viewCoordinatePosition, keyMats.wcvc); // console.log(viewCoordinatePosition);
166
+ // viewCoordinatePosition
167
+
168
+ lightPosArray[offset] = viewCoordinatePosition[0];
169
+ lightPosArray[offset + 1] = viewCoordinatePosition[1];
170
+ lightPosArray[offset + 2] = viewCoordinatePosition[2];
171
+ lightPosArray[offset + 3] = 0; // Rotation (All are negative to correct for -Z being forward)
172
+
173
+ lightDirArray[offset] = -lights[_i].getDirection()[0];
174
+ lightDirArray[offset + 1] = -lights[_i].getDirection()[1];
175
+ lightDirArray[offset + 2] = -lights[_i].getDirection()[2];
176
+ lightDirArray[offset + 3] = 0; // Color
177
+
178
+ lightColorArray[offset] = lights[_i].getColor()[0];
179
+ lightColorArray[offset + 1] = lights[_i].getColor()[1];
180
+ lightColorArray[offset + 2] = lights[_i].getColor()[2];
181
+ lightColorArray[offset + 3] = lights[_i].getIntensity() * 5; // arbitrary multiplication to fix the dullness of low value PBR lights
182
+ // Type
183
+
184
+ lightTypeArray[offset] = getLightTypeIndex(lights[_i]); // Type
185
+
186
+ lightTypeArray[offset + 1] = Math.cos(radiansFromDegrees(lights[_i].getConeAngle())); // Inner Phi, should probably do some check on these to make sure they dont excede limits
187
+
188
+ lightTypeArray[offset + 2] = Math.cos(radiansFromDegrees(lights[_i].getConeAngle() + lights[_i].getConeFalloff())); // Outer Phi
189
+
190
+ lightTypeArray[offset + 3] = 0;
191
+ } // Im not sure how correct this is, but this is what the example does
192
+ // https://kitware.github.io/vtk-js/api/Rendering_WebGPU_VolumePassFSQ.html
193
+
194
+
195
+ model.SSBO.clearData();
196
+ model.SSBO.setNumberOfInstances(lights.length);
197
+ model.SSBO.addEntry('LightPos', 'vec4<f32>'); // Position
198
+
199
+ model.SSBO.addEntry('LightDir', 'vec4<f32>'); // Direction
200
+
201
+ model.SSBO.addEntry('LightColor', 'vec4<f32>'); // Color (r, g, b, intensity)
202
+
203
+ model.SSBO.addEntry('LightData', 'vec4<f32>'); // Other data (type, etc, etc, etc)
204
+
205
+ model.SSBO.setAllInstancesFromArray('LightPos', lightPosArray);
206
+ model.SSBO.setAllInstancesFromArray('LightDir', lightDirArray);
207
+ model.SSBO.setAllInstancesFromArray('LightColor', lightColorArray);
208
+ model.SSBO.setAllInstancesFromArray('LightData', lightTypeArray);
209
+
210
+ var device = model._parent.getDevice();
211
+
212
+ model.SSBO.send(device);
213
+ }
214
+
215
+ model.lightTimeString = lightTimeString;
216
+ };
217
+
121
218
  publicAPI.scissorAndViewport = function (encoder) {
122
219
  var tsize = publicAPI.getYInvertedTiledSizeAndOrigin();
123
220
  encoder.getHandle().setViewport(tsize.lowerLeftU, tsize.lowerLeftV, tsize.usize, tsize.vsize, 0.0, 1.0); // set scissor
@@ -134,6 +231,7 @@ function vtkWebGPURenderer(publicAPI, model) {
134
231
  if (prepass) {
135
232
  model.renderEncoder.begin(model._parent.getCommandEncoder());
136
233
  publicAPI.updateUBO();
234
+ publicAPI.updateSSBO();
137
235
  } else {
138
236
  publicAPI.scissorAndViewport(model.renderEncoder);
139
237
  publicAPI.clear();
@@ -284,7 +382,8 @@ function extend(publicAPI, model) {
284
382
  var initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
285
383
  Object.assign(model, DEFAULT_VALUES, initialValues); // Inheritance
286
384
 
287
- vtkViewNode.extend(publicAPI, model, initialValues);
385
+ vtkViewNode.extend(publicAPI, model, initialValues); // UBO
386
+
288
387
  model.UBO = vtkWebGPUUniformBuffer.newInstance({
289
388
  label: 'rendererUBO'
290
389
  });
@@ -295,11 +394,17 @@ function extend(publicAPI, model) {
295
394
  model.UBO.addEntry('VCPCMatrix', 'mat4x4<f32>');
296
395
  model.UBO.addEntry('WCVCNormals', 'mat4x4<f32>');
297
396
  model.UBO.addEntry('viewportSize', 'vec2<f32>');
298
- model.UBO.addEntry('cameraParallel', 'u32');
397
+ model.UBO.addEntry('LightCount', 'i32');
398
+ model.UBO.addEntry('cameraParallel', 'u32'); // SSBO (Light data)
399
+
400
+ model.SSBO = vtkWebGPUStorageBuffer.newInstance({
401
+ label: 'rendererLightSSBO'
402
+ });
403
+ model.lightTimeString = '';
299
404
  model.bindGroup = vtkWebGPUBindGroup.newInstance({
300
405
  label: 'rendererBG'
301
406
  });
302
- model.bindGroup.setBindables([model.UBO]);
407
+ model.bindGroup.setBindables([model.UBO, model.SSBO]);
303
408
  model.tmpMat4 = mat4.identity(new Float64Array(16));
304
409
  model.stabilizedTime = {};
305
410
  obj(model.stabilizedTime, {
@@ -7,7 +7,7 @@ import { registerOverride } from './ViewNodeFactory.js';
7
7
 
8
8
  var BufferUsage = vtkWebGPUBufferManager.BufferUsage;
9
9
  var vtkErrorMacro = vtkErrorMacro$1;
10
- var vtkWebGPUSphereMapperVS = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::Color::Dec\n\n//VTK::IOStructs::Dec\n\n@vertex\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : vertexOutput;\n\n var vertexVC: vec4<f32> = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.x, vertexBC.y, vertexBC.z, 1.0);\n\n //VTK::Color::Impl\n\n // compute the projected vertex position\n output.centerVC = vertexVC.xyz;\n output.radiusVC = length(offsetMC)*0.5;\n\n // make the triangle face the camera\n if (rendererUBO.cameraParallel == 0u)\n {\n var dir: vec3<f32> = normalize(-vertexVC.xyz);\n var base2: vec3<f32> = normalize(cross(dir,vec3<f32>(1.0,0.0,0.0)));\n var base1: vec3<f32> = cross(base2,dir);\n dir = vertexVC.xyz + offsetMC.x*base1 + offsetMC.y*base2;\n vertexVC = vec4<f32>(dir, 1.0);\n }\n else\n {\n // add in the offset\n var tmp2: vec2<f32> = vertexVC.xy + offsetMC;\n vertexVC = vec4<f32>(tmp2, vertexVC.zw);\n }\n\n output.vertexVC = vertexVC.xyz;\n\n //VTK::Position::Impl\n\n return output;\n}\n"; // ----------------------------------------------------------------------------
10
+ var vtkWebGPUSphereMapperVS = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::Color::Dec\n\n//VTK::IOStructs::Dec\n\n@vertex\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var output : vertexOutput;\n\n var vertexVC: vec4<f32> = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.xyz, 1.0);\n\n //VTK::Color::Impl\n\n // compute the projected vertex position\n output.centerVC = vertexVC.xyz;\n output.radiusVC = length(offsetMC)*0.5;\n\n // make the triangle face the camera\n if (rendererUBO.cameraParallel == 0u)\n {\n var dir: vec3<f32> = normalize(-vertexVC.xyz);\n var base2: vec3<f32> = normalize(cross(dir,vec3<f32>(1.0,0.0,0.0)));\n var base1: vec3<f32> = cross(base2,dir);\n dir = vertexVC.xyz + offsetMC.x*base1 + offsetMC.y*base2;\n vertexVC = vec4<f32>(dir, 1.0);\n }\n else\n {\n // add in the offset\n var tmp2: vec2<f32> = vertexVC.xy + offsetMC;\n vertexVC = vec4<f32>(tmp2, vertexVC.zw);\n }\n\n output.vertexVC = vec4<f32>(vertexVC.xyz, 0.0);\n\n //VTK::Position::Impl\n\n return output;\n}\n"; // ----------------------------------------------------------------------------
11
11
  // vtkWebGPUSphereMapper methods
12
12
  // ----------------------------------------------------------------------------
13
13
 
@@ -32,7 +32,7 @@ function vtkWebGPUSphereMapper(publicAPI, model) {
32
32
 
33
33
  publicAPI.replaceShaderNormal = function (hash, pipeline, vertexInput) {
34
34
  var vDesc = pipeline.getShaderDescription('vertex');
35
- vDesc.addOutput('vec3<f32>', 'vertexVC');
35
+ if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');
36
36
  vDesc.addOutput('vec3<f32>', 'centerVC');
37
37
  vDesc.addOutput('f32', 'radiusVC');
38
38
  var fDesc = pipeline.getShaderDescription('fragment');
@@ -26,7 +26,7 @@ var vtkErrorMacro = vtkErrorMacro$1; // Vertices
26
26
  // 4: 011
27
27
  // 5: 111
28
28
 
29
- var vtkWebGPUStickMapperVS = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::Color::Dec\n\n//VTK::IOStructs::Dec\n\n@vertex\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var offsetsArray: array<vec3<f32>, 12> = array<vec3<f32>, 12>(\n vec3<f32>(-1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n vec3<f32>(-1.0, -1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, 1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n vec3<f32>(1.0, 1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, 1.0),\n vec3<f32>(1.0, 1.0, 1.0),\n vec3<f32>(-1.0, 1.0, 1.0)\n );\n\n var output : vertexOutput;\n\n var vertexVC: vec4<f32> = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.x, vertexBC.y, vertexBC.z, 1.0);\n\n //VTK::Color::Impl\n\n // compute the projected vertex position\n output.centerVC = vertexVC.xyz;\n output.radiusVC = radiusMC;\n output.lengthVC = length(orientMC);\n output.orientVC = (rendererUBO.WCVCNormals * vec4<f32>(normalize(orientMC), 0.0)).xyz;\n\n // make sure it is pointing out of the screen\n if (output.orientVC.z < 0.0)\n {\n output.orientVC = -output.orientVC;\n }\n\n // make the basis\n var xbase: vec3<f32>;\n var ybase: vec3<f32>;\n var dir: vec3<f32> = vec3<f32>(0.0,0.0,1.0);\n if (rendererUBO.cameraParallel == 0u)\n {\n dir = normalize(-vertexVC.xyz);\n }\n if (abs(dot(dir,output.orientVC)) == 1.0)\n {\n xbase = normalize(cross(vec3<f32>(0.0,1.0,0.0),output.orientVC));\n ybase = cross(xbase,output.orientVC);\n }\n else\n {\n xbase = normalize(cross(output.orientVC,dir));\n ybase = cross(output.orientVC,xbase);\n }\n\n\n var vertIdx: u32 = input.vertexIndex % 12u;\n var offsets: vec3<f32> = offsetsArray[vertIdx];\n\n vertexVC = vec4<f32>(vertexVC.xyz +\n output.radiusVC * offsets.x * xbase +\n output.radiusVC * offsets.y * ybase +\n 0.5 * output.lengthVC * offsets.z * output.orientVC, 1.0);\n\n output.vertexVC = vertexVC;\n\n //VTK::Position::Impl\n\n return output;\n}\n"; // ----------------------------------------------------------------------------
29
+ var vtkWebGPUStickMapperVS = "\n//VTK::Renderer::Dec\n\n//VTK::Mapper::Dec\n\n//VTK::Color::Dec\n\n//VTK::IOStructs::Dec\n\n@vertex\nfn main(\n//VTK::IOStructs::Input\n)\n//VTK::IOStructs::Output\n{\n var offsetsArray: array<vec3<f32>, 12> = array<vec3<f32>, 12>(\n vec3<f32>(-1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, -1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n vec3<f32>(-1.0, -1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, 1.0),\n vec3<f32>(1.0, -1.0, 1.0),\n vec3<f32>(1.0, 1.0, 1.0),\n\n vec3<f32>(-1.0, -1.0, 1.0),\n vec3<f32>(1.0, 1.0, 1.0),\n vec3<f32>(-1.0, 1.0, 1.0)\n );\n\n var output : vertexOutput;\n\n var vertexVC: vec4<f32> = rendererUBO.SCVCMatrix * mapperUBO.BCSCMatrix * vec4<f32>(vertexBC.xyz, 1.0);\n\n //VTK::Color::Impl\n\n // compute the projected vertex position\n output.centerVC = vertexVC.xyz;\n output.radiusVC = radiusMC;\n output.lengthVC = length(orientMC);\n output.orientVC = (rendererUBO.WCVCNormals * vec4<f32>(normalize(orientMC), 0.0)).xyz;\n\n // make sure it is pointing out of the screen\n if (output.orientVC.z < 0.0)\n {\n output.orientVC = -output.orientVC;\n }\n\n // make the basis\n var xbase: vec3<f32>;\n var ybase: vec3<f32>;\n var dir: vec3<f32> = vec3<f32>(0.0,0.0,1.0);\n if (rendererUBO.cameraParallel == 0u)\n {\n dir = normalize(-vertexVC.xyz);\n }\n if (abs(dot(dir,output.orientVC)) == 1.0)\n {\n xbase = normalize(cross(vec3<f32>(0.0,1.0,0.0),output.orientVC));\n ybase = cross(xbase,output.orientVC);\n }\n else\n {\n xbase = normalize(cross(output.orientVC,dir));\n ybase = cross(output.orientVC,xbase);\n }\n\n\n var vertIdx: u32 = input.vertexIndex % 12u;\n var offsets: vec3<f32> = offsetsArray[vertIdx];\n\n vertexVC = vec4<f32>(vertexVC.xyz +\n output.radiusVC * offsets.x * xbase +\n output.radiusVC * offsets.y * ybase +\n 0.5 * output.lengthVC * offsets.z * output.orientVC, 1.0);\n\n output.vertexVC = vertexVC;\n\n //VTK::Position::Impl\n\n return output;\n}\n"; // ----------------------------------------------------------------------------
30
30
  // vtkWebGPUStickMapper methods
31
31
  // ----------------------------------------------------------------------------
32
32
 
@@ -51,7 +51,7 @@ function vtkWebGPUStickMapper(publicAPI, model) {
51
51
 
52
52
  publicAPI.replaceShaderNormal = function (hash, pipeline, vertexInput) {
53
53
  var vDesc = pipeline.getShaderDescription('vertex');
54
- vDesc.addOutput('vec4<f32>', 'vertexVC');
54
+ if (!vDesc.hasOutput('vertexVC')) vDesc.addOutput('vec4<f32>', 'vertexVC');
55
55
  vDesc.addOutput('vec3<f32>', 'centerVC');
56
56
  vDesc.addOutput('vec3<f32>', 'orientVC');
57
57
  vDesc.addOutput('f32', 'radiusVC');
@@ -184,8 +184,12 @@ function vtkWebGPUTexture(publicAPI, model) {
184
184
  return tDetails.numComponents;
185
185
  };
186
186
 
187
- publicAPI.getDimensions = function () {
188
- return model.depth > 1 ? 3 : 2;
187
+ publicAPI.getDimensionality = function () {
188
+ var dims = 0;
189
+ if (model.width > 1) dims++;
190
+ if (model.height > 1) dims++;
191
+ if (model.depth > 1) dims++;
192
+ return dims;
189
193
  };
190
194
 
191
195
  publicAPI.resizeToMatch = function (tex) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitware/vtk.js",
3
- "version": "25.1.3",
3
+ "version": "25.2.0",
4
4
  "description": "Visualization Toolkit for the Web",
5
5
  "keywords": [
6
6
  "3d",