@terraware/web-components 5.0.1 → 5.0.3

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.
Files changed (36) hide show
  1. package/components/VirtualWalkthrough/SplatModel.js +2 -1
  2. package/components/VirtualWalkthrough/SplatRevealRain.d.ts +3 -1
  3. package/components/VirtualWalkthrough/SplatRevealRain.d.ts.map +1 -1
  4. package/components/VirtualWalkthrough/SplatRevealRain.js +15 -8
  5. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts +12 -1
  6. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts.map +1 -1
  7. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +42 -5
  8. package/components/VirtualWalkthrough/useRestartOnVr.d.ts +16 -0
  9. package/components/VirtualWalkthrough/useRestartOnVr.d.ts.map +1 -0
  10. package/components/VirtualWalkthrough/useRestartOnVr.js +38 -0
  11. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.d.ts +32 -0
  12. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.d.ts.map +1 -0
  13. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +88 -0
  14. package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts +8 -12
  15. package/components/VirtualWalkthrough/xr-annotation-candidates.d.ts.map +1 -1
  16. package/components/VirtualWalkthrough/xr-annotation-candidates.js +11 -32
  17. package/components/VirtualWalkthrough/xr-annotation-targeting.d.ts.map +1 -1
  18. package/components/VirtualWalkthrough/xr-annotation-targeting.js +9 -4
  19. package/components/VirtualWalkthrough/xr-exit-button-mask.d.ts +11 -0
  20. package/components/VirtualWalkthrough/xr-exit-button-mask.d.ts.map +1 -0
  21. package/components/VirtualWalkthrough/xr-exit-button-mask.js +41 -0
  22. package/components/VirtualWalkthrough/xr-exit-button.d.ts +3 -16
  23. package/components/VirtualWalkthrough/xr-exit-button.d.ts.map +1 -1
  24. package/components/VirtualWalkthrough/xr-exit-button.js +30 -93
  25. package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts +14 -9
  26. package/components/VirtualWalkthrough/xr-gaze-dwell.d.ts.map +1 -1
  27. package/components/VirtualWalkthrough/xr-gaze-dwell.js +72 -90
  28. package/components/VirtualWalkthrough/xr-progress-pie-material.d.ts +24 -0
  29. package/components/VirtualWalkthrough/xr-progress-pie-material.d.ts.map +1 -0
  30. package/components/VirtualWalkthrough/xr-progress-pie-material.js +174 -0
  31. package/components/VirtualWalkthrough/xr-progress-pie.d.ts +7 -7
  32. package/components/VirtualWalkthrough/xr-progress-pie.d.ts.map +1 -1
  33. package/components/VirtualWalkthrough/xr-progress-pie.js +8 -29
  34. package/hooks/useXr.d.ts.map +1 -1
  35. package/hooks/useXr.js +6 -4
  36. package/package.json +1 -1
@@ -0,0 +1,174 @@
1
+ import { ShaderMaterial, SEMANTIC_TEXCOORD0, SEMANTIC_POSITION, BLEND_NORMAL, CULLFACE_NONE, Texture, ADDRESS_CLAMP_TO_EDGE, FILTER_NEAREST, PIXELFORMAT_RGBA8, Mesh } from 'playcanvas';
2
+
3
+ /** Pie radius as a fraction of the quad's half-extent. */
4
+ const PIE_RADIUS = 0.92;
5
+
6
+ // GLSL only, for the same reason as BoundaryWallScript: @playcanvas/react's Application defaults to
7
+ // deviceTypes [DEVICETYPE_WEBGL2], so WGSL variants would never be compiled here.
8
+ const pieVertexGLSL = /* glsl */`
9
+ attribute vec3 vertex_position;
10
+ attribute vec2 aUv0;
11
+
12
+ uniform mat4 matrix_model;
13
+ uniform mat4 matrix_viewProjection;
14
+
15
+ varying vec2 uv0;
16
+
17
+ void main(void) {
18
+ uv0 = aUv0;
19
+ gl_Position = matrix_viewProjection * matrix_model * vec4(vertex_position, 1.0);
20
+ }
21
+ `;
22
+
23
+ /**
24
+ * Draws the progress sweep analytically from `uProgress`, so animating it costs one uniform write
25
+ * per frame.
26
+ *
27
+ * `uMaskMap` carries whatever static artwork sits around the pie, baked once by the caller: red is
28
+ * a mask for a backing disc drawn under the sweep, green a mask for a glyph drawn over it. Callers
29
+ * with no such artwork bind a transparent pixel and get the bare pie.
30
+ */
31
+ const pieFragmentGLSL = /* glsl */`
32
+ uniform float uProgress;
33
+ uniform vec3 uTrackColor;
34
+ uniform float uTrackAlpha;
35
+ uniform vec3 uWedgeColor;
36
+ uniform float uWedgeAlpha;
37
+ uniform vec3 uDiscColor;
38
+ uniform float uDiscAlpha;
39
+ uniform vec3 uGlyphColor;
40
+ uniform float uOpacity;
41
+ uniform sampler2D uMaskMap;
42
+
43
+ varying vec2 uv0;
44
+
45
+ const float PI = 3.14159265359;
46
+ const float TWO_PI = 6.28318530718;
47
+ const float HALF_PI = 1.57079632679;
48
+ const float PIE_RADIUS = ${PIE_RADIUS};
49
+
50
+ /** Source-over composite of a straight-alpha source onto a straight-alpha destination. */
51
+ vec4 over(vec4 dst, vec4 src) {
52
+ float a = src.a + dst.a * (1.0 - src.a);
53
+ if (a <= 0.0) {
54
+ return vec4(0.0);
55
+ }
56
+
57
+ return vec4((src.rgb * src.a + dst.rgb * dst.a * (1.0 - src.a)) / a, a);
58
+ }
59
+
60
+ void main(void) {
61
+ // The quad's UVs run y-down (v = 0 along the top edge), so centring them gives a coordinate
62
+ // system with +y downward and angles increasing clockwise.
63
+ vec2 p = uv0 * 2.0 - 1.0;
64
+ float d = length(p);
65
+
66
+ // Derivatives are undefined in non-uniform control flow, so every fwidth runs before the
67
+ // discard at the end.
68
+ float radiusFeather = fwidth(d);
69
+ float insideDisc = 1.0 - smoothstep(PIE_RADIUS - radiusFeather, PIE_RADIUS + radiusFeather, d);
70
+
71
+ // Fraction of a full turn, measured clockwise from 12 o'clock.
72
+ float turn = mod(atan(p.y, p.x) + HALF_PI + TWO_PI, TWO_PI) / TWO_PI;
73
+
74
+ // Signed arc-length to the sweep's leading edge, which is smooth either side of that edge and
75
+ // so antialiases cleanly. It jumps at the 12 o'clock seam, where fwidth would blow up and
76
+ // wash the edge out to half coverage, so the feather is capped to keep the smoothstep
77
+ // saturated there instead.
78
+ float edgeDistance = (turn - uProgress) * TWO_PI * d;
79
+ float edgeFeather = min(fwidth(edgeDistance), 0.05);
80
+ float swept = 1.0 - smoothstep(-edgeFeather, edgeFeather, edgeDistance);
81
+
82
+ // A pie at zero progress is not started, and should not show even its track.
83
+ float pie = insideDisc * step(0.0001, uProgress);
84
+
85
+ vec4 mask = texture2D(uMaskMap, uv0);
86
+
87
+ vec4 color = over(vec4(0.0), vec4(uDiscColor, uDiscAlpha * mask.r));
88
+ color = over(color, vec4(uTrackColor, uTrackAlpha * pie));
89
+ color = over(color, vec4(uWedgeColor, uWedgeAlpha * pie * swept));
90
+ color = over(color, vec4(uGlyphColor, mask.g));
91
+ color.a *= uOpacity;
92
+
93
+ if (color.a <= 0.0) {
94
+ discard;
95
+ }
96
+
97
+ gl_FragColor = color;
98
+ }
99
+ `;
100
+
101
+ /** A camera-facing unit quad with y-down UVs, sized to `halfExtent` in local units. */
102
+ const progressPieQuadMesh = (device, halfExtent) => {
103
+ const h = halfExtent;
104
+ const mesh = new Mesh(device);
105
+ mesh.setPositions([-h, -h, 0, h, -h, 0, h, h, 0, -h, h, 0]);
106
+ mesh.setNormals([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
107
+ mesh.setUvs(0, [0, 1, 1, 1, 1, 0, 0, 0]);
108
+ mesh.setIndices([0, 1, 2, 0, 2, 3]);
109
+ mesh.update();
110
+ return mesh;
111
+ };
112
+
113
+ /**
114
+ * A single transparent texel, for pies that carry no static artwork of their own.
115
+ *
116
+ * The filters are set explicitly because the default minification filter samples a mipmap chain,
117
+ * which a single-level texture does not have. That combination is incomplete in WebGL, and what a
118
+ * shader reads from an incomplete texture is not something to rely on.
119
+ */
120
+ const emptyMaskTexture = device => {
121
+ const texture = new Texture(device, {
122
+ name: 'xr-progress-pie-empty-mask',
123
+ width: 1,
124
+ height: 1,
125
+ format: PIXELFORMAT_RGBA8,
126
+ mipmaps: false,
127
+ minFilter: FILTER_NEAREST,
128
+ magFilter: FILTER_NEAREST,
129
+ addressU: ADDRESS_CLAMP_TO_EDGE,
130
+ addressV: ADDRESS_CLAMP_TO_EDGE
131
+ });
132
+ const pixels = texture.lock();
133
+ pixels.set([0, 0, 0, 0]);
134
+ texture.unlock();
135
+ return texture;
136
+ };
137
+ /**
138
+ * Material for a progress pie. Everything that animates rides on the `uProgress` uniform, so a
139
+ * running sweep costs one uniform write per frame and no texture work at all.
140
+ */
141
+ const createProgressPieMaterial = ({
142
+ uniqueName,
143
+ maskMap,
144
+ discColor = [0.08, 0.08, 0.08],
145
+ discAlpha = 0
146
+ }) => {
147
+ const material = new ShaderMaterial({
148
+ uniqueName,
149
+ vertexGLSL: pieVertexGLSL,
150
+ fragmentGLSL: pieFragmentGLSL,
151
+ attributes: {
152
+ vertex_position: SEMANTIC_POSITION,
153
+ aUv0: SEMANTIC_TEXCOORD0
154
+ }
155
+ });
156
+ material.blendType = BLEND_NORMAL;
157
+ material.cull = CULLFACE_NONE;
158
+ material.depthTest = false;
159
+ material.depthWrite = false;
160
+ material.setParameter('uProgress', 0);
161
+ material.setParameter('uTrackColor', [1, 1, 1]);
162
+ material.setParameter('uTrackAlpha', 0.18);
163
+ material.setParameter('uWedgeColor', [64 / 255, 200 / 255, 1]);
164
+ material.setParameter('uWedgeAlpha', 0.85);
165
+ material.setParameter('uDiscColor', discColor);
166
+ material.setParameter('uDiscAlpha', discAlpha);
167
+ material.setParameter('uGlyphColor', [1, 1, 1]);
168
+ material.setParameter('uOpacity', 1);
169
+ material.setParameter('uMaskMap', maskMap);
170
+ material.update();
171
+ return material;
172
+ };
173
+
174
+ export { createProgressPieMaterial, emptyMaskTexture, progressPieQuadMesh };
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Draws a circular progress pie into the `size` x `size` region at the context origin: a faint
3
- * full-circle track under a wedge that sweeps clockwise as `progress` goes 0 -> 1. A `progress`
4
- * above 1 simply sweeps a full circle. Neither clears the context nor uploads a texture, so callers
5
- * can composite it between their own layers. Saves and restores the drawing state it touches and
6
- * leaves the path clean on exit, so callers can stroke or fill right after without an explicit
7
- * `beginPath()` of their own.
2
+ * Progress to hand the pie shader, where 0 draws nothing at all: an untouched target wears no faint
3
+ * track, and a completed one has already opened its annotation or ended the session.
4
+ *
5
+ * Visibility rides on this value so the quad can stay enabled for the whole session. A mesh
6
+ * instance compiles its shader variant the first time it is drawn, and that compile has to land
7
+ * somewhere other than the frame a pie first appears.
8
8
  */
9
- export declare const drawProgressPie: (ctx: CanvasRenderingContext2D, size: number, progress: number) => void;
9
+ export declare const pieShaderProgress: (progress: number) => number;
10
10
  //# sourceMappingURL=xr-progress-pie.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"xr-progress-pie.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/xr-progress-pie.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,wBAAwB,EAAE,MAAM,MAAM,EAAE,UAAU,MAAM,SAsB5F,CAAC"}
1
+ {"version":3,"file":"xr-progress-pie.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/xr-progress-pie.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,MAAuD,CAAC"}
@@ -1,32 +1,11 @@
1
- /** Sweep starts at 12 o'clock. */
2
- const PIE_START_ANGLE = -Math.PI / 2;
3
-
4
1
  /**
5
- * Draws a circular progress pie into the `size` x `size` region at the context origin: a faint
6
- * full-circle track under a wedge that sweeps clockwise as `progress` goes 0 -> 1. A `progress`
7
- * above 1 simply sweeps a full circle. Neither clears the context nor uploads a texture, so callers
8
- * can composite it between their own layers. Saves and restores the drawing state it touches and
9
- * leaves the path clean on exit, so callers can stroke or fill right after without an explicit
10
- * `beginPath()` of their own.
2
+ * Progress to hand the pie shader, where 0 draws nothing at all: an untouched target wears no faint
3
+ * track, and a completed one has already opened its annotation or ended the session.
4
+ *
5
+ * Visibility rides on this value so the quad can stay enabled for the whole session. A mesh
6
+ * instance compiles its shader variant the first time it is drawn, and that compile has to land
7
+ * somewhere other than the frame a pie first appears.
11
8
  */
12
- const drawProgressPie = (ctx, size, progress) => {
13
- const c = size / 2;
14
- const r = c * 0.92;
15
- ctx.save();
16
- ctx.beginPath();
17
- ctx.arc(c, c, r, 0, Math.PI * 2);
18
- ctx.fillStyle = 'rgba(255, 255, 255, 0.18)';
19
- ctx.fill();
20
- if (progress > 0) {
21
- ctx.beginPath();
22
- ctx.moveTo(c, c);
23
- ctx.arc(c, c, r, PIE_START_ANGLE, PIE_START_ANGLE + progress * Math.PI * 2);
24
- ctx.closePath();
25
- ctx.fillStyle = 'rgba(64, 200, 255, 0.85)';
26
- ctx.fill();
27
- }
28
- ctx.restore();
29
- ctx.beginPath();
30
- };
9
+ const pieShaderProgress = progress => progress > 0 && progress < 1 ? progress : 0;
31
10
 
32
- export { drawProgressPie };
11
+ export { pieShaderProgress };
@@ -1 +1 @@
1
- {"version":3,"file":"useXr.d.ts","sourceRoot":"","sources":["../../src/hooks/useXr.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjC,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAgBD,eAAO,MAAM,KAAK,GAAI,cAAa,YAAiB;0BA4CT,MAAM;oBAGtC,MAAM;;;;;CA8BhB,CAAC;AAEF,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"useXr.d.ts","sourceRoot":"","sources":["../../src/hooks/useXr.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjC,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAgBD,eAAO,MAAM,KAAK,GAAI,cAAa,YAAiB;0BAiDT,MAAM;oBAGtC,MAAM;;;;;CA8BhB,CAAC;AAEF,eAAe,KAAK,CAAC"}
package/hooks/useXr.js CHANGED
@@ -19,10 +19,12 @@ const useXr = ({
19
19
  onError
20
20
  } = {}) => {
21
21
  const app = useApp();
22
- const [available, setAvailable] = useState({
23
- VR: false,
24
- AR: false
25
- });
22
+ // Seeded from the manager rather than defaulting to unavailable, so a caller acting in a mount
23
+ // effect isn't told VR is unsupported while the effect below is still waiting to run.
24
+ const [available, setAvailable] = useState(() => ({
25
+ VR: app.xr?.isAvailable(XRTYPE_VR) ?? false,
26
+ AR: app.xr?.isAvailable(XRTYPE_AR) ?? false
27
+ }));
26
28
  // `type` is set as soon as a session is requested, so gate on `active` to ignore pending sessions.
27
29
  const [currentXrType, setCurrentXrType] = useState(app.xr?.active ? app.xr.type : null);
28
30
  useEffect(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terraware/web-components",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "author": "Terraformation Inc.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {