@terraware/web-components 5.0.26 → 5.0.27-rc.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.
Files changed (295) hide show
  1. package/components/AntSwitch/index.js +19 -22
  2. package/components/Autocomplete/Autocomplete.js +106 -130
  3. package/components/Badge/index.js +28 -29
  4. package/components/BusySpinner/index.js +40 -45
  5. package/components/Button/Button.js +41 -47
  6. package/components/Checkbox.js +64 -63
  7. package/components/Confirm/index.js +74 -83
  8. package/components/DatePicker/DatePicker.js +138 -141
  9. package/components/DialogBox/DialogBox.js +111 -118
  10. package/components/Divisor.js +18 -18
  11. package/components/Dropdown.js +66 -94
  12. package/components/EditableTable/index.js +211 -271
  13. package/components/ErrorBox/ErrorBox.js +47 -49
  14. package/components/FileChooser/index.js +117 -135
  15. package/components/FormBottomBar/index.js +75 -84
  16. package/components/Icon/Icon.js +12 -20
  17. package/components/Icon/icons/index.js +256 -256
  18. package/components/IconTooltip/index.js +49 -60
  19. package/components/Map/MapBox.js +545 -452
  20. package/components/Map/MapContainer.js +25 -29
  21. package/components/Map/MapDateSliderControl.js +64 -64
  22. package/components/Map/MapDrawer.js +41 -47
  23. package/components/Map/MapLegend.js +176 -172
  24. package/components/Map/MapViewStyleControl.js +59 -63
  25. package/components/Map/ReactMapboxWrapper.js +3 -2
  26. package/components/Map/index.js +161 -180
  27. package/components/Map/types.js +12 -17
  28. package/components/Map/useMaintainLayerOrder.js +16 -26
  29. package/components/Markdown/index.js +25 -26
  30. package/components/Message/Message.js +73 -76
  31. package/components/MultiSelect/index.js +112 -124
  32. package/components/Navbar/NavFooter.js +9 -13
  33. package/components/Navbar/NavItem.js +70 -76
  34. package/components/Navbar/NavSection.js +17 -19
  35. package/components/Navbar/Navbar.js +39 -46
  36. package/components/Navbar/SubNavbar.js +9 -13
  37. package/components/Note.js +18 -22
  38. package/components/OverlayModal/OverlayModal.js +56 -63
  39. package/components/PageForm/index.js +33 -41
  40. package/components/PhotoChooser/index.js +165 -173
  41. package/components/PhotosCarousel/PhotoSlide.js +32 -35
  42. package/components/PhotosCarousel/index.js +80 -85
  43. package/components/PhotosCarousel/usePhotosCarousel.js +69 -76
  44. package/components/Pill/index.js +41 -45
  45. package/components/PillList/index.js +17 -24
  46. package/components/PlacementWrapper/index.js +38 -59
  47. package/components/PopoverMenu/Popover.js +68 -85
  48. package/components/PopoverMenu/index.js +51 -56
  49. package/components/PopoverMultiSelect/index.js +52 -59
  50. package/components/ProgressCircle/ProgressCircle.js +28 -32
  51. package/components/RadioButton.js +17 -19
  52. package/components/Select/Select.js +13 -15
  53. package/components/Select/SelectT.js +235 -250
  54. package/components/Separator/index.js +14 -17
  55. package/components/Size.js +0 -1
  56. package/components/Slider/index.js +43 -56
  57. package/components/SummaryBox.js +70 -72
  58. package/components/Tabs/index.js +133 -135
  59. package/components/TextTruncated/index.js +117 -130
  60. package/components/Textfield/Textfield.js +158 -190
  61. package/components/Textfield/TruncatedTextArea.js +70 -84
  62. package/components/TimelineSlider/TimelineMarker.js +25 -30
  63. package/components/TimelineSlider/TimelineRail.js +8 -11
  64. package/components/TimelineSlider/index.js +64 -66
  65. package/components/TimelineSliderV2/ClusterBadge.js +25 -29
  66. package/components/TimelineSliderV2/TimelineDot.js +31 -35
  67. package/components/TimelineSliderV2/TimelineRail.js +8 -11
  68. package/components/TimelineSliderV2/index.js +121 -103
  69. package/components/TimelineSliderV2/layout.js +124 -147
  70. package/components/TimelineSliderV2/types.js +0 -1
  71. package/components/Tooltip/Tooltip.js +47 -56
  72. package/components/ViewPhotosDialog/index.js +28 -35
  73. package/components/VirtualWalkthrough/Annotation.js +163 -207
  74. package/components/VirtualWalkthrough/AnnotationEditPane.js +198 -184
  75. package/components/VirtualWalkthrough/AnnotationPanel.js +187 -199
  76. package/components/VirtualWalkthrough/Application.js +31 -34
  77. package/components/VirtualWalkthrough/AutoRotator.js +85 -223
  78. package/components/VirtualWalkthrough/BlockingSpinner.js +26 -28
  79. package/components/VirtualWalkthrough/BoundaryRing.js +30 -34
  80. package/components/VirtualWalkthrough/BoundaryWall.js +32 -36
  81. package/components/VirtualWalkthrough/CameraInfo.js +100 -89
  82. package/components/VirtualWalkthrough/ControlsInfoPane.js +254 -195
  83. package/components/VirtualWalkthrough/GradientSky.js +72 -73
  84. package/components/VirtualWalkthrough/SplatControls.js +209 -221
  85. package/components/VirtualWalkthrough/SplatCrop.js +21 -20
  86. package/components/VirtualWalkthrough/SplatFadeCrop.js +21 -20
  87. package/components/VirtualWalkthrough/SplatModel.js +65 -85
  88. package/components/VirtualWalkthrough/SplatRevealRain.js +41 -46
  89. package/components/VirtualWalkthrough/TfAnnotationManager.js +197 -379
  90. package/components/VirtualWalkthrough/TfXrNavigation.js +73 -162
  91. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +438 -431
  92. package/components/VirtualWalkthrough/VrAnnotationPanel.js +18 -22
  93. package/components/VirtualWalkthrough/XrAnnotationInteraction.js +14 -17
  94. package/components/VirtualWalkthrough/XrExitButton.js +14 -17
  95. package/components/VirtualWalkthrough/XrGazeDwell.js +14 -17
  96. package/components/VirtualWalkthrough/XrPointerRay.js +14 -17
  97. package/components/VirtualWalkthrough/XrStartPose.js +67 -121
  98. package/components/VirtualWalkthrough/boundary-ring.js +87 -117
  99. package/components/VirtualWalkthrough/boundary-wall.js +175 -258
  100. package/components/VirtualWalkthrough/camera-adoption.js +18 -64
  101. package/components/VirtualWalkthrough/groundPlane.js +32 -47
  102. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
  103. package/components/VirtualWalkthrough/splatFormat.js +13 -47
  104. package/components/VirtualWalkthrough/useAdoptedCamera.js +23 -44
  105. package/components/VirtualWalkthrough/useRestartOnVr.js +26 -37
  106. package/components/VirtualWalkthrough/useXrRenderTuning.js +33 -54
  107. package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +67 -99
  108. package/components/VirtualWalkthrough/vr-annotation-panel.js +403 -433
  109. package/components/VirtualWalkthrough/vr-annotation-scroll.js +10 -28
  110. package/components/VirtualWalkthrough/walkthrough-camera.js +204 -329
  111. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +39 -80
  112. package/components/VirtualWalkthrough/xr-annotation-candidates.js +6 -18
  113. package/components/VirtualWalkthrough/xr-annotation-interaction.js +31 -56
  114. package/components/VirtualWalkthrough/xr-annotation-targeting.js +22 -38
  115. package/components/VirtualWalkthrough/xr-button-arming.js +7 -17
  116. package/components/VirtualWalkthrough/xr-button-hold.js +22 -36
  117. package/components/VirtualWalkthrough/xr-exit-button-mask.js +22 -36
  118. package/components/VirtualWalkthrough/xr-exit-button.js +148 -207
  119. package/components/VirtualWalkthrough/xr-face-buttons.js +19 -29
  120. package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +23 -34
  121. package/components/VirtualWalkthrough/xr-gaze-dwell.js +107 -144
  122. package/components/VirtualWalkthrough/xr-interactive-ui.js +12 -24
  123. package/components/VirtualWalkthrough/xr-pointer-ray.js +13 -20
  124. package/components/VirtualWalkthrough/xr-progress-pie-material.js +117 -88
  125. package/components/VirtualWalkthrough/xr-progress-pie.js +1 -10
  126. package/components/VirtualWalkthrough/xr-ray-quad.js +16 -30
  127. package/components/VirtualWalkthrough/xr-render-tuning.js +17 -48
  128. package/components/VirtualWalkthrough/xr-scene-bounds.js +21 -37
  129. package/components/VirtualWalkthrough/xr-start-pose.js +27 -73
  130. package/components/VirtualWalkthrough/xr-teleport-gesture.js +16 -42
  131. package/components/svg/BlobbyGrayIconImage.js +41 -39
  132. package/components/svg/BlobbyGrayIconImport.js +41 -39
  133. package/components/svg/BlobbyGrayIconUploadToTheCloud.js +41 -39
  134. package/components/svg/BlobbyIconCloudChecked.js +202 -164
  135. package/components/svg/BlobbyIconExportOrUploadPhoto.js +202 -164
  136. package/components/svg/BlobbyIconFolder.js +202 -164
  137. package/components/svg/BlobbyIconGraphReport.js +202 -164
  138. package/components/svg/BlobbyIconHappy.js +202 -164
  139. package/components/svg/BlobbyIconHeartMonitor.js +202 -164
  140. package/components/svg/BlobbyIconLeaf.js +202 -164
  141. package/components/svg/BlobbyIconLibrary.js +202 -164
  142. package/components/svg/BlobbyIconNursery.js +204 -166
  143. package/components/svg/BlobbyIconOrganization.js +202 -164
  144. package/components/svg/BlobbyIconParchment.js +202 -164
  145. package/components/svg/BlobbyIconPeople.js +202 -164
  146. package/components/svg/BlobbyIconSeedBank.js +202 -164
  147. package/components/svg/BlobbyIconSeedling.js +202 -164
  148. package/components/svg/BlobbyIconSeeds.js +202 -164
  149. package/components/svg/BlobbyIconSite.js +206 -167
  150. package/components/svg/BlobbyIconSparkles.js +202 -164
  151. package/components/svg/BlobbyIconWrench.js +202 -164
  152. package/components/svg/Bug.js +19 -21
  153. package/components/svg/Calendar.js +19 -21
  154. package/components/svg/CaretDown.js +19 -21
  155. package/components/svg/CaretLeft.js +19 -21
  156. package/components/svg/CaretRight.js +19 -21
  157. package/components/svg/CaretUp.js +19 -21
  158. package/components/svg/ChevronDown.js +19 -21
  159. package/components/svg/ChevronUp.js +19 -21
  160. package/components/svg/Close.js +19 -21
  161. package/components/svg/Critical.js +19 -21
  162. package/components/svg/Edit.js +202 -164
  163. package/components/svg/Error.js +19 -21
  164. package/components/svg/Filter.js +19 -21
  165. package/components/svg/Folder.js +19 -21
  166. package/components/svg/Help.js +19 -21
  167. package/components/svg/Home.js +19 -21
  168. package/components/svg/IconAdd.js +19 -21
  169. package/components/svg/IconArrowRight.js +21 -23
  170. package/components/svg/IconBusinessNetwork.js +19 -21
  171. package/components/svg/IconCalendar.js +19 -21
  172. package/components/svg/IconCancel.js +19 -21
  173. package/components/svg/IconChargingBattery.js +19 -21
  174. package/components/svg/IconChecklist.js +21 -23
  175. package/components/svg/IconCheckmark.js +19 -21
  176. package/components/svg/IconCoinInHand.js +19 -21
  177. package/components/svg/IconColumns.js +19 -21
  178. package/components/svg/IconComment.js +20 -22
  179. package/components/svg/IconDashboard.js +19 -21
  180. package/components/svg/IconDataMigration.js +19 -21
  181. package/components/svg/IconDocument.js +21 -23
  182. package/components/svg/IconDownloadFromTheCloud.js +21 -23
  183. package/components/svg/IconEdit.js +21 -23
  184. package/components/svg/IconExpand.js +21 -23
  185. package/components/svg/IconExport.js +19 -21
  186. package/components/svg/IconExternalLink.js +19 -21
  187. package/components/svg/IconEye.js +19 -21
  188. package/components/svg/IconEyeOff.js +19 -21
  189. package/components/svg/IconFile.js +19 -21
  190. package/components/svg/IconFolder.js +19 -21
  191. package/components/svg/IconFullScreen.js +20 -22
  192. package/components/svg/IconFutures.js +19 -21
  193. package/components/svg/IconGraphReport.js +19 -21
  194. package/components/svg/IconGrid.js +19 -21
  195. package/components/svg/IconHeartMonitor.js +19 -21
  196. package/components/svg/IconHelp.js +21 -23
  197. package/components/svg/IconHistory.js +19 -21
  198. package/components/svg/IconImport.js +19 -21
  199. package/components/svg/IconIndex.js +19 -21
  200. package/components/svg/IconLayers.js +19 -21
  201. package/components/svg/IconLibrary.js +20 -22
  202. package/components/svg/IconList.js +19 -21
  203. package/components/svg/IconLivePlant.js +19 -21
  204. package/components/svg/IconLocations.js +19 -21
  205. package/components/svg/IconMail.js +19 -21
  206. package/components/svg/IconManager.js +19 -21
  207. package/components/svg/IconMap.js +19 -21
  208. package/components/svg/IconMarker.js +19 -21
  209. package/components/svg/IconMenu.js +19 -21
  210. package/components/svg/IconMenuHorizontal.js +19 -21
  211. package/components/svg/IconModule.js +19 -21
  212. package/components/svg/IconMyLocation.js +19 -21
  213. package/components/svg/IconNote.js +21 -23
  214. package/components/svg/IconNursery.js +21 -23
  215. package/components/svg/IconOrg.js +19 -21
  216. package/components/svg/IconParchment.js +19 -21
  217. package/components/svg/IconPhoto.js +19 -21
  218. package/components/svg/IconPlantsFilled.js +19 -21
  219. package/components/svg/IconRedo.js +19 -21
  220. package/components/svg/IconRestorationSite.js +19 -21
  221. package/components/svg/IconSeedBank.js +19 -21
  222. package/components/svg/IconSeedling.js +19 -21
  223. package/components/svg/IconSettings.js +19 -21
  224. package/components/svg/IconSlice.js +19 -21
  225. package/components/svg/IconSubmit.js +19 -21
  226. package/components/svg/IconSubtract.js +21 -23
  227. package/components/svg/IconSynced.js +19 -21
  228. package/components/svg/IconTrashCan.js +19 -21
  229. package/components/svg/IconTreasureMap.js +19 -21
  230. package/components/svg/IconUnavailable.js +19 -21
  231. package/components/svg/IconUndo.js +19 -21
  232. package/components/svg/IconVariable.js +19 -21
  233. package/components/svg/IconVideo.js +30 -31
  234. package/components/svg/IconWifi.js +19 -21
  235. package/components/svg/Info.js +19 -21
  236. package/components/svg/Key.js +19 -21
  237. package/components/svg/Leaf.js +19 -21
  238. package/components/svg/Lock.js +19 -21
  239. package/components/svg/Logo.js +48 -43
  240. package/components/svg/MenuVertical.js +19 -21
  241. package/components/svg/Notification.js +19 -21
  242. package/components/svg/Person.js +19 -21
  243. package/components/svg/Plus.js +19 -21
  244. package/components/svg/RestorationSite.js +19 -21
  245. package/components/svg/Search.js +19 -21
  246. package/components/svg/Seeds.js +19 -21
  247. package/components/svg/Site.js +19 -21
  248. package/components/svg/Sparkles.js +19 -21
  249. package/components/svg/Species.js +19 -21
  250. package/components/svg/Species2.js +202 -164
  251. package/components/svg/Spinner.js +54 -48
  252. package/components/svg/Star.js +19 -21
  253. package/components/svg/Success.js +19 -21
  254. package/components/svg/SuccessFilled.js +19 -21
  255. package/components/svg/TerrawareLogoDesktop.js +55 -49
  256. package/components/svg/TerrawareLogoMobile.js +25 -26
  257. package/components/svg/Touchscreen.js +19 -21
  258. package/components/svg/UploadCloud.js +202 -164
  259. package/components/svg/Warning.js +19 -21
  260. package/components/svg/WelcomeClipboard.js +156 -133
  261. package/components/svg/index.js +129 -129
  262. package/components/table/EnhancedTableToolbar.js +68 -74
  263. package/components/table/EnhancedTableToolbarV2.js +31 -41
  264. package/components/table/TableCellRenderer.js +196 -231
  265. package/components/table/TableHeader.js +71 -73
  266. package/components/table/TableHeaderItem.js +97 -102
  267. package/components/table/density.js +18 -19
  268. package/components/table/index.js +350 -369
  269. package/components/table/sort.js +27 -52
  270. package/components/table/types.js +0 -1
  271. package/components/types/index.js +0 -1
  272. package/hooks/cameraEntityContext.js +2 -13
  273. package/hooks/useBoolean.js +17 -13
  274. package/hooks/useCameraPosition.js +56 -56
  275. package/hooks/useDevicePerformance.js +17 -17
  276. package/hooks/useXr.js +85 -87
  277. package/index.js +58 -58
  278. package/package.json +1 -1
  279. package/setupTests.js +0 -4
  280. package/strings/export.js +111 -170
  281. package/strings/index.js +25 -54
  282. package/strings/locales.js +21 -41
  283. package/strings/strings-en.js +49 -51
  284. package/strings/strings-es.js +49 -51
  285. package/strings/strings-fr.js +49 -51
  286. package/strings/strings-gx.js +49 -51
  287. package/style-dictionary-dist/TerrawareTheme.js +186 -186
  288. package/theme.js +74 -76
  289. package/utils/color.js +8 -15
  290. package/utils/date.js +42 -108
  291. package/utils/index.js +4 -4
  292. package/utils/preventDefaultEvent.js +3 -3
  293. package/utils/text.js +11 -11
  294. package/utils/useDeviceInfo.js +42 -79
  295. package/virtualWalkthrough.js +23 -23
@@ -1,157 +1,120 @@
1
- import { Vec3, Script, Quat, XRTYPE_VR, MeshInstance, Entity, LAYERID_IMMEDIATE } from 'playcanvas';
2
- import { createAnnotationCandidateBuffer } from './xr-annotation-candidates.js';
3
- import { nearestAnnotationHit } from './xr-annotation-targeting.js';
4
- import { INITIAL_DWELL_STATE, advanceDwell } from './xr-gaze-dwell-state.js';
5
- import { pieShaderProgress } from './xr-progress-pie.js';
6
- import { emptyMaskTexture, createProgressPieMaterial, progressPieQuadMesh } from './xr-progress-pie-material.js';
7
- import { HOTSPOT_HALF_EXTENT } from './xr-annotation-candidate-buffer.js';
8
-
9
- /** Multiplier on the hotspot's world radius for gaze targeting. */
1
+ import { Entity, LAYERID_IMMEDIATE, MeshInstance, Quat, Script, Vec3, XRTYPE_VR } from "playcanvas";
2
+ import { HOTSPOT_HALF_EXTENT, createAnnotationCandidateBuffer } from "./xr-annotation-candidates.js";
3
+ import { nearestAnnotationHit } from "./xr-annotation-targeting.js";
4
+ import { INITIAL_DWELL_STATE, advanceDwell } from "./xr-gaze-dwell-state.js";
5
+ import { pieShaderProgress } from "./xr-progress-pie.js";
6
+ import { createProgressPieMaterial, emptyMaskTexture, progressPieQuadMesh } from "./xr-progress-pie-material.js";
10
7
  const GAZE_HIT_RADIUS_PAD = 5;
11
-
12
- /** Seconds of continuous gaze required to open an annotation. */
13
8
  const DWELL_SECONDS = 1.25;
14
-
15
- /** Progress-pie quad radius as a multiple of the hotspot's rendered half-extent (covers the hotspot). */
16
9
  const PIE_RADIUS_SCALE = 1.25;
17
10
  const LOCAL_FORWARD = new Vec3(0, 0, -1);
18
11
  class XrGazeDwell extends Script {
19
- static scriptName = 'xrGazeDwell';
20
-
21
- /** Index of the annotation whose panel is currently open, or -1. Excluded from gaze targets so
22
- * dwell opens OTHER annotations while one is open (and never re-dwells the open one). */
23
- activeIndex = -1;
24
- _dwell = INITIAL_DWELL_STATE;
25
- _candidates = createAnnotationCandidateBuffer();
26
- _headPos = new Vec3();
27
- _headRot = new Quat();
28
- _viewOffset = new Vec3();
29
- _forward = new Vec3();
30
- _scratchScale = new Vec3();
31
- _isVrActive = () => this.app.xr?.active === true && this.app.xr?.type === XRTYPE_VR;
32
-
33
- /**
34
- * A render component hands its mesh instances to a layer only when it flips from disabled to
35
- * enabled, and hands over nothing if the layer cannot be resolved yet. The session events are the
36
- * points where the layers are known to exist, so the pie flips there: start registers it, and end
37
- * restores the transition for the next session.
38
- */
39
- _onXrStart = () => {
40
- this._dwell = INITIAL_DWELL_STATE;
41
- this._setPieEnabled(this._isVrActive());
42
- };
43
- _onXrEnd = () => {
44
- this._dwell = INITIAL_DWELL_STATE;
45
- this._pieMaterial?.setParameter('uProgress', 0);
46
- this._setPieEnabled(false);
47
- };
48
- _setPieEnabled(enabled) {
49
- if (this._pieEntity) {
50
- this._pieEntity.enabled = enabled;
12
+ static scriptName = 'xrGazeDwell';
13
+ activeIndex = -1;
14
+ _dwell = INITIAL_DWELL_STATE;
15
+ _candidates = createAnnotationCandidateBuffer();
16
+ _pieEntity;
17
+ _pieMaterial;
18
+ _pieMesh;
19
+ _pieMask;
20
+ _headPos = new Vec3();
21
+ _headRot = new Quat();
22
+ _viewOffset = new Vec3();
23
+ _forward = new Vec3();
24
+ _scratchScale = new Vec3();
25
+ _isVrActive = ()=>this.app.xr?.active === true && this.app.xr?.type === XRTYPE_VR;
26
+ _onXrStart = ()=>{
27
+ this._dwell = INITIAL_DWELL_STATE;
28
+ this._setPieEnabled(this._isVrActive());
29
+ };
30
+ _onXrEnd = ()=>{
31
+ this._dwell = INITIAL_DWELL_STATE;
32
+ this._pieMaterial?.setParameter('uProgress', 0);
33
+ this._setPieEnabled(false);
34
+ };
35
+ _setPieEnabled(enabled) {
36
+ if (this._pieEntity) this._pieEntity.enabled = enabled;
51
37
  }
52
- }
53
- initialize() {
54
- this._pieMask = emptyMaskTexture(this.app.graphicsDevice);
55
- this._pieMaterial = createProgressPieMaterial({
56
- uniqueName: 'xr-gaze-dwell-pie',
57
- maskMap: this._pieMask
58
- });
59
- this._pieMesh = progressPieQuadMesh(this.app.graphicsDevice, HOTSPOT_HALF_EXTENT);
60
- const meshInstance = new MeshInstance(this._pieMesh, this._pieMaterial);
61
-
62
- // Never frustum-culled. The quad only takes a position once a sweep starts, and until then sits
63
- // at the origin where culling would drop it from the render list - taking the shader compile it
64
- // stays enabled for along with it. An always-submitted quad that discards every fragment costs
65
- // less than that compile landing mid-gaze.
66
- meshInstance.cull = false;
67
- this._pieEntity = new Entity('xr-gaze-dwell-pie');
68
- this._pieEntity.addComponent('render', {
69
- meshInstances: [meshInstance],
70
- layers: [LAYERID_IMMEDIATE]
71
- });
72
- this._pieEntity.enabled = false;
73
- this.entity.addChild(this._pieEntity);
74
-
75
- // Both orders happen: the walkthrough can mount into a session that has already started (as it
76
- // does when VR is entered from outside the canvas), or mount first and wait for one.
77
- this._setPieEnabled(this._isVrActive());
78
- this.app.xr?.on('start', this._onXrStart);
79
- this.app.xr?.on('end', this._onXrEnd);
80
- }
81
- update(dt) {
82
- if (!this._pieEntity || !this._pieMaterial) {
83
- return;
38
+ initialize() {
39
+ this._pieMask = emptyMaskTexture(this.app.graphicsDevice);
40
+ this._pieMaterial = createProgressPieMaterial({
41
+ uniqueName: 'xr-gaze-dwell-pie',
42
+ maskMap: this._pieMask
43
+ });
44
+ this._pieMesh = progressPieQuadMesh(this.app.graphicsDevice, HOTSPOT_HALF_EXTENT);
45
+ const meshInstance = new MeshInstance(this._pieMesh, this._pieMaterial);
46
+ meshInstance.cull = false;
47
+ this._pieEntity = new Entity('xr-gaze-dwell-pie');
48
+ this._pieEntity.addComponent('render', {
49
+ meshInstances: [
50
+ meshInstance
51
+ ],
52
+ layers: [
53
+ LAYERID_IMMEDIATE
54
+ ]
55
+ });
56
+ this._pieEntity.enabled = false;
57
+ this.entity.addChild(this._pieEntity);
58
+ this._setPieEnabled(this._isVrActive());
59
+ this.app.xr?.on('start', this._onXrStart);
60
+ this.app.xr?.on('end', this._onXrEnd);
84
61
  }
85
- if (!this._isVrActive() || !this._trackHead()) {
86
- this._dwell = INITIAL_DWELL_STATE;
87
- this._pieMaterial.setParameter('uProgress', 0);
88
- return;
62
+ update(dt) {
63
+ if (!this._pieEntity || !this._pieMaterial) return;
64
+ if (!this._isVrActive() || !this._trackHead()) {
65
+ this._dwell = INITIAL_DWELL_STATE;
66
+ this._pieMaterial.setParameter('uProgress', 0);
67
+ return;
68
+ }
69
+ const candidates = this._candidates.collect(this.app, GAZE_HIT_RADIUS_PAD, `annotation-${this.activeIndex}`);
70
+ this._headRot.transformVector(LOCAL_FORWARD, this._forward);
71
+ const target = nearestAnnotationHit(this._headPos, this._forward, candidates);
72
+ const result = advanceDwell(this._dwell, target, dt, DWELL_SECONDS);
73
+ this._dwell = result.state;
74
+ const progress = pieShaderProgress(result.progress);
75
+ this._pieMaterial.setParameter('uProgress', progress);
76
+ if (progress > 0 && null !== target) this._placePie(candidates[target].entity);
77
+ if (result.justOpened && null !== target) {
78
+ const { entity, script } = candidates[target];
79
+ const camera = this.app.root.findComponent('camera');
80
+ const screen = camera?.worldToScreen(entity.getPosition());
81
+ script.onVrOpenCallback(screen?.x ?? 0, screen?.y ?? 0);
82
+ }
89
83
  }
90
- const candidates = this._candidates.collect(this.app, GAZE_HIT_RADIUS_PAD, `annotation-${this.activeIndex}`);
91
- this._headRot.transformVector(LOCAL_FORWARD, this._forward);
92
- const target = nearestAnnotationHit(this._headPos, this._forward, candidates);
93
- const result = advanceDwell(this._dwell, target, dt, DWELL_SECONDS);
94
- this._dwell = result.state;
95
- const progress = pieShaderProgress(result.progress);
96
- this._pieMaterial.setParameter('uProgress', progress);
97
- if (progress > 0 && target !== null) {
98
- this._placePie(candidates[target].entity);
84
+ _trackHead() {
85
+ const views = this.app.xr?.views?.list;
86
+ if (!views || 0 === views.length) return false;
87
+ this._headPos.set(0, 0, 0);
88
+ for (const view of views){
89
+ view.viewInvOffMat.getTranslation(this._viewOffset);
90
+ this._headPos.add(this._viewOffset);
91
+ }
92
+ this._headPos.mulScalar(1 / views.length);
93
+ this._headRot.setFromMat4(views[0].viewInvOffMat);
94
+ return true;
99
95
  }
100
- if (result.justOpened && target !== null) {
101
- const {
102
- entity,
103
- script
104
- } = candidates[target];
105
- const camera = this.app.root.findComponent('camera');
106
- const screen = camera?.worldToScreen(entity.getPosition());
107
- script.onVrOpenCallback(screen?.x ?? 0, screen?.y ?? 0);
96
+ _placePie(entity) {
97
+ if (!this._pieEntity) return;
98
+ entity.getWorldTransform().getScale(this._scratchScale);
99
+ const scale = this._scratchScale.x * PIE_RADIUS_SCALE;
100
+ this._pieEntity.setLocalScale(scale, scale, scale);
101
+ this._pieEntity.setPosition(entity.getPosition());
102
+ this._pieEntity.setRotation(this._headRot);
108
103
  }
109
- }
110
- _trackHead() {
111
- const views = this.app.xr?.views?.list;
112
- if (!views || views.length === 0) {
113
- return false;
104
+ destroy() {
105
+ this.app.xr?.off('start', this._onXrStart);
106
+ this.app.xr?.off('end', this._onXrEnd);
107
+ if (this._pieEntity) {
108
+ if (this._pieEntity.render) this._pieEntity.render.meshInstances = [];
109
+ this._pieEntity.destroy();
110
+ this._pieEntity = void 0;
111
+ }
112
+ this._pieMesh?.destroy();
113
+ this._pieMaterial?.destroy();
114
+ this._pieMask?.destroy();
115
+ this._pieMesh = void 0;
116
+ this._pieMaterial = void 0;
117
+ this._pieMask = void 0;
114
118
  }
115
- this._headPos.set(0, 0, 0);
116
- for (const view of views) {
117
- view.viewInvOffMat.getTranslation(this._viewOffset);
118
- this._headPos.add(this._viewOffset);
119
- }
120
- this._headPos.mulScalar(1 / views.length);
121
- this._headRot.setFromMat4(views[0].viewInvOffMat);
122
- return true;
123
- }
124
-
125
- /** Sits the pie on the hotspot, square to the head. */
126
- _placePie(entity) {
127
- if (!this._pieEntity) {
128
- return;
129
- }
130
-
131
- // The unit quad has half-extent HOTSPOT_HALF_EXTENT, so scale it to reach the desired world size.
132
- entity.getWorldTransform().getScale(this._scratchScale);
133
- const scale = this._scratchScale.x * PIE_RADIUS_SCALE;
134
- this._pieEntity.setLocalScale(scale, scale, scale);
135
- this._pieEntity.setPosition(entity.getPosition());
136
- this._pieEntity.setRotation(this._headRot);
137
- }
138
- destroy() {
139
- this.app.xr?.off('start', this._onXrStart);
140
- this.app.xr?.off('end', this._onXrEnd);
141
- if (this._pieEntity) {
142
- if (this._pieEntity.render) {
143
- this._pieEntity.render.meshInstances = [];
144
- }
145
- this._pieEntity.destroy();
146
- this._pieEntity = undefined;
147
- }
148
- this._pieMesh?.destroy();
149
- this._pieMaterial?.destroy();
150
- this._pieMask?.destroy();
151
- this._pieMesh = undefined;
152
- this._pieMaterial = undefined;
153
- this._pieMask = undefined;
154
- }
155
119
  }
156
-
157
120
  export { XrGazeDwell };
@@ -1,27 +1,15 @@
1
- import { VrAnnotationPanel } from './vr-annotation-panel.js';
2
- import { collectAnnotationHitCandidates, HIT_RADIUS_PAD } from './xr-annotation-candidates.js';
3
- import { nearestAnnotationHit } from './xr-annotation-targeting.js';
4
- import { XrExitButton } from './xr-exit-button.js';
5
-
6
- /** True when the ray points at an openable annotation hotspot. */
7
- const rayHitsAnnotationHotspot = (app, origin, direction) => nearestAnnotationHit(origin, direction, collectAnnotationHitCandidates(app, HIT_RADIUS_PAD)) !== null;
8
-
9
- /** True when the ray points at the open VR annotation panel. False when no panel is mounted. */
10
- const rayHitsAnnotationPanel = (app, origin, direction) => {
11
- const script = app.root.findByName('vr-annotation-panel')?.script?.get(VrAnnotationPanel.scriptName);
12
- return typeof script?.rayHitsPanel === 'function' && script.rayHitsPanel(origin, direction) === true;
1
+ import { VrAnnotationPanel } from "./vr-annotation-panel.js";
2
+ import { HIT_RADIUS_PAD, collectAnnotationHitCandidates } from "./xr-annotation-candidates.js";
3
+ import { nearestAnnotationHit } from "./xr-annotation-targeting.js";
4
+ import { XrExitButton } from "./xr-exit-button.js";
5
+ const rayHitsAnnotationHotspot = (app, origin, direction)=>null !== nearestAnnotationHit(origin, direction, collectAnnotationHitCandidates(app, HIT_RADIUS_PAD));
6
+ const rayHitsAnnotationPanel = (app, origin, direction)=>{
7
+ const script = app.root.findByName('vr-annotation-panel')?.script?.get(VrAnnotationPanel.scriptName);
8
+ return 'function' == typeof script?.rayHitsPanel && true === script.rayHitsPanel(origin, direction);
13
9
  };
14
-
15
- /** True when the ray points at the VR exit button. */
16
- const rayHitsExitButton = (app, origin, direction) => {
17
- const script = app.root.findByName('xr-exit-button')?.script?.get(XrExitButton.scriptName);
18
- return typeof script?.rayHitsButton === 'function' && script.rayHitsButton(origin, direction) === true;
10
+ const rayHitsExitButton = (app, origin, direction)=>{
11
+ const script = app.root.findByName('xr-exit-button')?.script?.get(XrExitButton.scriptName);
12
+ return 'function' == typeof script?.rayHitsButton && true === script.rayHitsButton(origin, direction);
19
13
  };
20
-
21
- /**
22
- * True when the ray points at anything the trigger is meant to operate. Teleport consults this so a
23
- * select that lands on UI doesn't also move the rig.
24
- */
25
- const rayHitsInteractiveUi = (app, origin, direction) => rayHitsAnnotationHotspot(app, origin, direction) || rayHitsAnnotationPanel(app, origin, direction) || rayHitsExitButton(app, origin, direction);
26
-
14
+ const rayHitsInteractiveUi = (app, origin, direction)=>rayHitsAnnotationHotspot(app, origin, direction) || rayHitsAnnotationPanel(app, origin, direction) || rayHitsExitButton(app, origin, direction);
27
15
  export { rayHitsAnnotationHotspot, rayHitsAnnotationPanel, rayHitsExitButton, rayHitsInteractiveUi };
@@ -1,26 +1,19 @@
1
- import { Color, Script, Vec3 } from 'playcanvas';
2
-
3
- /** Length of the drawn pointer ray, in world meters. */
1
+ import { Color, Script, Vec3 } from "playcanvas";
4
2
  const RAY_LENGTH = 5;
5
-
6
- /** Default ray colour (cyan) — bright enough to read against the scene. */
7
3
  const DEFAULT_RAY_COLOR = new Color(0.25, 0.8, 1);
8
4
  class XrPointerRay extends Script {
9
- static scriptName = 'xrPointerRay';
10
- color = DEFAULT_RAY_COLOR.clone();
11
- _end = new Vec3();
12
- update() {
13
- if (this.app.xr?.active !== true) {
14
- return;
5
+ static scriptName = 'xrPointerRay';
6
+ color = DEFAULT_RAY_COLOR.clone();
7
+ _end = new Vec3();
8
+ update() {
9
+ if (this.app.xr?.active !== true) return;
10
+ const sources = this.app.xr?.input?.inputSources ?? [];
11
+ for (const source of sources){
12
+ const origin = source.getOrigin();
13
+ const direction = source.getDirection();
14
+ this._end.copy(direction).mulScalar(RAY_LENGTH).add(origin);
15
+ this.app.drawLine(origin, this._end, this.color, false);
16
+ }
15
17
  }
16
- const sources = this.app.xr?.input?.inputSources ?? [];
17
- for (const source of sources) {
18
- const origin = source.getOrigin();
19
- const direction = source.getDirection();
20
- this._end.copy(direction).mulScalar(RAY_LENGTH).add(origin);
21
- this.app.drawLine(origin, this._end, this.color, false);
22
- }
23
- }
24
18
  }
25
-
26
19
  export { DEFAULT_RAY_COLOR, XrPointerRay };
@@ -1,11 +1,6 @@
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. */
1
+ import { ADDRESS_CLAMP_TO_EDGE, BLEND_NORMAL, CULLFACE_NONE, FILTER_NEAREST, Mesh, PIXELFORMAT_RGBA8, SEMANTIC_POSITION, SEMANTIC_TEXCOORD0, ShaderMaterial, Texture } from "playcanvas";
4
2
  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 */`
3
+ const pieVertexGLSL = `
9
4
  attribute vec3 vertex_position;
10
5
  attribute vec2 aUv0;
11
6
 
@@ -19,16 +14,7 @@ const pieVertexGLSL = /* glsl */`
19
14
  gl_Position = matrix_viewProjection * matrix_model * vec4(vertex_position, 1.0);
20
15
  }
21
16
  `;
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 */`
17
+ const pieFragmentGLSL = `
32
18
  uniform float uProgress;
33
19
  uniform vec3 uTrackColor;
34
20
  uniform float uTrackAlpha;
@@ -97,78 +83,121 @@ const pieFragmentGLSL = /* glsl */`
97
83
  gl_FragColor = color;
98
84
  }
99
85
  `;
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;
86
+ const progressPieQuadMesh = (device, halfExtent)=>{
87
+ const h = halfExtent;
88
+ const mesh = new Mesh(device);
89
+ mesh.setPositions([
90
+ -h,
91
+ -h,
92
+ 0,
93
+ h,
94
+ -h,
95
+ 0,
96
+ h,
97
+ h,
98
+ 0,
99
+ -h,
100
+ h,
101
+ 0
102
+ ]);
103
+ mesh.setNormals([
104
+ 0,
105
+ 0,
106
+ 1,
107
+ 0,
108
+ 0,
109
+ 1,
110
+ 0,
111
+ 0,
112
+ 1,
113
+ 0,
114
+ 0,
115
+ 1
116
+ ]);
117
+ mesh.setUvs(0, [
118
+ 0,
119
+ 1,
120
+ 1,
121
+ 1,
122
+ 1,
123
+ 0,
124
+ 0,
125
+ 0
126
+ ]);
127
+ mesh.setIndices([
128
+ 0,
129
+ 1,
130
+ 2,
131
+ 0,
132
+ 2,
133
+ 3
134
+ ]);
135
+ mesh.update();
136
+ return mesh;
111
137
  };
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;
138
+ const emptyMaskTexture = (device)=>{
139
+ const texture = new Texture(device, {
140
+ name: 'xr-progress-pie-empty-mask',
141
+ width: 1,
142
+ height: 1,
143
+ format: PIXELFORMAT_RGBA8,
144
+ mipmaps: false,
145
+ minFilter: FILTER_NEAREST,
146
+ magFilter: FILTER_NEAREST,
147
+ addressU: ADDRESS_CLAMP_TO_EDGE,
148
+ addressV: ADDRESS_CLAMP_TO_EDGE
149
+ });
150
+ const pixels = texture.lock();
151
+ pixels.set([
152
+ 0,
153
+ 0,
154
+ 0,
155
+ 0
156
+ ]);
157
+ texture.unlock();
158
+ return texture;
136
159
  };
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;
160
+ const createProgressPieMaterial = ({ uniqueName, maskMap, discColor = [
161
+ 0.08,
162
+ 0.08,
163
+ 0.08
164
+ ], discAlpha = 0 })=>{
165
+ const material = new ShaderMaterial({
166
+ uniqueName,
167
+ vertexGLSL: pieVertexGLSL,
168
+ fragmentGLSL: pieFragmentGLSL,
169
+ attributes: {
170
+ vertex_position: SEMANTIC_POSITION,
171
+ aUv0: SEMANTIC_TEXCOORD0
172
+ }
173
+ });
174
+ material.blendType = BLEND_NORMAL;
175
+ material.cull = CULLFACE_NONE;
176
+ material.depthTest = false;
177
+ material.depthWrite = false;
178
+ material.setParameter('uProgress', 0);
179
+ material.setParameter('uTrackColor', [
180
+ 1,
181
+ 1,
182
+ 1
183
+ ]);
184
+ material.setParameter('uTrackAlpha', 0.18);
185
+ material.setParameter('uWedgeColor', [
186
+ 64 / 255,
187
+ 200 / 255,
188
+ 1
189
+ ]);
190
+ material.setParameter('uWedgeAlpha', 0.85);
191
+ material.setParameter('uDiscColor', discColor);
192
+ material.setParameter('uDiscAlpha', discAlpha);
193
+ material.setParameter('uGlyphColor', [
194
+ 1,
195
+ 1,
196
+ 1
197
+ ]);
198
+ material.setParameter('uOpacity', 1);
199
+ material.setParameter('uMaskMap', maskMap);
200
+ material.update();
201
+ return material;
172
202
  };
173
-
174
203
  export { createProgressPieMaterial, emptyMaskTexture, progressPieQuadMesh };
@@ -1,11 +1,2 @@
1
- /**
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
- */
9
- const pieShaderProgress = progress => progress > 0 && progress < 1 ? progress : 0;
10
-
1
+ const pieShaderProgress = (progress)=>progress > 0 && progress < 1 ? progress : 0;
11
2
  export { pieShaderProgress };
@@ -1,32 +1,18 @@
1
- import { Vec3 } from 'playcanvas';
2
-
3
- /**
4
- * Intersects the ray with the rectangle centred at `center` and spanned by unit axes `right`/`up`,
5
- * returning the hit in normalized quad coordinates (u,v in [-1,1]) or null. `u`/`v` divide the
6
- * in-plane offset by `halfWidth`/`halfHeight`. Null when the ray is parallel to the plane, the
7
- * plane is behind the origin, or the hit lands outside the rectangle.
8
- */
9
- const rayQuadHit = (origin, direction, center, right, up, halfWidth, halfHeight) => {
10
- const dir = direction.clone().normalize();
11
- const normal = new Vec3().cross(right, up);
12
- const denom = dir.dot(normal);
13
- if (Math.abs(denom) < 1e-6) {
14
- return null;
15
- }
16
- const t = new Vec3().sub2(center, origin).dot(normal) / denom;
17
- if (t < 0) {
18
- return null;
19
- }
20
- const offset = new Vec3().copy(dir).mulScalar(t).add(origin).sub(center);
21
- const u = offset.dot(right) / halfWidth;
22
- const v = offset.dot(up) / halfHeight;
23
- if (u < -1 || u > 1 || v < -1 || v > 1) {
24
- return null;
25
- }
26
- return {
27
- u,
28
- v
29
- };
1
+ import { Vec3 } from "playcanvas";
2
+ const rayQuadHit = (origin, direction, center, right, up, halfWidth, halfHeight)=>{
3
+ const dir = direction.clone().normalize();
4
+ const normal = new Vec3().cross(right, up);
5
+ const denom = dir.dot(normal);
6
+ if (Math.abs(denom) < 1e-6) return null;
7
+ const t = new Vec3().sub2(center, origin).dot(normal) / denom;
8
+ if (t < 0) return null;
9
+ const offset = new Vec3().copy(dir).mulScalar(t).add(origin).sub(center);
10
+ const u = offset.dot(right) / halfWidth;
11
+ const v = offset.dot(up) / halfHeight;
12
+ if (u < -1 || u > 1 || v < -1 || v > 1) return null;
13
+ return {
14
+ u,
15
+ v
16
+ };
30
17
  };
31
-
32
18
  export { rayQuadHit };