@equinor/videx-3d 3.0.0 → 3.1.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.
- package/dist/main.js +1456 -1213
- package/dist/src/sdk/materials/shaderLib/procedural-normal.glsl +274 -0
- package/dist/types/components/Wellbores/Casings/CasingMaterial.d.ts +229 -1
- package/dist/types/components/Wellbores/Casings/CasingSection.d.ts +6 -1
- package/dist/types/components/Wellbores/Casings/Casings.d.ts +45 -4
- package/dist/types/components/Wellbores/Casings/casings-defs.d.ts +44 -0
- package/dist/types/components/Wellbores/Casings/index.d.ts +2 -1
- package/package.json +3 -9
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// Procedural (texture-free) normal-detail helpers.
|
|
2
|
+
//
|
|
3
|
+
// Pure functions - no uniforms, no varyings - so any material can reuse them by
|
|
4
|
+
// supplying its own inputs and controls. Include this file in a FRAGMENT shader
|
|
5
|
+
// (it relies on screen-space derivatives, dFdx/dFdy).
|
|
6
|
+
//
|
|
7
|
+
// Dependencies the consuming material must provide when calling perturbNormalHeight:
|
|
8
|
+
// - normal : the shading normal to perturb, in VIEW space
|
|
9
|
+
// - viewPos : the surface position in VIEW space (e.g. -vViewPosition)
|
|
10
|
+
// - height : a scalar height field sampled at this fragment (produced by the
|
|
11
|
+
// pnGranular / pnGrain / pnScratches helpers below, summed); the caller
|
|
12
|
+
// scales it to taste (its "strength"/bump amount) and may fade it by
|
|
13
|
+
// distance
|
|
14
|
+
// And when sampling a pattern height (pnGranular / pnGrain / pnScratches):
|
|
15
|
+
// - uv : a 2D coordinate ALREADY scaled by the caller's frequency. The
|
|
16
|
+
// caller owns the units (world distance, normalized, radius-based,
|
|
17
|
+
// ...) and which axis maps to uv.y (the grain/stretch axis).
|
|
18
|
+
// - octaves : fbm octave count for pnGranular / pnGrain (1..N). pnScratches is
|
|
19
|
+
// segment-based and takes NO octave count.
|
|
20
|
+
// - periodX : if > 0, the noise tiles seamlessly every `periodX` cells in x (pass
|
|
21
|
+
// the number of cells around a circumference to remove the wrap seam
|
|
22
|
+
// on a closed cylinder; pass 0 to disable). Tiling is exact for
|
|
23
|
+
// granular and for grain/scratches at angle 0.
|
|
24
|
+
// The patterns return scalar heights; combine several by summing (optionally weighted)
|
|
25
|
+
// and feed the result to perturbNormalHeight.
|
|
26
|
+
//
|
|
27
|
+
// "width" of the features is controlled by the caller's frequency/anisotropy/angle
|
|
28
|
+
// folded into uv; "height" of the bump is the caller-owned scalar passed to
|
|
29
|
+
// perturbNormalHeight. No CPU data or vertex attributes are required beyond whatever
|
|
30
|
+
// the material already uses to build its uv.
|
|
31
|
+
|
|
32
|
+
// Lattice wrap period (cells). The value-noise lattice (and the scratch seed grid) repeat
|
|
33
|
+
// every PN_WRAP cells per axis, so a caller that reduces a very large sample coordinate
|
|
34
|
+
// into [0, PN_WRAP) - to keep floor()/fract() float-precise at oilfield scale, where
|
|
35
|
+
// metres-along-trajectory x frequency reaches ~1e5 and fract() quantises into visible
|
|
36
|
+
// banding - still tiles SEAMLESSLY at the wrap. Large enough (4096 cells) that the repeat
|
|
37
|
+
// is invisible for fine detail; a no-op (mod = identity) for the small coords normal use
|
|
38
|
+
// produces.
|
|
39
|
+
#define PN_WRAP 4096.0
|
|
40
|
+
|
|
41
|
+
// Precision-robust 2D->1D hash (Dave Hoskins). fract() is applied BEFORE any multiply/
|
|
42
|
+
// dot so the working values stay in [0,1) - this survives the large sample coordinates
|
|
43
|
+
// that occur at oilfield scale (a pattern sampled by metres-along-trajectory times a
|
|
44
|
+
// frequency reaches ~1e5+), where the older fract(p*c) + dot(p, p) form overflowed
|
|
45
|
+
// float32 precision so fract() returned near-constant values and the noise collapsed /
|
|
46
|
+
// stretched with distance down the well.
|
|
47
|
+
float pnHash2(vec2 p) {
|
|
48
|
+
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
|
49
|
+
p3 += dot(p3, p3.yzx + 33.33);
|
|
50
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 2D value noise (smoothstep-interpolated).
|
|
54
|
+
float pnValueNoise2(vec2 p) {
|
|
55
|
+
vec2 i = floor(p);
|
|
56
|
+
vec2 f = fract(p);
|
|
57
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
58
|
+
// Wrap the integer lattice at PN_WRAP so the noise tiles seamlessly when a caller reduces
|
|
59
|
+
// a huge coordinate into [0, PN_WRAP) (identity for the small coords normal use produces).
|
|
60
|
+
vec2 i0 = mod(i, PN_WRAP);
|
|
61
|
+
vec2 i1 = mod(i + 1.0, PN_WRAP);
|
|
62
|
+
float a = pnHash2(i0);
|
|
63
|
+
float b = pnHash2(vec2(i1.x, i0.y));
|
|
64
|
+
float c = pnHash2(vec2(i0.x, i1.y));
|
|
65
|
+
float d = pnHash2(i1);
|
|
66
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Fractional Brownian motion with a dynamic (uniform-driven) octave count. The loop
|
|
70
|
+
// bound is a compile-time constant (GLSL requirement); `octaves` clamps it at runtime.
|
|
71
|
+
float pnFbm2(vec2 p, int octaves) {
|
|
72
|
+
float sum = 0.0;
|
|
73
|
+
float amp = 0.5;
|
|
74
|
+
float norm = 0.0;
|
|
75
|
+
for(int o = 0; o < 8; o++) {
|
|
76
|
+
if(o >= octaves)
|
|
77
|
+
break;
|
|
78
|
+
sum += amp * pnValueNoise2(p);
|
|
79
|
+
norm += amp;
|
|
80
|
+
p *= 2.02;
|
|
81
|
+
amp *= 0.5;
|
|
82
|
+
}
|
|
83
|
+
return norm > 0.0 ? sum / norm : 0.0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Tiling value noise: the integer lattice wraps at `periodX` cells in x, so a pattern
|
|
87
|
+
// sampled over exactly `periodX` units in x is seamless (used to wrap around a
|
|
88
|
+
// cylinder's circumference). y is not tiled.
|
|
89
|
+
float pnValueNoise2Tiled(vec2 p, float periodX) {
|
|
90
|
+
vec2 i = floor(p);
|
|
91
|
+
vec2 f = fract(p);
|
|
92
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
93
|
+
float x0 = mod(i.x, periodX);
|
|
94
|
+
float x1 = mod(i.x + 1.0, periodX);
|
|
95
|
+
float y0 = mod(i.y, PN_WRAP);
|
|
96
|
+
float y1 = mod(i.y + 1.0, PN_WRAP);
|
|
97
|
+
float a = pnHash2(vec2(x0, y0));
|
|
98
|
+
float b = pnHash2(vec2(x1, y0));
|
|
99
|
+
float c = pnHash2(vec2(x0, y1));
|
|
100
|
+
float d = pnHash2(vec2(x1, y1));
|
|
101
|
+
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// fbm on the x-tiling noise; lacunarity 2.0 so every octave's period stays integer.
|
|
105
|
+
float pnFbm2Tiled(vec2 p, float periodX, int octaves) {
|
|
106
|
+
float sum = 0.0;
|
|
107
|
+
float amp = 0.5;
|
|
108
|
+
float norm = 0.0;
|
|
109
|
+
float per = max(periodX, 1.0);
|
|
110
|
+
for(int o = 0; o < 8; o++) {
|
|
111
|
+
if(o >= octaves)
|
|
112
|
+
break;
|
|
113
|
+
sum += amp * pnValueNoise2Tiled(p, per);
|
|
114
|
+
norm += amp;
|
|
115
|
+
p *= 2.0;
|
|
116
|
+
per *= 2.0;
|
|
117
|
+
amp *= 0.5;
|
|
118
|
+
}
|
|
119
|
+
return norm > 0.0 ? sum / norm : 0.0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// fbm that tiles in x when periodX > 0, else plain (non-tiling) fbm.
|
|
123
|
+
float pnFbm2Auto(vec2 p, int octaves, float periodX) {
|
|
124
|
+
return periodX > 0.5 ? pnFbm2Tiled(p, periodX, octaves) : pnFbm2(p, octaves);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// GRANULAR: isotropic value-noise bumps; `anisotropy` (0..1) stretches the cells along
|
|
128
|
+
// uv.y. Signed height. periodX > 0 tiles x (e.g. a circumference).
|
|
129
|
+
float pnGranular(vec2 uv, float anisotropy, int octaves, float periodX) {
|
|
130
|
+
vec2 p = vec2(uv.x, uv.y * mix(1.0, 0.04, anisotropy));
|
|
131
|
+
return pnFbm2Auto(p, octaves, periodX) - 0.5;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// BRUSHED: a directional fine grain - thin parallel ridges running at `angle` (radians;
|
|
135
|
+
// 0 = along uv.y). `sharpness` (0..1) thins the ridges (their "width"); `uniformity`
|
|
136
|
+
// (0..1) blends from an irregular grain to perfectly regular flutes. Positive height.
|
|
137
|
+
// Tiling is exact only for angle == 0; other angles degrade gracefully (a faint seam).
|
|
138
|
+
float pnGrain(vec2 uv, float angle, float sharpness, float uniformity, int octaves, float periodX) {
|
|
139
|
+
float ca = cos(angle), sa = sin(angle);
|
|
140
|
+
vec2 r = vec2(uv.x * ca - uv.y * sa, uv.x * sa + uv.y * ca);
|
|
141
|
+
float tile = abs(angle) < 1e-3 ? periodX : 0.0;
|
|
142
|
+
float n = pnFbm2Auto(vec2(r.x, r.y * 0.06), octaves, tile);
|
|
143
|
+
// Irregular grain (fbm-smooth, so it has no cusp to alias); `sharpness` thins the ridges.
|
|
144
|
+
float irregular = pow(1.0 - abs(2.0 * n - 1.0), mix(2.0, 8.0, clamp(sharpness, 0.0, 1.0)));
|
|
145
|
+
// Regular flutes: footprint-anti-aliased evenly-spaced ridges (period 1 in r.x). A
|
|
146
|
+
// smoothstep whose transition is never narrower than the pixel footprint (fwidth)
|
|
147
|
+
// keeps the ridge crisp up close WITHOUT the sharp cusp of the old (1-|sin|)^pow form
|
|
148
|
+
// - that cusp under-sampled the normal and shimmered even close up - and naturally
|
|
149
|
+
// band-limits into a flat tone once a flute drops below a pixel far away. `sharpness`
|
|
150
|
+
// sets the ridge width.
|
|
151
|
+
float dCentre = abs(r.x - floor(r.x + 0.5)); // 0 at a flute centre .. 0.5 between
|
|
152
|
+
float hw = mix(0.35, 0.05, clamp(sharpness, 0.0, 1.0));
|
|
153
|
+
float aaw = max(fwidth(r.x), 1e-4);
|
|
154
|
+
float regular = 1.0 - smoothstep(hw - aaw, hw + aaw, dCentre);
|
|
155
|
+
// Once a flute period approaches the pixel footprint (fwidth ~ 0.5, i.e. the sine is
|
|
156
|
+
// near screen-Nyquist at the grazing sides of the shell), flatten the flutes toward
|
|
157
|
+
// their duty-cycle mean (~2*hw). Without this the ridge stays a smooth-but-undersampled
|
|
158
|
+
// sine there and still shimmers even though the edges are footprint-AA'd.
|
|
159
|
+
regular = mix(regular, 2.0 * hw, smoothstep(0.3, 0.5, aaw));
|
|
160
|
+
return mix(irregular, regular, clamp(uniformity, 0.0, 1.0));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// SCRATCHES: sparse, thin grooves crossing at varied angles/lengths - a cell/segment
|
|
164
|
+
// field rather than parallel lanes, so it reads like real scuffing. `density` (0..1) =
|
|
165
|
+
// fraction of seed cells that carry a scratch; `angle` = orientation bias and `wander`
|
|
166
|
+
// (0..1) widens the spread of directions around it (0 = all parallel, 1 = fully random);
|
|
167
|
+
// `lengthScale` (>0) scales each groove's length; `halfWidth` sets the groove width in
|
|
168
|
+
// sample (uv) units - pass (world-width x frequency) to get a FREQUENCY-INDEPENDENT width
|
|
169
|
+
// so lowering the frequency lengthens/thins-out the scratches without widening them. Two
|
|
170
|
+
// families are summed by pnScratches:
|
|
171
|
+
// a fine layer (short/medium scratches) plus a coarse layer (fewer, much longer ones).
|
|
172
|
+
// Each groove is analytically anti-aliased (edge widened to the pixel footprint).
|
|
173
|
+
// Negative height.
|
|
174
|
+
|
|
175
|
+
// One scratch "family": line segments seeded on an integer grid at the coordinate scale
|
|
176
|
+
// of `r`. Each present cell (gated by `density`) spawns a groove with a random midpoint,
|
|
177
|
+
// direction (biased toward `angle`, spread by `wander`), half-length, width and depth. A
|
|
178
|
+
// 3x3 neighbourhood is scanned so segments crossing in from adjacent cells are caught;
|
|
179
|
+
// half-length is capped below the search radius so grooves stay unbroken. `seed`
|
|
180
|
+
// decorrelates layers; x wraps at `periodX` cells (when angle ~ 0) for a seamless
|
|
181
|
+
// circumference seam.
|
|
182
|
+
float pnScratchLayer(vec2 r, float angle, float density, float lengthScale, float halfWidth, float wander, float periodX, float seed) {
|
|
183
|
+
float aaw = max(length(fwidth(r)), 1e-4);
|
|
184
|
+
vec2 cell = floor(r);
|
|
185
|
+
float acc = 0.0;
|
|
186
|
+
bool tile = periodX > 0.5 && abs(angle) < 1e-3;
|
|
187
|
+
for(int j = -1; j <= 1; j++) {
|
|
188
|
+
for(int i = -1; i <= 1; i++) {
|
|
189
|
+
vec2 c = cell + vec2(float(i), float(j));
|
|
190
|
+
vec2 h = mod(c, PN_WRAP); // keep hash coords small/precise + seamless at the axial wrap
|
|
191
|
+
if(tile)
|
|
192
|
+
// max() keeps the divisor provably non-zero: callers that disable tiling pass
|
|
193
|
+
// periodX = 0.0, and some backends (ANGLE/D3D) constant-fold the mod() division
|
|
194
|
+
// inside this branch BEFORE dead-code elimination and warn "X4008: floating
|
|
195
|
+
// point division by zero". Only reached when tile (periodX > 0.5), so this is a
|
|
196
|
+
// no-op at runtime.
|
|
197
|
+
h.x = mod(c.x, max(periodX, 1.0)); // seamless wrap around the circumference
|
|
198
|
+
h += seed;
|
|
199
|
+
if(pnHash2(h + 3.1) > density)
|
|
200
|
+
continue; // sparsity
|
|
201
|
+
vec2 mid = c + vec2(pnHash2(h + 7.3), pnHash2(h + 13.7));
|
|
202
|
+
float a = angle + (pnHash2(h + 21.1) - 0.5) * 3.14159265 * clamp(wander, 0.0, 1.0);
|
|
203
|
+
vec2 d = vec2(cos(a), sin(a));
|
|
204
|
+
vec2 perp = vec2(-d.y, d.x);
|
|
205
|
+
float hl = clamp(mix(0.1, 0.55, pnHash2(h + 29.3)) * max(lengthScale, 0.05), 0.03, 0.92);
|
|
206
|
+
float w = halfWidth * mix(0.7, 1.3, pnHash2(h + 41.7)); // world-scaled half-width (freq-independent)
|
|
207
|
+
float rStr = mix(0.3, 1.0, pnHash2(h + 37.7)); // random depth/brightness
|
|
208
|
+
float bend = (pnHash2(h + 51.9) - 0.5) * 0.7; // shallow curvature so lines aren't dead straight
|
|
209
|
+
vec2 pr = r - mid;
|
|
210
|
+
float t = clamp(dot(pr, d), -hl, hl); // nearest point on the straight axis
|
|
211
|
+
float u = t / max(hl, 1e-3);
|
|
212
|
+
vec2 foot = d * t + perp * (bend * u * u * hl); // bow the centre-line across its length
|
|
213
|
+
float dist = length(pr - foot);
|
|
214
|
+
acc = max(acc, (1.0 - smoothstep(w - aaw, w + aaw, dist)) * rStr);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return acc;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
float pnScratches(vec2 uv, float angle, float density, float lengthScale, float halfWidth, float wander, float periodX, float coarseWeight) {
|
|
221
|
+
float ca = cos(angle), sa = sin(angle);
|
|
222
|
+
vec2 r = vec2(uv.x * ca - uv.y * sa, uv.x * sa + uv.y * ca);
|
|
223
|
+
|
|
224
|
+
// Fine family: many short/medium scratches at the caller's frequency.
|
|
225
|
+
float fine = pnScratchLayer(r, angle, density, lengthScale, halfWidth, wander, periodX, 0.0);
|
|
226
|
+
|
|
227
|
+
// Coarse family: a few much longer grooves, seeded on a ~3x larger grid (bigger cells
|
|
228
|
+
// => longer scratches within the same 3x3 search). x is rescaled to an INTEGER period
|
|
229
|
+
// so the circumference seam stays seamless; y (never tiled) is just divided down. The
|
|
230
|
+
// half-width is scaled by the same factor so the coarse grooves keep the SAME world
|
|
231
|
+
// width as the fine ones (longer, not fatter). It is the most repetition-prone / "big"
|
|
232
|
+
// family AND doubles the per-fragment cost, so `coarseWeight` lets the caller drop it
|
|
233
|
+
// entirely (pass 0 - the whole 3x3 loop is then skipped): the branch is expected to be
|
|
234
|
+
// driven by a uniform (e.g. a quality knob) so it stays divergence-free.
|
|
235
|
+
float coarse = 0.0;
|
|
236
|
+
if(coarseWeight > 0.0) {
|
|
237
|
+
float coarsePeriod = max(floor(periodX / 3.0 + 0.5), 1.0);
|
|
238
|
+
float scale = coarsePeriod / max(periodX, 1.0);
|
|
239
|
+
vec2 rc = vec2(r.x * scale, r.y / 3.0);
|
|
240
|
+
coarse = pnScratchLayer(rc, angle, density * 0.5, lengthScale, halfWidth * scale, wander, coarsePeriod, 7.0) * coarseWeight;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return -max(fine, coarse);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Footprint anti-aliasing factor (1 near .. 0 sub-pixel). Fades a pattern out as its
|
|
247
|
+
// finest octave shrinks toward a pixel - with distance or at grazing angles - so the
|
|
248
|
+
// high-frequency detail never becomes a shimmering/aliasing signal. `uv` is the
|
|
249
|
+
// (frequency-scaled) sample coordinate; `octaves` is the fbm octave count. Must be
|
|
250
|
+
// called under uniform control flow (uses screen-space derivatives).
|
|
251
|
+
float pnFootprintFade(vec2 uv, int octaves) {
|
|
252
|
+
// cells per pixel of the sample coordinate (mildly boosted for finer octaves). Fade
|
|
253
|
+
// BEFORE the Nyquist limit: a cell must stay >~2 px to sample cleanly, so start fading
|
|
254
|
+
// around 2.5 px/cell and reach zero by ~1 px/cell. (A looser threshold left the pattern
|
|
255
|
+
// near full strength at ~1 cell/pixel, so it stippled/aliased on thin, minified faces
|
|
256
|
+
// such as the end caps and slice faces.)
|
|
257
|
+
float cellsPerPixel = max(fwidth(uv.x), fwidth(uv.y)) * (1.0 + 0.5 * float(octaves - 1));
|
|
258
|
+
return 1.0 - smoothstep(0.4, 1.0, cellsPerPixel);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Perturb a view-space normal by a scalar height field using the screen-space surface
|
|
262
|
+
// gradient (Mikkelsen) - no tangent/bitangent attributes needed. `height` must be the
|
|
263
|
+
// value sampled at THIS fragment; its screen-space derivatives give the slope.
|
|
264
|
+
vec3 perturbNormalHeight(vec3 normal, vec3 viewPos, float height) {
|
|
265
|
+
vec3 sx = dFdx(viewPos);
|
|
266
|
+
vec3 sy = dFdy(viewPos);
|
|
267
|
+
float hx = dFdx(height);
|
|
268
|
+
float hy = dFdy(height);
|
|
269
|
+
vec3 r1 = cross(sy, normal);
|
|
270
|
+
vec3 r2 = cross(normal, sx);
|
|
271
|
+
float det = dot(sx, r1);
|
|
272
|
+
vec3 grad = sign(det) * (hx * r1 + hy * r2);
|
|
273
|
+
return normalize(abs(det) * normal - grad);
|
|
274
|
+
}
|
|
@@ -1,5 +1,137 @@
|
|
|
1
1
|
import { MeshStandardMaterial, MeshStandardMaterialParameters, Uniform, WebGLProgramParametersWithUniforms } from 'three';
|
|
2
|
+
/**
|
|
3
|
+
* Parameters accepted by {@link CasingMaterial}. A `MeshStandardMaterial` at heart, so
|
|
4
|
+
* it takes the familiar PBR knobs (`color`, `roughness`, `metalness`, `emissive`,
|
|
5
|
+
* `envMap`, `envMapIntensity`, ...) plus the casing texture-UV units and the grouped
|
|
6
|
+
* {@link CasingEffects | effects} below.
|
|
7
|
+
*
|
|
8
|
+
* @expand
|
|
9
|
+
*/
|
|
10
|
+
export type CasingMaterialParameters = MeshStandardMaterialParameters & {
|
|
11
|
+
/** UV units for the base `map` (and the aoMap/lightMap/emissiveMap/metalnessMap/
|
|
12
|
+
* roughnessMap group): `'normalized'` = 0..1 around/along, `'world'` = object-space
|
|
13
|
+
* distance (arc length x trajectory distance) so `texture.repeat` becomes a density
|
|
14
|
+
* that stays consistent across sections of different radius and length. Default
|
|
15
|
+
* `'normalized'`. */
|
|
16
|
+
mapUvUnits?: 'normalized' | 'world';
|
|
17
|
+
/** UV units for `normalMap` / `bumpMap`, independent of `mapUvUnits`. Default
|
|
18
|
+
* `'normalized'`. */
|
|
19
|
+
normalMapUvUnits?: 'normalized' | 'world';
|
|
20
|
+
/** Grouped casing stylization effects (silhouette outline, section edge shading,
|
|
21
|
+
* procedural weathering, per-section variation and micro-normal surface detail).
|
|
22
|
+
* Every sub-effect is optional and independent; omitted ones fall back to their
|
|
23
|
+
* defaults (mostly off). */
|
|
24
|
+
effects?: CasingEffects;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Composable casing stylization effects for {@link CasingMaterial}. Every field is
|
|
28
|
+
* optional and independent; the procedural surface-detail layers (`granular`,
|
|
29
|
+
* `brushed`, `scratches`) simply sum. Strengths default to 0 (off) unless noted.
|
|
30
|
+
* @expand
|
|
31
|
+
*/
|
|
32
|
+
export type CasingEffects = {
|
|
33
|
+
/** View-space silhouette darkening that outlines each shell, helping nested strings
|
|
34
|
+
* read apart (especially under slicing / auto-slice). */
|
|
35
|
+
silhouette?: {
|
|
36
|
+
/** 0-1 strength (0 = off). Default 0. */
|
|
37
|
+
strength?: number;
|
|
38
|
+
/** Exponent tightening the rim toward the edge (higher = tighter). Default 3. */
|
|
39
|
+
power?: number;
|
|
40
|
+
};
|
|
41
|
+
/** Darkening at each section's own top/bottom edges. */
|
|
42
|
+
edgeShading?: {
|
|
43
|
+
/** 0-1 strength (0 = off). Default 0. */
|
|
44
|
+
strength?: number;
|
|
45
|
+
/** Distance in metres the darkening reaches from each edge, independent of section
|
|
46
|
+
* length. Default 0.2. */
|
|
47
|
+
width?: number;
|
|
48
|
+
};
|
|
49
|
+
/** Procedural wear/tear/spill (no textures), pinned in world space. */
|
|
50
|
+
weathering?: {
|
|
51
|
+
/** 0-1 strength (0 = off). Default 0. */
|
|
52
|
+
strength?: number;
|
|
53
|
+
/** Noise frequency in cells per real-world metre (lower = larger, sparser smears).
|
|
54
|
+
* Default 1.5. */
|
|
55
|
+
scale?: number;
|
|
56
|
+
/** 0-1 per-material multiplier (1 = full wear, 0 = none) so a preset such as the
|
|
57
|
+
* matte shoe can resist the weathering. Default 1. */
|
|
58
|
+
resistance?: number;
|
|
59
|
+
};
|
|
60
|
+
/** 0-1 per-section wear variation so adjacent telescoping strings read apart by
|
|
61
|
+
* looking differently worn rather than by a colour/value ramp. Default 0. */
|
|
62
|
+
sectionVariation?: number;
|
|
63
|
+
/** 0-1 performance vs. quality of the procedural surface detail (a fill-rate knob;
|
|
64
|
+
* off-effects always cost nothing regardless). Lower = cheaper: fewer weathering fbm
|
|
65
|
+
* octaves (2 at 0 .. 4 at 1) and the coarse/long scratch family only runs at >= 0.66.
|
|
66
|
+
* 1 = full-detail reference. Default 0.6. */
|
|
67
|
+
detailQuality?: number;
|
|
68
|
+
/** Isotropic value-noise bumps. */
|
|
69
|
+
granular?: {
|
|
70
|
+
/** 0-1 strength (0 = off). Default 0. */
|
|
71
|
+
strength?: number;
|
|
72
|
+
/** Cells per world (object-distance) unit. Default 2. */
|
|
73
|
+
frequency?: number;
|
|
74
|
+
/** 1-5 fbm octaves. Default 3. */
|
|
75
|
+
octaves?: number;
|
|
76
|
+
/** 0-1 stretch of the cells along the trajectory axis. Default 0. */
|
|
77
|
+
anisotropy?: number;
|
|
78
|
+
};
|
|
79
|
+
/** Directional fine grain (many thin parallel ridges). */
|
|
80
|
+
brushed?: {
|
|
81
|
+
/** 0-1 strength (0 = off). Default 0. */
|
|
82
|
+
strength?: number;
|
|
83
|
+
/** Cells per world (object-distance) unit. Default 2. */
|
|
84
|
+
frequency?: number;
|
|
85
|
+
/** 1-5 fbm octaves. Default 3. */
|
|
86
|
+
octaves?: number;
|
|
87
|
+
/** Grain direction in radians (0 = along the trajectory axis; non-zero angles
|
|
88
|
+
* reintroduce a faint seam on a full shell). Default 0. */
|
|
89
|
+
angle?: number;
|
|
90
|
+
/** 0-1 line thinness. Default 0.5. */
|
|
91
|
+
sharpness?: number;
|
|
92
|
+
/** 0-1 blend from an irregular grain (0) to regular, evenly-spaced flutes (1).
|
|
93
|
+
* Default 0. */
|
|
94
|
+
uniformity?: number;
|
|
95
|
+
};
|
|
96
|
+
/** Sparse, hair-thin surface scuffs that glint under changing light (their relief is
|
|
97
|
+
* kept very shallow - visibility comes from a localized polish, not depth). */
|
|
98
|
+
scratches?: {
|
|
99
|
+
/** 0-1 strength (0 = off). Default 0. */
|
|
100
|
+
strength?: number;
|
|
101
|
+
/** Cells per world (object-distance) unit. Default 10. */
|
|
102
|
+
frequency?: number;
|
|
103
|
+
/** Scratch direction in radians (0 = along the trajectory axis). Default 0. */
|
|
104
|
+
angle?: number;
|
|
105
|
+
/** 0-1 how many scratches survive. Default 0.4. */
|
|
106
|
+
density?: number;
|
|
107
|
+
/** Average scratch length multiplier (>0). Default 0.6. */
|
|
108
|
+
length?: number;
|
|
109
|
+
/** 0-1 how much the scratch direction drifts. Default 1. */
|
|
110
|
+
wander?: number;
|
|
111
|
+
/** 0-1 groove width, frequency-independent (world-scaled, hair/needle-thin:
|
|
112
|
+
* ~0.02..0.12mm half-width). Default 0.15. */
|
|
113
|
+
width?: number;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Physically-based (PBR) material for `Casings` / `CasingSection`. A
|
|
118
|
+
* `MeshStandardMaterial` at its core - so casings pick up real image-based
|
|
119
|
+
* reflections from the scene `environment`, exactly matching neighbouring
|
|
120
|
+
* `CompletionTools` - with the casing-specific stylization (procedural weathering,
|
|
121
|
+
* granular/brushed/scratch micro-normal detail, silhouette darkening and edge shading)
|
|
122
|
+
* spliced into its shaders via `onBeforeCompile`, and the slicing/vertex
|
|
123
|
+
* transform driven by the same custom attributes as the picking material.
|
|
124
|
+
*
|
|
125
|
+
* OIT-capable via {@link makeOitCompatible} so casings participate in the
|
|
126
|
+
* `OITRenderPass` hybrid pipeline (a no-op in the default render loop).
|
|
127
|
+
*/
|
|
2
128
|
export declare class CasingMaterial extends MeshStandardMaterial {
|
|
129
|
+
isCasingMaterial: boolean;
|
|
130
|
+
private _weathering;
|
|
131
|
+
private _wearResistance;
|
|
132
|
+
private _wellLength;
|
|
133
|
+
/** Custom uniforms bound into the patched program in `onBeforeCompile` and
|
|
134
|
+
* shared by reference with the OIT variants so per-frame updates propagate. */
|
|
3
135
|
uniforms: {
|
|
4
136
|
sizeMultiplier: Uniform<number>;
|
|
5
137
|
radius: Uniform<number>;
|
|
@@ -7,8 +139,40 @@ export declare class CasingMaterial extends MeshStandardMaterial {
|
|
|
7
139
|
sliceOffset: Uniform<number>;
|
|
8
140
|
sliceAngle: Uniform<number>;
|
|
9
141
|
autoSlicePosition: Uniform<boolean>;
|
|
142
|
+
sectionVariation: Uniform<number>;
|
|
143
|
+
silhouette: Uniform<number>;
|
|
144
|
+
silhouettePower: Uniform<number>;
|
|
145
|
+
edgeShading: Uniform<number>;
|
|
146
|
+
edgeShadingWidth: Uniform<number>;
|
|
147
|
+
sectionIndex: Uniform<number>;
|
|
148
|
+
detailFadeNear: Uniform<number>;
|
|
149
|
+
detailFadeFar: Uniform<number>;
|
|
150
|
+
detailQuality: Uniform<number>;
|
|
151
|
+
schematic: Uniform<number>;
|
|
152
|
+
mapUvWorld: Uniform<number>;
|
|
153
|
+
normalMapUvWorld: Uniform<number>;
|
|
154
|
+
wellLength: Uniform<number>;
|
|
155
|
+
granularStrength: Uniform<number>;
|
|
156
|
+
granularFrequency: Uniform<number>;
|
|
157
|
+
granularOctaves: Uniform<number>;
|
|
158
|
+
granularAnisotropy: Uniform<number>;
|
|
159
|
+
brushedStrength: Uniform<number>;
|
|
160
|
+
brushedFrequency: Uniform<number>;
|
|
161
|
+
brushedOctaves: Uniform<number>;
|
|
162
|
+
brushedAngle: Uniform<number>;
|
|
163
|
+
brushedSharpness: Uniform<number>;
|
|
164
|
+
brushedUniformity: Uniform<number>;
|
|
165
|
+
scratchStrength: Uniform<number>;
|
|
166
|
+
scratchFrequency: Uniform<number>;
|
|
167
|
+
scratchAngle: Uniform<number>;
|
|
168
|
+
scratchDensity: Uniform<number>;
|
|
169
|
+
scratchLength: Uniform<number>;
|
|
170
|
+
scratchWander: Uniform<number>;
|
|
171
|
+
scratchWidth: Uniform<number>;
|
|
172
|
+
weathering: Uniform<number>;
|
|
173
|
+
weatheringScale: Uniform<number>;
|
|
10
174
|
};
|
|
11
|
-
constructor(
|
|
175
|
+
constructor(parameters?: CasingMaterialParameters);
|
|
12
176
|
get sizeMultiplier(): number;
|
|
13
177
|
set sizeMultiplier(v: number);
|
|
14
178
|
get radius(): number;
|
|
@@ -21,5 +185,69 @@ export declare class CasingMaterial extends MeshStandardMaterial {
|
|
|
21
185
|
set sliceAngle(v: number);
|
|
22
186
|
get autoSlicePosition(): boolean;
|
|
23
187
|
set autoSlicePosition(v: boolean);
|
|
188
|
+
/** 0-1 strength of the per-section wear variation: how much each section's
|
|
189
|
+
* procedural wear is offset (seed) and scaled (amount) by its index, so adjacent
|
|
190
|
+
* telescoping strings read apart by looking differently worn rather than by a
|
|
191
|
+
* colour/value ramp. */
|
|
192
|
+
get sectionVariation(): number;
|
|
193
|
+
set sectionVariation(v: number);
|
|
194
|
+
/** 0-1 strength of the view-space silhouette darkening (outlines each shell). */
|
|
195
|
+
get silhouette(): number;
|
|
196
|
+
set silhouette(v: number);
|
|
197
|
+
/** Exponent tightening the silhouette rim toward the edge (higher = tighter). Default 3. */
|
|
198
|
+
get silhouettePower(): number;
|
|
199
|
+
set silhouettePower(v: number);
|
|
200
|
+
/** 0-1 strength of the edge shading (darkening at each section's own top/bottom edges). */
|
|
201
|
+
get edgeShading(): number;
|
|
202
|
+
set edgeShading(v: number);
|
|
203
|
+
/** Distance in metres the edge shading reaches from each section edge. Default 0.2. */
|
|
204
|
+
get edgeShadingWidth(): number;
|
|
205
|
+
set edgeShadingWidth(v: number);
|
|
206
|
+
/** Index of this section within the wellbore's casing stack, used to seed the
|
|
207
|
+
* per-section variation of the stylization effects. */
|
|
208
|
+
get sectionIndex(): number;
|
|
209
|
+
set sectionIndex(v: number);
|
|
210
|
+
/** 0-1 performance vs. quality of the procedural surface detail (fill-rate knob).
|
|
211
|
+
* Lower = cheaper (fewer weathering fbm octaves; the coarse scratch family only runs
|
|
212
|
+
* at >= 0.66); 1 = full-detail reference. */
|
|
213
|
+
get detailQuality(): number;
|
|
214
|
+
set detailQuality(v: number);
|
|
215
|
+
/** Unlit "schematic" shading mode: flat material `color` + `silhouette` outline only,
|
|
216
|
+
* with all lighting/env, textures and realism detail ignored. Set by the `Casings`
|
|
217
|
+
* component's `schematic` prop; the slice is locked separately (component side). */
|
|
218
|
+
get schematic(): boolean;
|
|
219
|
+
set schematic(v: boolean);
|
|
220
|
+
/** 0-1 strength of the procedural wear/tear/spill surface detail (no textures). The
|
|
221
|
+
* effective amount is scaled per-material by {@link wearResistance}. */
|
|
222
|
+
get weathering(): number;
|
|
223
|
+
set weathering(v: number);
|
|
224
|
+
/** 0-1 per-material wear multiplier (1 = full wear, 0 = none). Lets material presets
|
|
225
|
+
* (e.g. the matte shoe) resist the procedural weathering the component applies
|
|
226
|
+
* globally via {@link weathering}. */
|
|
227
|
+
get wearResistance(): number;
|
|
228
|
+
set wearResistance(v: number);
|
|
229
|
+
/** Weathering noise frequency in cells per real-world metre (lower = larger,
|
|
230
|
+
* sparser smears). */
|
|
231
|
+
get weatheringScale(): number;
|
|
232
|
+
set weatheringScale(v: number);
|
|
233
|
+
/** Real-world total length (metres) of the whole wellbore trajectory. Uploaded as a
|
|
234
|
+
* uniform and used by the vertex shader to derive each section's physical length
|
|
235
|
+
* (`vSectionLength`) and the along-axis coordinate (`casingAxial`). The weathering is
|
|
236
|
+
* pinned in world space (via `vWorldPos`) and does not use it. */
|
|
237
|
+
get wellLength(): number;
|
|
238
|
+
set wellLength(v: number);
|
|
239
|
+
/** UV units for the base `map` group. `'world'` uses object-space distance so
|
|
240
|
+
* `texture.repeat` is a radius/length-consistent density. */
|
|
241
|
+
get mapUvUnits(): 'normalized' | 'world';
|
|
242
|
+
set mapUvUnits(v: 'normalized' | 'world');
|
|
243
|
+
/** UV units for `normalMap` / `bumpMap`, independent of {@link mapUvUnits}. */
|
|
244
|
+
get normalMapUvUnits(): 'normalized' | 'world';
|
|
245
|
+
set normalMapUvUnits(v: 'normalized' | 'world');
|
|
246
|
+
/** Grouped casing stylization effects (silhouette, edge shading, weathering,
|
|
247
|
+
* per-section variation and the granular/brushed/scratch micro-normal layers).
|
|
248
|
+
* Reading returns the current settings reconstructed from the uniforms. Assigning
|
|
249
|
+
* applies the whole group at once - omitted sub-effects reset to their defaults. */
|
|
250
|
+
get effects(): CasingEffects;
|
|
251
|
+
set effects(v: CasingEffects | undefined);
|
|
24
252
|
onBeforeCompile(parameters: WebGLProgramParametersWithUniforms): void;
|
|
25
253
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PointerEvents } from '../../../main';
|
|
2
|
+
import { CasingEffects } from './CasingMaterial';
|
|
2
3
|
import { CasingSectionMaterialOptions } from './Casings';
|
|
3
4
|
import { CasingSectionType } from './casings-defs';
|
|
4
5
|
type CasingSectionProps = {
|
|
@@ -11,6 +12,10 @@ type CasingSectionProps = {
|
|
|
11
12
|
autoSlicePosition?: boolean;
|
|
12
13
|
opacity?: number;
|
|
13
14
|
renderOrder?: number;
|
|
15
|
+
effects?: CasingEffects;
|
|
16
|
+
wellLength?: number;
|
|
17
|
+
sectionIndex?: number;
|
|
18
|
+
schematic?: boolean;
|
|
14
19
|
} & PointerEvents;
|
|
15
|
-
export declare const CasingSection: ({ section, materialOptions, radialSegments, sizeMultiplier, sliceAngle, sliceOffset, autoSlicePosition, opacity, renderOrder, onPointerClick, onPointerEnter, onPointerLeave, onPointerMove, }: CasingSectionProps) => import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export declare const CasingSection: ({ section, materialOptions, radialSegments, sizeMultiplier, sliceAngle, sliceOffset, autoSlicePosition, opacity, renderOrder, effects, wellLength, sectionIndex, schematic, onPointerClick, onPointerEnter, onPointerLeave, onPointerMove, }: CasingSectionProps) => import("react/jsx-runtime").JSX.Element;
|
|
16
21
|
export {};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { ReactElement } from 'react';
|
|
2
|
-
import { Group,
|
|
2
|
+
import { Group, Object3D } from 'three';
|
|
3
3
|
import { CommonComponentProps } from '../../../common/types';
|
|
4
4
|
import { PointerEvents } from '../../../main';
|
|
5
|
+
import { CasingEffects, CasingMaterialParameters } from './CasingMaterial';
|
|
5
6
|
import { CasingSectionType } from './casings-defs';
|
|
6
7
|
/**
|
|
7
8
|
* CasingSectionMaterialOptions
|
|
8
9
|
* @expand
|
|
9
10
|
*/
|
|
10
11
|
export type CasingSectionMaterialOptions = {
|
|
11
|
-
primary:
|
|
12
|
-
inner?:
|
|
13
|
-
slice?:
|
|
12
|
+
primary: CasingMaterialParameters;
|
|
13
|
+
inner?: CasingMaterialParameters;
|
|
14
|
+
slice?: CasingMaterialParameters;
|
|
14
15
|
};
|
|
15
16
|
/**
|
|
16
17
|
* MaterialOptions
|
|
@@ -31,9 +32,29 @@ export type CasingProps = PointerEvents & CommonComponentProps & {
|
|
|
31
32
|
shoeFactor?: number;
|
|
32
33
|
overrideSegmentsPerMeter?: number;
|
|
33
34
|
overrideSimplificationThreshold?: number;
|
|
35
|
+
/** Schematic (diagram) mode: an unlit, flat-shaded look for a clean "cutaway
|
|
36
|
+
* schematic" rather than realism. Locks the slice to a half-cut that always faces
|
|
37
|
+
* the camera (`sliceAngle = PI`, `autoSlicePosition = true`, `sliceOffset = 0` -
|
|
38
|
+
* those props are ignored), renders each face with only its flat `color` (all
|
|
39
|
+
* lighting/env, textures and realism detail are ignored) plus the `silhouette`
|
|
40
|
+
* outline for contrast. Dropping the specular lighting also removes the dominant
|
|
41
|
+
* source of casing aliasing; note that geometric silhouette-edge anti-aliasing still
|
|
42
|
+
* relies on the host render pipeline (MSAA/FXAA/SMAA). Default false. */
|
|
43
|
+
schematic?: boolean;
|
|
44
|
+
/** Maps each section to its material parameters. Keep this a STABLE reference
|
|
45
|
+
* (module-level function or `useCallback`) - a new function identity each render
|
|
46
|
+
* rebuilds every section's materials and forces a shader recompile per frame,
|
|
47
|
+
* which is the main cause of sluggish casing updates. Defaults to a stable
|
|
48
|
+
* module-level function. */
|
|
34
49
|
materialOptions?: MaterialOptions;
|
|
35
50
|
opacity?: number;
|
|
36
51
|
priority?: number;
|
|
52
|
+
/** Grouped casing stylization effects (silhouette outline, section edge shading,
|
|
53
|
+
* procedural weathering, per-section variation and micro-normal surface detail).
|
|
54
|
+
* Applied as the global default for every section; a section's per-face
|
|
55
|
+
* `materialOptions.*.effects` override individual sub-effects on top of this.
|
|
56
|
+
* Defaults to {@link defaultCasingEffects}. */
|
|
57
|
+
effects?: CasingEffects;
|
|
37
58
|
};
|
|
38
59
|
/**
|
|
39
60
|
* Generic render of casings based on depths, diameters and type. Must be a child of the `Wellbore` component.
|
|
@@ -66,7 +87,27 @@ export declare const Casings: import('react').ForwardRefExoticComponent<PointerE
|
|
|
66
87
|
shoeFactor?: number;
|
|
67
88
|
overrideSegmentsPerMeter?: number;
|
|
68
89
|
overrideSimplificationThreshold?: number;
|
|
90
|
+
/** Schematic (diagram) mode: an unlit, flat-shaded look for a clean "cutaway
|
|
91
|
+
* schematic" rather than realism. Locks the slice to a half-cut that always faces
|
|
92
|
+
* the camera (`sliceAngle = PI`, `autoSlicePosition = true`, `sliceOffset = 0` -
|
|
93
|
+
* those props are ignored), renders each face with only its flat `color` (all
|
|
94
|
+
* lighting/env, textures and realism detail are ignored) plus the `silhouette`
|
|
95
|
+
* outline for contrast. Dropping the specular lighting also removes the dominant
|
|
96
|
+
* source of casing aliasing; note that geometric silhouette-edge anti-aliasing still
|
|
97
|
+
* relies on the host render pipeline (MSAA/FXAA/SMAA). Default false. */
|
|
98
|
+
schematic?: boolean;
|
|
99
|
+
/** Maps each section to its material parameters. Keep this a STABLE reference
|
|
100
|
+
* (module-level function or `useCallback`) - a new function identity each render
|
|
101
|
+
* rebuilds every section's materials and forces a shader recompile per frame,
|
|
102
|
+
* which is the main cause of sluggish casing updates. Defaults to a stable
|
|
103
|
+
* module-level function. */
|
|
69
104
|
materialOptions?: MaterialOptions;
|
|
70
105
|
opacity?: number;
|
|
71
106
|
priority?: number;
|
|
107
|
+
/** Grouped casing stylization effects (silhouette outline, section edge shading,
|
|
108
|
+
* procedural weathering, per-section variation and micro-normal surface detail).
|
|
109
|
+
* Applied as the global default for every section; a section's per-face
|
|
110
|
+
* `materialOptions.*.effects` override individual sub-effects on top of this.
|
|
111
|
+
* Defaults to {@link defaultCasingEffects}. */
|
|
112
|
+
effects?: CasingEffects;
|
|
72
113
|
} & import('react').RefAttributes<Group<import('three').Object3DEventMap>>>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Color } from 'three';
|
|
1
2
|
import { Vec3 } from '../../../sdk';
|
|
3
|
+
import { CasingEffects } from './CasingMaterial';
|
|
2
4
|
export declare const casings = "casings";
|
|
3
5
|
export type CasingSectionType = {
|
|
4
6
|
type: string;
|
|
@@ -15,3 +17,45 @@ export type CasingSectionType = {
|
|
|
15
17
|
};
|
|
16
18
|
};
|
|
17
19
|
export type CasingsGeneratorResponse = CasingSectionType[];
|
|
20
|
+
/**
|
|
21
|
+
* The component-level default {@link CasingEffects} applied globally to every section
|
|
22
|
+
* (a section's per-face `materialOptions.*.effects` can override individual sub-effects
|
|
23
|
+
* on top of this). Tuned to help adjacent/nested strings read apart.
|
|
24
|
+
*/
|
|
25
|
+
export declare const defaultCasingEffects: CasingEffects;
|
|
26
|
+
/**
|
|
27
|
+
* A custom function may be passed to the component, but this is not well documented at this time
|
|
28
|
+
* as this behavior is subject to change.
|
|
29
|
+
*/
|
|
30
|
+
export declare const defaultMaterialOptions: (section: CasingSectionType) => {
|
|
31
|
+
primary: {
|
|
32
|
+
color: string;
|
|
33
|
+
roughness: number;
|
|
34
|
+
metalness: number;
|
|
35
|
+
effects: {
|
|
36
|
+
weathering: {
|
|
37
|
+
resistance: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
inner: {
|
|
42
|
+
color: string;
|
|
43
|
+
roughness: number;
|
|
44
|
+
metalness: number;
|
|
45
|
+
};
|
|
46
|
+
slice: {
|
|
47
|
+
color: Color;
|
|
48
|
+
roughness: number;
|
|
49
|
+
metalness: number;
|
|
50
|
+
effects: {
|
|
51
|
+
weathering: {
|
|
52
|
+
resistance: number;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
} | {
|
|
56
|
+
color: Color;
|
|
57
|
+
roughness: number;
|
|
58
|
+
metalness: number;
|
|
59
|
+
effects?: undefined;
|
|
60
|
+
};
|
|
61
|
+
};
|