@terraware/web-components 5.0.27-rc.0 → 5.0.27

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 +22 -19
  2. package/components/Autocomplete/Autocomplete.js +130 -106
  3. package/components/Badge/index.js +29 -28
  4. package/components/BusySpinner/index.js +45 -40
  5. package/components/Button/Button.js +47 -41
  6. package/components/Checkbox.js +63 -64
  7. package/components/Confirm/index.js +83 -74
  8. package/components/DatePicker/DatePicker.js +141 -138
  9. package/components/DialogBox/DialogBox.js +118 -111
  10. package/components/Divisor.js +18 -18
  11. package/components/Dropdown.js +94 -66
  12. package/components/EditableTable/index.js +271 -211
  13. package/components/ErrorBox/ErrorBox.js +49 -47
  14. package/components/FileChooser/index.js +135 -117
  15. package/components/FormBottomBar/index.js +84 -75
  16. package/components/Icon/Icon.js +20 -12
  17. package/components/Icon/icons/index.js +256 -256
  18. package/components/IconTooltip/index.js +60 -49
  19. package/components/Map/MapBox.js +452 -545
  20. package/components/Map/MapContainer.js +29 -25
  21. package/components/Map/MapDateSliderControl.js +64 -64
  22. package/components/Map/MapDrawer.js +47 -41
  23. package/components/Map/MapLegend.js +172 -176
  24. package/components/Map/MapViewStyleControl.js +63 -59
  25. package/components/Map/ReactMapboxWrapper.js +2 -3
  26. package/components/Map/index.js +180 -161
  27. package/components/Map/types.js +17 -12
  28. package/components/Map/useMaintainLayerOrder.js +26 -16
  29. package/components/Markdown/index.js +26 -25
  30. package/components/Message/Message.js +76 -73
  31. package/components/MultiSelect/index.js +124 -112
  32. package/components/Navbar/NavFooter.js +13 -9
  33. package/components/Navbar/NavItem.js +76 -70
  34. package/components/Navbar/NavSection.js +19 -17
  35. package/components/Navbar/Navbar.js +46 -39
  36. package/components/Navbar/SubNavbar.js +13 -9
  37. package/components/Note.js +22 -18
  38. package/components/OverlayModal/OverlayModal.js +63 -56
  39. package/components/PageForm/index.js +41 -33
  40. package/components/PhotoChooser/index.js +173 -165
  41. package/components/PhotosCarousel/PhotoSlide.js +35 -32
  42. package/components/PhotosCarousel/index.js +85 -80
  43. package/components/PhotosCarousel/usePhotosCarousel.js +76 -69
  44. package/components/Pill/index.js +45 -41
  45. package/components/PillList/index.js +24 -17
  46. package/components/PlacementWrapper/index.js +59 -38
  47. package/components/PopoverMenu/Popover.js +85 -68
  48. package/components/PopoverMenu/index.js +56 -51
  49. package/components/PopoverMultiSelect/index.js +59 -52
  50. package/components/ProgressCircle/ProgressCircle.js +32 -28
  51. package/components/RadioButton.js +19 -17
  52. package/components/Select/Select.js +15 -13
  53. package/components/Select/SelectT.js +250 -235
  54. package/components/Separator/index.js +17 -14
  55. package/components/Size.js +1 -0
  56. package/components/Slider/index.js +56 -43
  57. package/components/SummaryBox.js +72 -70
  58. package/components/Tabs/index.js +135 -133
  59. package/components/TextTruncated/index.js +130 -117
  60. package/components/Textfield/Textfield.js +190 -158
  61. package/components/Textfield/TruncatedTextArea.js +84 -70
  62. package/components/TimelineSlider/TimelineMarker.js +30 -25
  63. package/components/TimelineSlider/TimelineRail.js +11 -8
  64. package/components/TimelineSlider/index.js +66 -64
  65. package/components/TimelineSliderV2/ClusterBadge.js +29 -25
  66. package/components/TimelineSliderV2/TimelineDot.js +35 -31
  67. package/components/TimelineSliderV2/TimelineRail.js +11 -8
  68. package/components/TimelineSliderV2/index.js +103 -121
  69. package/components/TimelineSliderV2/layout.js +147 -124
  70. package/components/TimelineSliderV2/types.js +1 -0
  71. package/components/Tooltip/Tooltip.js +56 -47
  72. package/components/ViewPhotosDialog/index.js +35 -28
  73. package/components/VirtualWalkthrough/Annotation.js +207 -163
  74. package/components/VirtualWalkthrough/AnnotationEditPane.js +184 -198
  75. package/components/VirtualWalkthrough/AnnotationPanel.js +199 -187
  76. package/components/VirtualWalkthrough/Application.js +34 -31
  77. package/components/VirtualWalkthrough/AutoRotator.js +223 -85
  78. package/components/VirtualWalkthrough/BlockingSpinner.js +28 -26
  79. package/components/VirtualWalkthrough/BoundaryRing.js +34 -30
  80. package/components/VirtualWalkthrough/BoundaryWall.js +36 -32
  81. package/components/VirtualWalkthrough/CameraInfo.js +89 -100
  82. package/components/VirtualWalkthrough/ControlsInfoPane.js +195 -254
  83. package/components/VirtualWalkthrough/GradientSky.js +73 -72
  84. package/components/VirtualWalkthrough/SplatControls.js +221 -209
  85. package/components/VirtualWalkthrough/SplatCrop.js +20 -21
  86. package/components/VirtualWalkthrough/SplatFadeCrop.js +20 -21
  87. package/components/VirtualWalkthrough/SplatModel.js +85 -65
  88. package/components/VirtualWalkthrough/SplatRevealRain.js +46 -41
  89. package/components/VirtualWalkthrough/TfAnnotationManager.js +379 -197
  90. package/components/VirtualWalkthrough/TfXrNavigation.js +162 -73
  91. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +431 -438
  92. package/components/VirtualWalkthrough/VrAnnotationPanel.js +22 -18
  93. package/components/VirtualWalkthrough/XrAnnotationInteraction.js +17 -14
  94. package/components/VirtualWalkthrough/XrExitButton.js +17 -14
  95. package/components/VirtualWalkthrough/XrGazeDwell.js +17 -14
  96. package/components/VirtualWalkthrough/XrPointerRay.js +17 -14
  97. package/components/VirtualWalkthrough/XrStartPose.js +121 -67
  98. package/components/VirtualWalkthrough/boundary-ring.js +117 -87
  99. package/components/VirtualWalkthrough/boundary-wall.js +258 -175
  100. package/components/VirtualWalkthrough/camera-adoption.js +64 -18
  101. package/components/VirtualWalkthrough/groundPlane.js +47 -32
  102. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
  103. package/components/VirtualWalkthrough/splatFormat.js +47 -13
  104. package/components/VirtualWalkthrough/useAdoptedCamera.js +44 -23
  105. package/components/VirtualWalkthrough/useRestartOnVr.js +37 -26
  106. package/components/VirtualWalkthrough/useXrRenderTuning.js +54 -33
  107. package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +99 -67
  108. package/components/VirtualWalkthrough/vr-annotation-panel.js +433 -403
  109. package/components/VirtualWalkthrough/vr-annotation-scroll.js +28 -10
  110. package/components/VirtualWalkthrough/walkthrough-camera.js +329 -204
  111. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +80 -39
  112. package/components/VirtualWalkthrough/xr-annotation-candidates.js +18 -6
  113. package/components/VirtualWalkthrough/xr-annotation-interaction.js +56 -31
  114. package/components/VirtualWalkthrough/xr-annotation-targeting.js +38 -22
  115. package/components/VirtualWalkthrough/xr-button-arming.js +17 -7
  116. package/components/VirtualWalkthrough/xr-button-hold.js +36 -22
  117. package/components/VirtualWalkthrough/xr-exit-button-mask.js +36 -22
  118. package/components/VirtualWalkthrough/xr-exit-button.js +207 -148
  119. package/components/VirtualWalkthrough/xr-face-buttons.js +29 -19
  120. package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +34 -23
  121. package/components/VirtualWalkthrough/xr-gaze-dwell.js +144 -107
  122. package/components/VirtualWalkthrough/xr-interactive-ui.js +24 -12
  123. package/components/VirtualWalkthrough/xr-pointer-ray.js +20 -13
  124. package/components/VirtualWalkthrough/xr-progress-pie-material.js +88 -117
  125. package/components/VirtualWalkthrough/xr-progress-pie.js +10 -1
  126. package/components/VirtualWalkthrough/xr-ray-quad.js +30 -16
  127. package/components/VirtualWalkthrough/xr-render-tuning.js +48 -17
  128. package/components/VirtualWalkthrough/xr-scene-bounds.js +37 -21
  129. package/components/VirtualWalkthrough/xr-start-pose.js +73 -27
  130. package/components/VirtualWalkthrough/xr-teleport-gesture.js +42 -16
  131. package/components/svg/BlobbyGrayIconImage.js +39 -41
  132. package/components/svg/BlobbyGrayIconImport.js +39 -41
  133. package/components/svg/BlobbyGrayIconUploadToTheCloud.js +39 -41
  134. package/components/svg/BlobbyIconCloudChecked.js +164 -202
  135. package/components/svg/BlobbyIconExportOrUploadPhoto.js +164 -202
  136. package/components/svg/BlobbyIconFolder.js +164 -202
  137. package/components/svg/BlobbyIconGraphReport.js +164 -202
  138. package/components/svg/BlobbyIconHappy.js +164 -202
  139. package/components/svg/BlobbyIconHeartMonitor.js +164 -202
  140. package/components/svg/BlobbyIconLeaf.js +164 -202
  141. package/components/svg/BlobbyIconLibrary.js +164 -202
  142. package/components/svg/BlobbyIconNursery.js +166 -204
  143. package/components/svg/BlobbyIconOrganization.js +164 -202
  144. package/components/svg/BlobbyIconParchment.js +164 -202
  145. package/components/svg/BlobbyIconPeople.js +164 -202
  146. package/components/svg/BlobbyIconSeedBank.js +164 -202
  147. package/components/svg/BlobbyIconSeedling.js +164 -202
  148. package/components/svg/BlobbyIconSeeds.js +164 -202
  149. package/components/svg/BlobbyIconSite.js +167 -206
  150. package/components/svg/BlobbyIconSparkles.js +164 -202
  151. package/components/svg/BlobbyIconWrench.js +164 -202
  152. package/components/svg/Bug.js +21 -19
  153. package/components/svg/Calendar.js +21 -19
  154. package/components/svg/CaretDown.js +21 -19
  155. package/components/svg/CaretLeft.js +21 -19
  156. package/components/svg/CaretRight.js +21 -19
  157. package/components/svg/CaretUp.js +21 -19
  158. package/components/svg/ChevronDown.js +21 -19
  159. package/components/svg/ChevronUp.js +21 -19
  160. package/components/svg/Close.js +21 -19
  161. package/components/svg/Critical.js +21 -19
  162. package/components/svg/Edit.js +164 -202
  163. package/components/svg/Error.js +21 -19
  164. package/components/svg/Filter.js +21 -19
  165. package/components/svg/Folder.js +21 -19
  166. package/components/svg/Help.js +21 -19
  167. package/components/svg/Home.js +21 -19
  168. package/components/svg/IconAdd.js +21 -19
  169. package/components/svg/IconArrowRight.js +23 -21
  170. package/components/svg/IconBusinessNetwork.js +21 -19
  171. package/components/svg/IconCalendar.js +21 -19
  172. package/components/svg/IconCancel.js +21 -19
  173. package/components/svg/IconChargingBattery.js +21 -19
  174. package/components/svg/IconChecklist.js +23 -21
  175. package/components/svg/IconCheckmark.js +21 -19
  176. package/components/svg/IconCoinInHand.js +21 -19
  177. package/components/svg/IconColumns.js +21 -19
  178. package/components/svg/IconComment.js +22 -20
  179. package/components/svg/IconDashboard.js +21 -19
  180. package/components/svg/IconDataMigration.js +21 -19
  181. package/components/svg/IconDocument.js +23 -21
  182. package/components/svg/IconDownloadFromTheCloud.js +23 -21
  183. package/components/svg/IconEdit.js +23 -21
  184. package/components/svg/IconExpand.js +23 -21
  185. package/components/svg/IconExport.js +21 -19
  186. package/components/svg/IconExternalLink.js +21 -19
  187. package/components/svg/IconEye.js +21 -19
  188. package/components/svg/IconEyeOff.js +21 -19
  189. package/components/svg/IconFile.js +21 -19
  190. package/components/svg/IconFolder.js +21 -19
  191. package/components/svg/IconFullScreen.js +22 -20
  192. package/components/svg/IconFutures.js +21 -19
  193. package/components/svg/IconGraphReport.js +21 -19
  194. package/components/svg/IconGrid.js +21 -19
  195. package/components/svg/IconHeartMonitor.js +21 -19
  196. package/components/svg/IconHelp.js +23 -21
  197. package/components/svg/IconHistory.js +21 -19
  198. package/components/svg/IconImport.js +21 -19
  199. package/components/svg/IconIndex.js +21 -19
  200. package/components/svg/IconLayers.js +21 -19
  201. package/components/svg/IconLibrary.js +22 -20
  202. package/components/svg/IconList.js +21 -19
  203. package/components/svg/IconLivePlant.js +21 -19
  204. package/components/svg/IconLocations.js +21 -19
  205. package/components/svg/IconMail.js +21 -19
  206. package/components/svg/IconManager.js +21 -19
  207. package/components/svg/IconMap.js +21 -19
  208. package/components/svg/IconMarker.js +21 -19
  209. package/components/svg/IconMenu.js +21 -19
  210. package/components/svg/IconMenuHorizontal.js +21 -19
  211. package/components/svg/IconModule.js +21 -19
  212. package/components/svg/IconMyLocation.js +21 -19
  213. package/components/svg/IconNote.js +23 -21
  214. package/components/svg/IconNursery.js +23 -21
  215. package/components/svg/IconOrg.js +21 -19
  216. package/components/svg/IconParchment.js +21 -19
  217. package/components/svg/IconPhoto.js +21 -19
  218. package/components/svg/IconPlantsFilled.js +21 -19
  219. package/components/svg/IconRedo.js +21 -19
  220. package/components/svg/IconRestorationSite.js +21 -19
  221. package/components/svg/IconSeedBank.js +21 -19
  222. package/components/svg/IconSeedling.js +21 -19
  223. package/components/svg/IconSettings.js +21 -19
  224. package/components/svg/IconSlice.js +21 -19
  225. package/components/svg/IconSubmit.js +21 -19
  226. package/components/svg/IconSubtract.js +23 -21
  227. package/components/svg/IconSynced.js +21 -19
  228. package/components/svg/IconTrashCan.js +21 -19
  229. package/components/svg/IconTreasureMap.js +21 -19
  230. package/components/svg/IconUnavailable.js +21 -19
  231. package/components/svg/IconUndo.js +21 -19
  232. package/components/svg/IconVariable.js +21 -19
  233. package/components/svg/IconVideo.js +31 -30
  234. package/components/svg/IconWifi.js +21 -19
  235. package/components/svg/Info.js +21 -19
  236. package/components/svg/Key.js +21 -19
  237. package/components/svg/Leaf.js +21 -19
  238. package/components/svg/Lock.js +21 -19
  239. package/components/svg/Logo.js +43 -48
  240. package/components/svg/MenuVertical.js +21 -19
  241. package/components/svg/Notification.js +21 -19
  242. package/components/svg/Person.js +21 -19
  243. package/components/svg/Plus.js +21 -19
  244. package/components/svg/RestorationSite.js +21 -19
  245. package/components/svg/Search.js +21 -19
  246. package/components/svg/Seeds.js +21 -19
  247. package/components/svg/Site.js +21 -19
  248. package/components/svg/Sparkles.js +21 -19
  249. package/components/svg/Species.js +21 -19
  250. package/components/svg/Species2.js +164 -202
  251. package/components/svg/Spinner.js +48 -54
  252. package/components/svg/Star.js +21 -19
  253. package/components/svg/Success.js +21 -19
  254. package/components/svg/SuccessFilled.js +21 -19
  255. package/components/svg/TerrawareLogoDesktop.js +49 -55
  256. package/components/svg/TerrawareLogoMobile.js +26 -25
  257. package/components/svg/Touchscreen.js +21 -19
  258. package/components/svg/UploadCloud.js +164 -202
  259. package/components/svg/Warning.js +21 -19
  260. package/components/svg/WelcomeClipboard.js +133 -156
  261. package/components/svg/index.js +129 -129
  262. package/components/table/EnhancedTableToolbar.js +74 -68
  263. package/components/table/EnhancedTableToolbarV2.js +41 -31
  264. package/components/table/TableCellRenderer.js +231 -196
  265. package/components/table/TableHeader.js +73 -71
  266. package/components/table/TableHeaderItem.js +102 -97
  267. package/components/table/density.js +19 -18
  268. package/components/table/index.js +369 -350
  269. package/components/table/sort.js +52 -27
  270. package/components/table/types.js +1 -0
  271. package/components/types/index.js +1 -0
  272. package/hooks/cameraEntityContext.js +13 -2
  273. package/hooks/useBoolean.js +13 -17
  274. package/hooks/useCameraPosition.js +56 -56
  275. package/hooks/useDevicePerformance.js +17 -17
  276. package/hooks/useXr.js +87 -85
  277. package/index.js +58 -58
  278. package/package.json +1 -1
  279. package/setupTests.js +4 -0
  280. package/strings/export.js +170 -111
  281. package/strings/index.js +54 -25
  282. package/strings/locales.js +41 -21
  283. package/strings/strings-en.js +51 -49
  284. package/strings/strings-es.js +51 -49
  285. package/strings/strings-fr.js +51 -49
  286. package/strings/strings-gx.js +51 -49
  287. package/style-dictionary-dist/TerrawareTheme.js +186 -186
  288. package/theme.js +76 -74
  289. package/utils/color.js +15 -8
  290. package/utils/date.js +108 -42
  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 +79 -42
  295. package/virtualWalkthrough.js +23 -23
@@ -1,6 +1,7 @@
1
- import { Vec3 } from "playcanvas";
2
- import { GSplatShaderEffect } from "playcanvas/scripts/esm/gsplat/gsplat-shader-effect.mjs";
3
- const shaderGLSL = `
1
+ import { Vec3 } from 'playcanvas';
2
+ import { GSplatShaderEffect } from 'playcanvas/scripts/esm/gsplat/gsplat-shader-effect.mjs';
3
+
4
+ const shaderGLSL = /* glsl */`
4
5
  uniform vec3 uAabbMin;
5
6
  uniform vec3 uAabbMax;
6
7
  uniform float uFadeDistance;
@@ -43,7 +44,7 @@ void modifySplatColor(vec3 center, inout vec4 color) {
43
44
  // No color modification needed
44
45
  }
45
46
  `;
46
- const shaderWGSL = `
47
+ const shaderWGSL = /* wgsl */`
47
48
  uniform uAabbMin: vec3f;
48
49
  uniform uAabbMax: vec3f;
49
50
  uniform uFadeDistance: f32;
@@ -86,37 +87,36 @@ fn modifySplatColor(center: vec3f, color: ptr<function, vec4f>) {
86
87
  // No color modification needed
87
88
  }
88
89
  `;
90
+
91
+ /**
92
+ * Fade crop shader effect for gaussian splats.
93
+ * Applies a smooth fade to splats outside the AABB based on distance.
94
+ * Splats at the AABB edge are fully visible, and fade to invisible at fadeDistance away.
95
+ */
89
96
  class GsplatFadeCropShaderEffect extends GSplatShaderEffect {
90
- static scriptName = 'gsplatFadeCropShaderEffect';
91
- _aabbMinArray = [
92
- 0,
93
- 0,
94
- 0
95
- ];
96
- _aabbMaxArray = [
97
- 0,
98
- 0,
99
- 0
100
- ];
101
- aabbMin = new Vec3(-0.5, -0.5, -0.5);
102
- aabbMax = new Vec3(0.5, 0.5, 0.5);
103
- fadeDistance = 0.5;
104
- getShaderGLSL() {
105
- return shaderGLSL;
106
- }
107
- getShaderWGSL() {
108
- return shaderWGSL;
109
- }
110
- updateEffect() {
111
- this._aabbMinArray[0] = this.aabbMin.x;
112
- this._aabbMinArray[1] = this.aabbMin.y;
113
- this._aabbMinArray[2] = this.aabbMin.z;
114
- this.setUniform('uAabbMin', this._aabbMinArray);
115
- this._aabbMaxArray[0] = this.aabbMax.x;
116
- this._aabbMaxArray[1] = this.aabbMax.y;
117
- this._aabbMaxArray[2] = this.aabbMax.z;
118
- this.setUniform('uAabbMax', this._aabbMaxArray);
119
- this.setUniform('uFadeDistance', this.fadeDistance);
120
- }
97
+ static scriptName = 'gsplatFadeCropShaderEffect';
98
+ _aabbMinArray = [0, 0, 0];
99
+ _aabbMaxArray = [0, 0, 0];
100
+ aabbMin = new Vec3(-0.5, -0.5, -0.5);
101
+ aabbMax = new Vec3(0.5, 0.5, 0.5);
102
+ fadeDistance = 0.5;
103
+ getShaderGLSL() {
104
+ return shaderGLSL;
105
+ }
106
+ getShaderWGSL() {
107
+ return shaderWGSL;
108
+ }
109
+ updateEffect() {
110
+ this._aabbMinArray[0] = this.aabbMin.x;
111
+ this._aabbMinArray[1] = this.aabbMin.y;
112
+ this._aabbMinArray[2] = this.aabbMin.z;
113
+ this.setUniform('uAabbMin', this._aabbMinArray);
114
+ this._aabbMaxArray[0] = this.aabbMax.x;
115
+ this._aabbMaxArray[1] = this.aabbMax.y;
116
+ this._aabbMaxArray[2] = this.aabbMax.z;
117
+ this.setUniform('uAabbMax', this._aabbMaxArray);
118
+ this.setUniform('uFadeDistance', this.fadeDistance);
119
+ }
121
120
  }
121
+
122
122
  export { GsplatFadeCropShaderEffect };
@@ -1,18 +1,52 @@
1
+ /*
2
+ * The playcanvas gsplat loader picks its parser from the asset's `filename`, not from the URL it
3
+ * actually downloads, because a splat is frequently served from a signed or extensionless URL. Each
4
+ * supported format therefore maps to a canonical filename whose extension/basename routes the asset
5
+ * to the matching engine parser:
6
+ *
7
+ * model.sog -> SogBundleParser sog packed into a single zip archive
8
+ * meta.json -> SogParser the same sog data left unpacked as separate files
9
+ * lod-meta.json -> GSplatOctreeParser sog with LOD levels, streamed in as the camera moves
10
+ * model.ply -> PlyParser
11
+ *
12
+ * `sog` and `sogUnbundled` are two packagings of one format, not two formats: both parsers build a
13
+ * GSplatSogData/GSplatSogResource and both await every texture before the resource exists. Only
14
+ * `sogStreamed` streams — playcanvas.d.ts on GSplatOctreeResource#numSplats: "Not all of these are
15
+ * resident at runtime: the LOD streaming system selects a subset per node based on view distance and
16
+ * the configured splat budget."
17
+ *
18
+ * Only the filename is faked; the textures an unbundled or streamed sog references are still
19
+ * resolved relative to the real URL, so no rewriting of splatSrc is needed.
20
+ */
1
21
  const LOADER_FILENAMES = {
2
- sog: 'model.sog',
3
- sogUnbundled: 'meta.json',
4
- sogStreamed: 'lod-meta.json',
5
- ply: 'model.ply'
22
+ sog: 'model.sog',
23
+ sogUnbundled: 'meta.json',
24
+ sogStreamed: 'lod-meta.json',
25
+ ply: 'model.ply'
6
26
  };
27
+ /**
28
+ * Format assumed when a URL is too opaque to tell us anything. Kept as the bundled `.sog` archive so
29
+ * that existing extensionless splat URLs keep loading the way they did before the other formats were
30
+ * supported.
31
+ */
7
32
  const DEFAULT_SPLAT_FORMAT = 'sog';
8
- const splatLoaderFilename = (format)=>LOADER_FILENAMES[format];
9
- const detectSplatFormat = (splatSrc)=>{
10
- const path = splatSrc.split(/[?#]/)[0];
11
- const basename = path.slice(path.lastIndexOf('/') + 1).toLowerCase();
12
- if ('lod-meta.json' === basename) return 'sogStreamed';
13
- if ('meta.json' === basename) return 'sogUnbundled';
14
- if (basename.endsWith('.ply')) return 'ply';
15
- if (basename.endsWith('.sog')) return 'sog';
16
- return DEFAULT_SPLAT_FORMAT;
33
+ const splatLoaderFilename = format => LOADER_FILENAMES[format];
34
+ const detectSplatFormat = splatSrc => {
35
+ const path = splatSrc.split(/[?#]/)[0];
36
+ const basename = path.slice(path.lastIndexOf('/') + 1).toLowerCase();
37
+ if (basename === 'lod-meta.json') {
38
+ return 'sogStreamed';
39
+ }
40
+ if (basename === 'meta.json') {
41
+ return 'sogUnbundled';
42
+ }
43
+ if (basename.endsWith('.ply')) {
44
+ return 'ply';
45
+ }
46
+ if (basename.endsWith('.sog')) {
47
+ return 'sog';
48
+ }
49
+ return DEFAULT_SPLAT_FORMAT;
17
50
  };
51
+
18
52
  export { DEFAULT_SPLAT_FORMAT, detectSplatFormat, splatLoaderFilename };
@@ -1,24 +1,45 @@
1
- import { useLayoutEffect, useState } from "react";
2
- import { useApp } from "@playcanvas/react/hooks";
3
- import { adoptSceneCamera } from "./camera-adoption.js";
4
- const useAdoptedCamera = (rig, ownCamera)=>{
5
- const app = useApp();
6
- const [adopted, setAdopted] = useState(null);
7
- useLayoutEffect(()=>{
8
- if (!rig || !ownCamera) return;
9
- const adoption = adoptSceneCamera(app.root, rig, ownCamera);
10
- setAdopted(adoption?.camera ?? null);
11
- return ()=>{
12
- adoption?.release();
13
- setAdopted(null);
14
- };
15
- }, [
16
- app,
17
- rig,
18
- ownCamera
19
- ]);
20
- return adopted;
1
+ import { useState, useLayoutEffect } from 'react';
2
+ import { useApp } from '@playcanvas/react/hooks';
3
+ import { adoptSceneCamera } from './camera-adoption.js';
4
+
5
+ /**
6
+ * Keeps the walkthrough's rig holding a camera for as long as the viewer is mounted, adopting the
7
+ * scene's own when the viewer wasn't given one. See {@link adoptSceneCamera} for why.
8
+ *
9
+ * @param rig - The `camera-root` entity, or null before it exists.
10
+ * @param ownCamera - The entity the viewer mounts its own camera on, or null before it exists.
11
+ * @returns The adopted camera, or null when the viewer brought one of its own. Held as state so the
12
+ * viewer re-renders and re-points everything that addresses the camera at the entity now carrying
13
+ * it.
14
+ */
15
+ const useAdoptedCamera = (rig, ownCamera) => {
16
+ const app = useApp();
17
+ const [adopted, setAdopted] = useState(null);
18
+
19
+ // A layout effect, so that the camera is handed back before the rig it was parented into is
20
+ // destroyed. `@playcanvas/react`'s Entity destroys its entity in a layout effect's teardown, and
21
+ // destroying an entity destroys its children — so a release deferred to a passive effect runs
22
+ // after the host's camera has already been destroyed along with the walkthrough, leaving the host
23
+ // scene with no camera to render through at all.
24
+ //
25
+ // The rig is a child of the viewer, and React tears an unmounting tree's layout effects down from
26
+ // the top, so this runs first.
27
+ useLayoutEffect(() => {
28
+ if (!rig || !ownCamera) {
29
+ return;
30
+ }
31
+
32
+ // Both entities are state set from refs, so this runs a commit after the child that mounts the
33
+ // camera component: a viewer that was given a camera is recognised as owning one rather than
34
+ // adopted on top of.
35
+ const adoption = adoptSceneCamera(app.root, rig, ownCamera);
36
+ setAdopted(adoption?.camera ?? null);
37
+ return () => {
38
+ adoption?.release();
39
+ setAdopted(null);
40
+ };
41
+ }, [app, rig, ownCamera]);
42
+ return adopted;
21
43
  };
22
- const VirtualWalkthrough_useAdoptedCamera = useAdoptedCamera;
23
- export default VirtualWalkthrough_useAdoptedCamera;
24
- export { useAdoptedCamera };
44
+
45
+ export { useAdoptedCamera as default, useAdoptedCamera };
@@ -1,27 +1,38 @@
1
- import { useEffect } from "react";
2
- import { useApp } from "@playcanvas/react/hooks";
3
- import { XRTYPE_VR } from "playcanvas";
4
- const useRestartOnVr = (scriptRef, enabled)=>{
5
- const app = useApp();
6
- useEffect(()=>{
7
- const xr = app?.xr;
8
- if (!xr || !enabled) return;
9
- const restart = ()=>{
10
- const script = scriptRef.current;
11
- if (!script || xr.type !== XRTYPE_VR) return;
12
- script.enabled = false;
13
- script.enabled = true;
14
- };
15
- xr.on('start', restart);
16
- return ()=>{
17
- xr.off('start', restart);
18
- };
19
- }, [
20
- app,
21
- enabled,
22
- scriptRef
23
- ]);
1
+ import { useEffect } from 'react';
2
+ import { useApp } from '@playcanvas/react/hooks';
3
+ import { XRTYPE_VR } from 'playcanvas';
4
+
5
+ /**
6
+ * Replays a one-shot script effect at the start of every VR session.
7
+ *
8
+ * `GSplatShaderEffect` zeroes its timeline on the script's `enable` event and switches itself off
9
+ * once the animation has played out, so re-enabling a finished effect runs it again. The `enabled`
10
+ * setter only fires the event on a change of state, which is why an effect still mid-play is taken
11
+ * back through disabled first.
12
+ *
13
+ * @param scriptRef - The script instance to replay, as handed back by `Script`'s ref.
14
+ * @param enabled - Whether the effect is in use at all. No session is watched while false.
15
+ */
16
+ const useRestartOnVr = (scriptRef, enabled) => {
17
+ const app = useApp();
18
+ useEffect(() => {
19
+ const xr = app?.xr;
20
+ if (!xr || !enabled) {
21
+ return;
22
+ }
23
+ const restart = () => {
24
+ const script = scriptRef.current;
25
+ if (!script || xr.type !== XRTYPE_VR) {
26
+ return;
27
+ }
28
+ script.enabled = false;
29
+ script.enabled = true;
30
+ };
31
+ xr.on('start', restart);
32
+ return () => {
33
+ xr.off('start', restart);
34
+ };
35
+ }, [app, enabled, scriptRef]);
24
36
  };
25
- const VirtualWalkthrough_useRestartOnVr = useRestartOnVr;
26
- export default VirtualWalkthrough_useRestartOnVr;
27
- export { useRestartOnVr };
37
+
38
+ export { useRestartOnVr as default, useRestartOnVr };
@@ -1,34 +1,55 @@
1
- import { useEffect } from "react";
2
- import { useApp } from "@playcanvas/react/hooks";
3
- import { XR_FIXED_FOVEATION, applyGsplatTuning, restoreGsplatTuning } from "./xr-render-tuning.js";
4
- const useXrRenderTuning = ()=>{
5
- const app = useApp();
6
- useEffect(()=>{
7
- const xr = app.xr;
8
- if (!xr) return;
9
- let previous = null;
10
- const restore = ()=>{
11
- if (previous) {
12
- restoreGsplatTuning(app.scene.gsplat, previous);
13
- previous = null;
14
- }
15
- };
16
- const handleStart = ()=>{
17
- if (!previous) previous = applyGsplatTuning(app.scene.gsplat);
18
- xr.fixedFoveation = XR_FIXED_FOVEATION;
19
- };
20
- xr.on('start', handleStart);
21
- xr.on('end', restore);
22
- if (xr.active) handleStart();
23
- return ()=>{
24
- xr.off('start', handleStart);
25
- xr.off('end', restore);
26
- restore();
27
- };
28
- }, [
29
- app
30
- ]);
1
+ import { useEffect } from 'react';
2
+ import { useApp } from '@playcanvas/react/hooks';
3
+ import { applyGsplatTuning, XR_FIXED_FOVEATION, restoreGsplatTuning } from './xr-render-tuning.js';
4
+
5
+ /**
6
+ * Holds the XR render settings in place for the duration of a session, including one that is
7
+ * already running when this mounts.
8
+ *
9
+ * Call this once per application. The gsplat settings it writes are scene-global, so a second
10
+ * caller would capture the first caller's tuned values as the state to restore, and the scene would
11
+ * come out of the session still tuned.
12
+ */
13
+ const useXrRenderTuning = () => {
14
+ const app = useApp();
15
+ useEffect(() => {
16
+ const xr = app.xr;
17
+ if (!xr) {
18
+ return;
19
+ }
20
+ let previous = null;
21
+ const restore = () => {
22
+ if (previous) {
23
+ restoreGsplatTuning(app.scene.gsplat, previous);
24
+ previous = null;
25
+ }
26
+ };
27
+ const handleStart = () => {
28
+ // Guards against capturing already-tuned values as the restore target if `start` ever
29
+ // fires again before a matching `end`.
30
+ if (!previous) {
31
+ previous = applyGsplatTuning(app.scene.gsplat);
32
+ }
33
+ // Only settable on an active session, so it cannot go in the start options alongside
34
+ // framebufferScaleFactor. A no-op when the presentation layer doesn't support it.
35
+ xr.fixedFoveation = XR_FIXED_FOVEATION;
36
+ };
37
+ xr.on('start', handleStart);
38
+ xr.on('end', restore);
39
+
40
+ // A viewer can be mounted into a scene that is already in a headset, in which case `start`
41
+ // fired before this hook existed and waiting for it would leave the whole session rendering
42
+ // with the desktop settings. `handleStart` is idempotent, so a session that goes on to fire
43
+ // `start` anyway does not capture the tuned values as the ones to restore.
44
+ if (xr.active) {
45
+ handleStart();
46
+ }
47
+ return () => {
48
+ xr.off('start', handleStart);
49
+ xr.off('end', restore);
50
+ restore();
51
+ };
52
+ }, [app]);
31
53
  };
32
- const VirtualWalkthrough_useXrRenderTuning = useXrRenderTuning;
33
- export default VirtualWalkthrough_useXrRenderTuning;
34
- export { useXrRenderTuning };
54
+
55
+ export { useXrRenderTuning as default, useXrRenderTuning };
@@ -1,20 +1,29 @@
1
- const wrapText = (text, maxWidth, measure)=>{
2
- const words = text.split(/\s+/).filter(Boolean);
3
- const lines = [];
4
- let current = '';
5
- words.forEach((word)=>{
6
- const candidate = current ? `${current} ${word}` : word;
7
- if (!current || measure(candidate) <= maxWidth) current = candidate;
8
- else {
9
- lines.push(current);
10
- current = word;
11
- }
12
- });
13
- if (current) lines.push(current);
14
- return lines;
1
+ /**
2
+ * Greedily wraps `text` into lines no wider than `maxWidth`, using `measure` to size
3
+ * each candidate line. A single word wider than `maxWidth` still gets its own line.
4
+ */
5
+ const wrapText = (text, maxWidth, measure) => {
6
+ const words = text.split(/\s+/).filter(Boolean);
7
+ const lines = [];
8
+ let current = '';
9
+ words.forEach(word => {
10
+ const candidate = current ? `${current} ${word}` : word;
11
+ if (!current || measure(candidate) <= maxWidth) {
12
+ current = candidate;
13
+ } else {
14
+ lines.push(current);
15
+ current = word;
16
+ }
17
+ });
18
+ if (current) {
19
+ lines.push(current);
20
+ }
21
+ return lines;
15
22
  };
16
23
  const PANEL_CANVAS_WIDTH = 1024;
24
+ /** Tallest the panel can grow; also the canvas the content is painted into. */
17
25
  const PANEL_MAX_HEIGHT = 768;
26
+ /** Shortest the panel can shrink, so a bare title still reads as a panel rather than a sliver. */
18
27
  const PANEL_MIN_HEIGHT = 200;
19
28
  const PANEL_PAD_X = 48;
20
29
  const PANEL_PAD_Y = 40;
@@ -28,63 +37,86 @@ const CHIP_GAP = 24;
28
37
  const TITLE_LINE_HEIGHT = 56;
29
38
  const TITLE_GAP = 16;
30
39
  const BODY_LINE_HEIGHT = 40;
40
+
41
+ /**
42
+ * Tallest canvas the body text is rasterised into. The body scrolls as a window onto this canvas,
43
+ * so it bounds how much text an annotation can show — far more than the panel itself holds.
44
+ */
31
45
  const TEXT_CANVAS_MAX_HEIGHT = 2048;
32
46
  const PANEL_FONTS = {
33
- label: '600 28px sans-serif',
34
- title: '700 48px sans-serif',
35
- body: '400 32px sans-serif'
47
+ label: '600 28px sans-serif',
48
+ title: '700 48px sans-serif',
49
+ body: '400 32px sans-serif'
36
50
  };
37
- const fitLines = (lines, y, lineHeight)=>{
38
- const room = Math.max(0, Math.floor((PANEL_MAX_HEIGHT - PANEL_PAD_Y - y) / lineHeight));
39
- return lines.slice(0, room);
51
+ /** Keeps the lines whose full line box fits above the bottom padding. */
52
+ const fitLines = (lines, y, lineHeight) => {
53
+ const room = Math.max(0, Math.floor((PANEL_MAX_HEIGHT - PANEL_PAD_Y - y) / lineHeight));
54
+ return lines.slice(0, room);
40
55
  };
41
- const layoutPanel = ({ title, label, bodyText, imageCount, contentWidth, measure })=>{
42
- let y = PANEL_PAD_Y;
43
- const image = imageCount > 0 ? {
44
- y,
45
- height: IMAGE_BAND_HEIGHT
46
- } : null;
47
- let dotsY = null;
48
- if (image) {
49
- y += IMAGE_BAND_HEIGHT + IMAGE_GAP;
50
- if (imageCount > 1) {
51
- dotsY = y + DOTS_CENTER_OFFSET;
52
- y += DOTS_BLOCK_HEIGHT;
53
- }
54
- y += IMAGE_GAP;
56
+
57
+ /**
58
+ * Lays the panel's blocks out top-to-bottom and reports the height they need, so the quad
59
+ * can be sized to the content instead of a fixed aspect. Everything above the body is a header
60
+ * that has to fit within `PANEL_MAX_HEIGHT`; the body instead scrolls within whatever height
61
+ * the header leaves it.
62
+ */
63
+ const layoutPanel = ({
64
+ title,
65
+ label,
66
+ bodyText,
67
+ imageCount,
68
+ contentWidth,
69
+ measure
70
+ }) => {
71
+ let y = PANEL_PAD_Y;
72
+ const image = imageCount > 0 ? {
73
+ y,
74
+ height: IMAGE_BAND_HEIGHT
75
+ } : null;
76
+ let dotsY = null;
77
+ if (image) {
78
+ y += IMAGE_BAND_HEIGHT + IMAGE_GAP;
79
+ if (imageCount > 1) {
80
+ dotsY = y + DOTS_CENTER_OFFSET;
81
+ y += DOTS_BLOCK_HEIGHT;
82
+ }
83
+ y += IMAGE_GAP;
84
+ }
85
+ const chip = label ? {
86
+ y,
87
+ width: measure(label, 'label') + CHIP_PAD_X * 2,
88
+ height: CHIP_HEIGHT
89
+ } : null;
90
+ if (chip) {
91
+ y += CHIP_HEIGHT + CHIP_GAP;
92
+ }
93
+ const titleLines = wrapText(title, contentWidth, segment => measure(segment, 'title'));
94
+ const fittedTitle = fitLines(titleLines, y, TITLE_LINE_HEIGHT);
95
+ const titleY = y;
96
+ y += fittedTitle.length * TITLE_LINE_HEIGHT + TITLE_GAP;
97
+ const bodyLines = bodyText ? wrapText(bodyText, contentWidth, segment => measure(segment, 'body')) : [];
98
+ const fittedBody = bodyLines.slice(0, Math.floor(TEXT_CANVAS_MAX_HEIGHT / BODY_LINE_HEIGHT));
99
+ const bodyY = y;
100
+ const contentHeight = fittedBody.length * BODY_LINE_HEIGHT;
101
+ const viewportHeight = Math.min(contentHeight, Math.max(0, PANEL_MAX_HEIGHT - PANEL_PAD_Y - bodyY));
102
+ const height = Math.max(PANEL_MIN_HEIGHT, Math.min(PANEL_MAX_HEIGHT, bodyY + viewportHeight + PANEL_PAD_Y));
103
+ return {
104
+ height,
105
+ image,
106
+ dotsY,
107
+ chip,
108
+ title: {
109
+ y: titleY,
110
+ lines: fittedTitle
111
+ },
112
+ body: {
113
+ y: bodyY,
114
+ lines: fittedBody,
115
+ contentHeight,
116
+ viewportHeight,
117
+ scrollable: viewportHeight > 0 && contentHeight > viewportHeight
55
118
  }
56
- const chip = label ? {
57
- y,
58
- width: measure(label, 'label') + 2 * CHIP_PAD_X,
59
- height: CHIP_HEIGHT
60
- } : null;
61
- if (chip) y += CHIP_HEIGHT + CHIP_GAP;
62
- const titleLines = wrapText(title, contentWidth, (segment)=>measure(segment, 'title'));
63
- const fittedTitle = fitLines(titleLines, y, TITLE_LINE_HEIGHT);
64
- const titleY = y;
65
- y += fittedTitle.length * TITLE_LINE_HEIGHT + TITLE_GAP;
66
- const bodyLines = bodyText ? wrapText(bodyText, contentWidth, (segment)=>measure(segment, 'body')) : [];
67
- const fittedBody = bodyLines.slice(0, Math.floor(TEXT_CANVAS_MAX_HEIGHT / BODY_LINE_HEIGHT));
68
- const bodyY = y;
69
- const contentHeight = fittedBody.length * BODY_LINE_HEIGHT;
70
- const viewportHeight = Math.min(contentHeight, Math.max(0, PANEL_MAX_HEIGHT - PANEL_PAD_Y - bodyY));
71
- const height = Math.max(PANEL_MIN_HEIGHT, Math.min(PANEL_MAX_HEIGHT, bodyY + viewportHeight + PANEL_PAD_Y));
72
- return {
73
- height,
74
- image,
75
- dotsY,
76
- chip,
77
- title: {
78
- y: titleY,
79
- lines: fittedTitle
80
- },
81
- body: {
82
- y: bodyY,
83
- lines: fittedBody,
84
- contentHeight,
85
- viewportHeight,
86
- scrollable: viewportHeight > 0 && contentHeight > viewportHeight
87
- }
88
- };
119
+ };
89
120
  };
121
+
90
122
  export { BODY_LINE_HEIGHT, CHIP_HEIGHT, IMAGE_BAND_HEIGHT, PANEL_CANVAS_WIDTH, PANEL_FONTS, PANEL_MAX_HEIGHT, PANEL_MIN_HEIGHT, PANEL_PAD_X, PANEL_PAD_Y, TEXT_CANVAS_MAX_HEIGHT, TITLE_LINE_HEIGHT, layoutPanel, wrapText };