@terraware/web-components 5.0.27 → 5.1.1-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 (311) 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/Stepper/index.d.ts +33 -0
  58. package/components/Stepper/index.d.ts.map +1 -0
  59. package/components/Stepper/index.js +66 -0
  60. package/components/SummaryBox.js +70 -72
  61. package/components/Tabs/index.js +133 -135
  62. package/components/TextTruncated/index.js +117 -130
  63. package/components/Textfield/Textfield.js +158 -190
  64. package/components/Textfield/TruncatedTextArea.js +70 -84
  65. package/components/TimelineSlider/TimelineMarker.js +25 -30
  66. package/components/TimelineSlider/TimelineRail.js +8 -11
  67. package/components/TimelineSlider/index.js +64 -66
  68. package/components/TimelineSliderV2/ClusterBadge.js +25 -29
  69. package/components/TimelineSliderV2/TimelineDot.js +31 -35
  70. package/components/TimelineSliderV2/TimelineRail.js +8 -11
  71. package/components/TimelineSliderV2/index.js +121 -103
  72. package/components/TimelineSliderV2/layout.js +124 -147
  73. package/components/TimelineSliderV2/types.js +0 -1
  74. package/components/Tooltip/Tooltip.js +47 -56
  75. package/components/ViewPhotosDialog/index.js +28 -35
  76. package/components/VirtualWalkthrough/Annotation.js +163 -207
  77. package/components/VirtualWalkthrough/AnnotationEditPane.js +198 -184
  78. package/components/VirtualWalkthrough/AnnotationPanel.js +187 -199
  79. package/components/VirtualWalkthrough/Application.js +31 -34
  80. package/components/VirtualWalkthrough/AutoRotator.js +85 -223
  81. package/components/VirtualWalkthrough/BlockingSpinner.js +26 -28
  82. package/components/VirtualWalkthrough/BoundaryRing.js +30 -34
  83. package/components/VirtualWalkthrough/BoundaryWall.js +32 -36
  84. package/components/VirtualWalkthrough/CameraInfo.js +100 -89
  85. package/components/VirtualWalkthrough/ControlsInfoPane.js +254 -195
  86. package/components/VirtualWalkthrough/GradientSky.js +72 -73
  87. package/components/VirtualWalkthrough/SplatControls.js +209 -221
  88. package/components/VirtualWalkthrough/SplatCrop.js +21 -20
  89. package/components/VirtualWalkthrough/SplatFadeCrop.js +21 -20
  90. package/components/VirtualWalkthrough/SplatModel.js +65 -85
  91. package/components/VirtualWalkthrough/SplatRevealRain.js +41 -46
  92. package/components/VirtualWalkthrough/TfAnnotationManager.js +197 -379
  93. package/components/VirtualWalkthrough/TfXrNavigation.js +73 -162
  94. package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +438 -431
  95. package/components/VirtualWalkthrough/VrAnnotationPanel.js +18 -22
  96. package/components/VirtualWalkthrough/XrAnnotationInteraction.js +14 -17
  97. package/components/VirtualWalkthrough/XrExitButton.js +14 -17
  98. package/components/VirtualWalkthrough/XrGazeDwell.js +14 -17
  99. package/components/VirtualWalkthrough/XrPointerRay.js +14 -17
  100. package/components/VirtualWalkthrough/XrStartPose.js +67 -121
  101. package/components/VirtualWalkthrough/boundary-ring.js +87 -117
  102. package/components/VirtualWalkthrough/boundary-wall.js +175 -258
  103. package/components/VirtualWalkthrough/camera-adoption.js +18 -64
  104. package/components/VirtualWalkthrough/groundPlane.js +32 -47
  105. package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
  106. package/components/VirtualWalkthrough/splatFormat.js +13 -47
  107. package/components/VirtualWalkthrough/useAdoptedCamera.js +23 -44
  108. package/components/VirtualWalkthrough/useRestartOnVr.js +26 -37
  109. package/components/VirtualWalkthrough/useXrRenderTuning.js +33 -54
  110. package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +67 -99
  111. package/components/VirtualWalkthrough/vr-annotation-panel.js +403 -433
  112. package/components/VirtualWalkthrough/vr-annotation-scroll.js +10 -28
  113. package/components/VirtualWalkthrough/walkthrough-camera.js +204 -329
  114. package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +39 -80
  115. package/components/VirtualWalkthrough/xr-annotation-candidates.js +6 -18
  116. package/components/VirtualWalkthrough/xr-annotation-interaction.js +31 -56
  117. package/components/VirtualWalkthrough/xr-annotation-targeting.js +22 -38
  118. package/components/VirtualWalkthrough/xr-button-arming.js +7 -17
  119. package/components/VirtualWalkthrough/xr-button-hold.js +22 -36
  120. package/components/VirtualWalkthrough/xr-exit-button-mask.js +22 -36
  121. package/components/VirtualWalkthrough/xr-exit-button.js +148 -207
  122. package/components/VirtualWalkthrough/xr-face-buttons.js +19 -29
  123. package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +23 -34
  124. package/components/VirtualWalkthrough/xr-gaze-dwell.js +107 -144
  125. package/components/VirtualWalkthrough/xr-interactive-ui.js +12 -24
  126. package/components/VirtualWalkthrough/xr-pointer-ray.js +13 -20
  127. package/components/VirtualWalkthrough/xr-progress-pie-material.js +117 -88
  128. package/components/VirtualWalkthrough/xr-progress-pie.js +1 -10
  129. package/components/VirtualWalkthrough/xr-ray-quad.js +16 -30
  130. package/components/VirtualWalkthrough/xr-render-tuning.js +17 -48
  131. package/components/VirtualWalkthrough/xr-scene-bounds.js +21 -37
  132. package/components/VirtualWalkthrough/xr-start-pose.js +27 -73
  133. package/components/VirtualWalkthrough/xr-teleport-gesture.js +16 -42
  134. package/components/svg/BlobbyGrayIconImage.js +41 -39
  135. package/components/svg/BlobbyGrayIconImport.js +41 -39
  136. package/components/svg/BlobbyGrayIconUploadToTheCloud.js +41 -39
  137. package/components/svg/BlobbyIconCloudChecked.js +202 -164
  138. package/components/svg/BlobbyIconExportOrUploadPhoto.js +202 -164
  139. package/components/svg/BlobbyIconFolder.js +202 -164
  140. package/components/svg/BlobbyIconGraphReport.js +202 -164
  141. package/components/svg/BlobbyIconHappy.js +202 -164
  142. package/components/svg/BlobbyIconHeartMonitor.js +202 -164
  143. package/components/svg/BlobbyIconLeaf.js +202 -164
  144. package/components/svg/BlobbyIconLibrary.js +202 -164
  145. package/components/svg/BlobbyIconNursery.js +204 -166
  146. package/components/svg/BlobbyIconOrganization.js +202 -164
  147. package/components/svg/BlobbyIconParchment.js +202 -164
  148. package/components/svg/BlobbyIconPeople.js +202 -164
  149. package/components/svg/BlobbyIconSeedBank.js +202 -164
  150. package/components/svg/BlobbyIconSeedling.js +202 -164
  151. package/components/svg/BlobbyIconSeeds.js +202 -164
  152. package/components/svg/BlobbyIconSite.js +206 -167
  153. package/components/svg/BlobbyIconSparkles.js +202 -164
  154. package/components/svg/BlobbyIconWrench.js +202 -164
  155. package/components/svg/Bug.js +19 -21
  156. package/components/svg/Calendar.js +19 -21
  157. package/components/svg/CaretDown.js +19 -21
  158. package/components/svg/CaretLeft.js +19 -21
  159. package/components/svg/CaretRight.js +19 -21
  160. package/components/svg/CaretUp.js +19 -21
  161. package/components/svg/ChevronDown.js +19 -21
  162. package/components/svg/ChevronUp.js +19 -21
  163. package/components/svg/Close.js +19 -21
  164. package/components/svg/Critical.js +19 -21
  165. package/components/svg/Edit.js +202 -164
  166. package/components/svg/Error.js +19 -21
  167. package/components/svg/Filter.js +19 -21
  168. package/components/svg/Folder.js +19 -21
  169. package/components/svg/Help.js +19 -21
  170. package/components/svg/Home.js +19 -21
  171. package/components/svg/IconAdd.js +19 -21
  172. package/components/svg/IconArrowRight.js +21 -23
  173. package/components/svg/IconBusinessNetwork.js +19 -21
  174. package/components/svg/IconCalendar.js +19 -21
  175. package/components/svg/IconCancel.js +19 -21
  176. package/components/svg/IconChargingBattery.js +19 -21
  177. package/components/svg/IconChecklist.js +21 -23
  178. package/components/svg/IconCheckmark.js +19 -21
  179. package/components/svg/IconCoinInHand.js +19 -21
  180. package/components/svg/IconColumns.js +19 -21
  181. package/components/svg/IconComment.js +20 -22
  182. package/components/svg/IconDashboard.js +19 -21
  183. package/components/svg/IconDataMigration.js +19 -21
  184. package/components/svg/IconDocument.js +21 -23
  185. package/components/svg/IconDownloadFromTheCloud.js +21 -23
  186. package/components/svg/IconEdit.js +21 -23
  187. package/components/svg/IconExpand.js +21 -23
  188. package/components/svg/IconExport.js +19 -21
  189. package/components/svg/IconExternalLink.js +19 -21
  190. package/components/svg/IconEye.js +19 -21
  191. package/components/svg/IconEyeOff.js +19 -21
  192. package/components/svg/IconFile.js +19 -21
  193. package/components/svg/IconFolder.js +19 -21
  194. package/components/svg/IconFullScreen.js +20 -22
  195. package/components/svg/IconFutures.js +19 -21
  196. package/components/svg/IconGraphReport.js +19 -21
  197. package/components/svg/IconGrid.js +19 -21
  198. package/components/svg/IconHeartMonitor.js +19 -21
  199. package/components/svg/IconHelp.js +21 -23
  200. package/components/svg/IconHistory.js +19 -21
  201. package/components/svg/IconImport.js +19 -21
  202. package/components/svg/IconIndex.js +19 -21
  203. package/components/svg/IconLayers.js +19 -21
  204. package/components/svg/IconLibrary.js +20 -22
  205. package/components/svg/IconList.js +19 -21
  206. package/components/svg/IconLivePlant.js +19 -21
  207. package/components/svg/IconLocations.js +19 -21
  208. package/components/svg/IconMail.js +19 -21
  209. package/components/svg/IconManager.js +19 -21
  210. package/components/svg/IconMap.js +19 -21
  211. package/components/svg/IconMarker.js +19 -21
  212. package/components/svg/IconMenu.js +19 -21
  213. package/components/svg/IconMenuHorizontal.js +19 -21
  214. package/components/svg/IconModule.js +19 -21
  215. package/components/svg/IconMyLocation.js +19 -21
  216. package/components/svg/IconNote.js +21 -23
  217. package/components/svg/IconNursery.js +21 -23
  218. package/components/svg/IconOrg.js +19 -21
  219. package/components/svg/IconParchment.js +19 -21
  220. package/components/svg/IconPhoto.js +19 -21
  221. package/components/svg/IconPlantsFilled.js +19 -21
  222. package/components/svg/IconRedo.js +19 -21
  223. package/components/svg/IconRestorationSite.js +19 -21
  224. package/components/svg/IconSeedBank.js +19 -21
  225. package/components/svg/IconSeedling.js +19 -21
  226. package/components/svg/IconSettings.js +19 -21
  227. package/components/svg/IconSlice.js +19 -21
  228. package/components/svg/IconSubmit.js +19 -21
  229. package/components/svg/IconSubtract.js +21 -23
  230. package/components/svg/IconSynced.js +19 -21
  231. package/components/svg/IconTrashCan.js +19 -21
  232. package/components/svg/IconTreasureMap.js +19 -21
  233. package/components/svg/IconUnavailable.js +19 -21
  234. package/components/svg/IconUndo.js +19 -21
  235. package/components/svg/IconVariable.js +19 -21
  236. package/components/svg/IconVideo.js +30 -31
  237. package/components/svg/IconWifi.js +19 -21
  238. package/components/svg/Info.js +19 -21
  239. package/components/svg/Key.js +19 -21
  240. package/components/svg/Leaf.js +19 -21
  241. package/components/svg/Lock.js +19 -21
  242. package/components/svg/Logo.js +48 -43
  243. package/components/svg/MenuVertical.js +19 -21
  244. package/components/svg/Notification.js +19 -21
  245. package/components/svg/Person.js +19 -21
  246. package/components/svg/Plus.js +19 -21
  247. package/components/svg/RestorationSite.js +19 -21
  248. package/components/svg/Search.js +19 -21
  249. package/components/svg/Seeds.js +19 -21
  250. package/components/svg/Site.js +19 -21
  251. package/components/svg/Sparkles.js +19 -21
  252. package/components/svg/Species.js +19 -21
  253. package/components/svg/Species2.js +202 -164
  254. package/components/svg/Spinner.js +54 -48
  255. package/components/svg/Star.js +19 -21
  256. package/components/svg/Success.js +19 -21
  257. package/components/svg/SuccessFilled.js +19 -21
  258. package/components/svg/TerrawareLogoDesktop.js +55 -49
  259. package/components/svg/TerrawareLogoMobile.js +25 -26
  260. package/components/svg/Touchscreen.js +19 -21
  261. package/components/svg/UploadCloud.js +202 -164
  262. package/components/svg/Warning.js +19 -21
  263. package/components/svg/WelcomeClipboard.js +156 -133
  264. package/components/svg/index.js +129 -129
  265. package/components/table/EnhancedTableToolbar.js +68 -74
  266. package/components/table/EnhancedTableToolbarV2.js +31 -41
  267. package/components/table/TableCellRenderer.js +196 -231
  268. package/components/table/TableHeader.js +71 -73
  269. package/components/table/TableHeaderItem.js +97 -102
  270. package/components/table/density.js +18 -19
  271. package/components/table/index.js +350 -369
  272. package/components/table/sort.js +27 -52
  273. package/components/table/types.js +0 -1
  274. package/components/types/index.js +0 -1
  275. package/hooks/cameraEntityContext.js +2 -13
  276. package/hooks/useBoolean.js +17 -13
  277. package/hooks/useCameraPosition.js +56 -56
  278. package/hooks/useDevicePerformance.js +17 -17
  279. package/hooks/useXr.js +85 -87
  280. package/index.d.ts +3 -1
  281. package/index.d.ts.map +1 -1
  282. package/index.js +59 -58
  283. package/package.json +1 -1
  284. package/setupTests.js +0 -4
  285. package/strings/csv/en.csv +1 -0
  286. package/strings/csv/es.csv +1 -0
  287. package/strings/csv/fr.csv +1 -0
  288. package/strings/export.js +111 -170
  289. package/strings/index.js +25 -54
  290. package/strings/locales.js +21 -41
  291. package/strings/strings-en.d.ts +1 -0
  292. package/strings/strings-en.d.ts.map +1 -1
  293. package/strings/strings-en.js +50 -51
  294. package/strings/strings-es.d.ts +1 -0
  295. package/strings/strings-es.d.ts.map +1 -1
  296. package/strings/strings-es.js +50 -51
  297. package/strings/strings-fr.d.ts +1 -0
  298. package/strings/strings-fr.d.ts.map +1 -1
  299. package/strings/strings-fr.js +50 -51
  300. package/strings/strings-gx.d.ts +1 -0
  301. package/strings/strings-gx.d.ts.map +1 -1
  302. package/strings/strings-gx.js +50 -51
  303. package/style-dictionary-dist/TerrawareTheme.js +186 -186
  304. package/theme.js +74 -76
  305. package/utils/color.js +8 -15
  306. package/utils/date.js +42 -108
  307. package/utils/index.js +4 -4
  308. package/utils/preventDefaultEvent.js +3 -3
  309. package/utils/text.js +11 -11
  310. package/utils/useDeviceInfo.js +42 -79
  311. package/virtualWalkthrough.js +23 -23
@@ -1,109 +1,62 @@
1
- import { Script, Vec3, Color, ShaderMaterial, SEMANTIC_TEXCOORD0, SEMANTIC_POSITION, BLEND_NORMAL, CULLFACE_NONE, Mesh, MeshInstance, LAYERID_IMMEDIATE } from 'playcanvas';
2
- import { computeGroundPlane, yOnPlane } from './groundPlane.js';
3
- import { distanceToCircleEdge } from './xr-scene-bounds.js';
4
-
5
- /**
6
- * Flat-array geometry ready for `Mesh.setPositions` / `Mesh.setUvs` / `Mesh.setIndices`, plus the
7
- * grid cell counts the shader needs to know how far the UVs run.
8
- */
9
-
10
- /**
11
- * Triangle geometry for an upright cylinder standing on the boundary circle. Each segment is a quad
12
- * (two triangles) spanning from the ground up to `topY`; the base is sampled on the ground plane so
13
- * the wall meets the dashed BoundaryRing exactly, while the top is a flat ring at `topY` — the user
14
- * stands on the rig floor, not on the splat's ground plane, so the top is positioned relative to the
15
- * former and only the base follows the latter.
16
- *
17
- * UVs are emitted in **grid cell units** rather than metres — u runs 0..columns around the
18
- * circumference and v runs 0..rows up the wall — so the shader can draw a line at every whole number
19
- * and have the seam at u = columns line up perfectly with u = 0.
20
- */
21
- const boundaryWallMesh = ({
22
- center,
23
- radius,
24
- groundPlane,
25
- baseY,
26
- topY,
27
- gridSpacing,
28
- segments = 96
29
- }) => {
30
- if (radius <= 0 || segments <= 0 || gridSpacing <= 0) {
31
- return {
32
- positions: [],
33
- uvs: [],
34
- indices: [],
35
- columns: 0,
36
- rows: 0
1
+ import { BLEND_NORMAL, CULLFACE_NONE, Color, LAYERID_IMMEDIATE, Mesh, MeshInstance, SEMANTIC_POSITION, SEMANTIC_TEXCOORD0, Script, ShaderMaterial, Vec3 } from "playcanvas";
2
+ import { computeGroundPlane, yOnPlane } from "./groundPlane.js";
3
+ import { distanceToCircleEdge } from "./xr-scene-bounds.js";
4
+ const boundaryWallMesh = ({ center, radius, groundPlane, baseY, topY, gridSpacing, segments = 96 })=>{
5
+ if (radius <= 0 || segments <= 0 || gridSpacing <= 0) return {
6
+ positions: [],
7
+ uvs: [],
8
+ indices: [],
9
+ columns: 0,
10
+ rows: 0
37
11
  };
38
- }
39
- const plane = computeGroundPlane(groundPlane);
40
- const step = Math.PI * 2 / segments;
41
-
42
- // First pass: the bottom of every column, which the row count and the extent guard both need
43
- // before any vertex can be emitted.
44
- const bottoms = Array.from({
45
- length: segments
46
- }, (_, index) => {
47
- const angle = index * step;
48
- const x = center.x + radius * Math.cos(angle);
49
- const z = center.z + radius * Math.sin(angle);
50
- return {
51
- x,
52
- z,
53
- y: plane ? yOnPlane(x, z, plane.normal, plane.point, baseY) : baseY
12
+ const plane = computeGroundPlane(groundPlane);
13
+ const step = 2 * Math.PI / segments;
14
+ const bottoms = Array.from({
15
+ length: segments
16
+ }, (_, index)=>{
17
+ const angle = index * step;
18
+ const x = center.x + radius * Math.cos(angle);
19
+ const z = center.z + radius * Math.sin(angle);
20
+ return {
21
+ x,
22
+ z,
23
+ y: plane ? yOnPlane(x, z, plane.normal, plane.point, baseY) : baseY
24
+ };
25
+ });
26
+ if (bottoms.some((bottom)=>bottom.y >= topY)) return {
27
+ positions: [],
28
+ uvs: [],
29
+ indices: [],
30
+ columns: 0,
31
+ rows: 0
54
32
  };
55
- });
56
- if (bottoms.some(bottom => bottom.y >= topY)) {
33
+ const averageBottomY = bottoms.reduce((total, bottom)=>total + bottom.y, 0) / bottoms.length;
34
+ const columns = Math.max(1, Math.round(2 * Math.PI * radius / gridSpacing));
35
+ const rows = Math.max(1, Math.round((topY - averageBottomY) / gridSpacing));
36
+ const positions = [];
37
+ const uvs = [];
38
+ const indices = [];
39
+ const pushColumn = (index)=>{
40
+ const { x, z, y } = bottoms[index % segments];
41
+ const u = index / segments * columns;
42
+ positions.push(x, y, z, x, topY, z);
43
+ uvs.push(u, 0, u, rows);
44
+ };
45
+ for(let i = 0; i < segments; i++){
46
+ const base = 4 * i;
47
+ pushColumn(i);
48
+ pushColumn(i + 1);
49
+ indices.push(base, base + 1, base + 3, base, base + 3, base + 2);
50
+ }
57
51
  return {
58
- positions: [],
59
- uvs: [],
60
- indices: [],
61
- columns: 0,
62
- rows: 0
52
+ positions,
53
+ uvs,
54
+ indices,
55
+ columns,
56
+ rows
63
57
  };
64
- }
65
-
66
- // Every column has its own world height once the ground tilts, but v still runs 0..rows uniformly
67
- // for all of them, so a single row count has to stand for the whole wall. Deriving it from the
68
- // average bottom is exact for a flat ground plane (the common case) and keeps the cells square on
69
- // average when the plane is tilted.
70
- const averageBottomY = bottoms.reduce((total, bottom) => total + bottom.y, 0) / bottoms.length;
71
- const columns = Math.max(1, Math.round(Math.PI * 2 * radius / gridSpacing));
72
- const rows = Math.max(1, Math.round((topY - averageBottomY) / gridSpacing));
73
- const positions = [];
74
- const uvs = [];
75
- const indices = [];
76
- const pushColumn = index => {
77
- // The closing column (index === segments) reuses column 0's position so the loop shuts exactly,
78
- // while u keeps counting up to `columns` to place the seam.
79
- const {
80
- x,
81
- z,
82
- y
83
- } = bottoms[index % segments];
84
- const u = index / segments * columns;
85
- positions.push(x, y, z, x, topY, z);
86
- uvs.push(u, 0, u, rows);
87
- };
88
- for (let i = 0; i < segments; i++) {
89
- const base = i * 4;
90
- // Per segment: v0 bottom@a0, v1 top@a0, v2 bottom@a1, v3 top@a1.
91
- pushColumn(i);
92
- pushColumn(i + 1);
93
- indices.push(base, base + 1, base + 3, base, base + 3, base + 2);
94
- }
95
- return {
96
- positions,
97
- uvs,
98
- indices,
99
- columns,
100
- rows
101
- };
102
58
  };
103
-
104
- // GLSL only: @playcanvas/react's Application defaults to deviceTypes [DEVICETYPE_WEBGL2], so the
105
- // WGSL variants the engine's own shaders carry would never be compiled here.
106
- const wallVertexGLSL = /* glsl */`
59
+ const wallVertexGLSL = `
107
60
  attribute vec3 vertex_position;
108
61
  attribute vec2 aUv0;
109
62
 
@@ -120,7 +73,7 @@ const wallVertexGLSL = /* glsl */`
120
73
  gl_Position = matrix_viewProjection * world;
121
74
  }
122
75
  `;
123
- const wallFragmentGLSL = /* glsl */`
76
+ const wallFragmentGLSL = `
124
77
  uniform vec3 uHeadPos;
125
78
  uniform float uFadeDistance;
126
79
  uniform float uBlockedFadeScale;
@@ -165,167 +118,131 @@ const wallFragmentGLSL = /* glsl */`
165
118
  gl_FragColor = vec4(mix(uCalmColor, uWarnColor, max(proximity, uBlocked)), alpha);
166
119
  }
167
120
  `;
168
-
169
- /**
170
- * Structural shape of TfXrNavigation this script depends on, kept local so it does not have to
171
- * import the navigation script. `enabled` is optional because a disabled script (e.g. while edit
172
- * mode is on) stops receiving postUpdate and would otherwise latch a stale clampDistance.
173
- */
174
-
175
- /**
176
- * Draws a Quest-guardian-style boundary wall: an upright cylinder on the bounds circle whose grid
177
- * fades in and shifts toward a warning colour as the head approaches, and goes to full intensity
178
- * while XR navigation is actively holding the user back.
179
- *
180
- * center / radius / groundPlane / baseY are set imperatively by the BoundaryWall component (see
181
- * BoundaryWall.tsx for why they are not reactive Script props), which calls `rebuild()` afterwards.
182
- */
183
121
  class BoundaryWallScript extends Script {
184
- static scriptName = 'boundaryWall';
185
- center = new Vec3();
186
- radius = 0;
187
- groundPlane = [];
188
- /** Flat bottom height used when no usable ground plane is supplied. */
189
- baseY = 0;
190
- /**
191
- * Absolute world Y of the wall's top edge. The default puts it 2.5 m above a rig floor at y = 0,
192
- * which is where the user actually stands - the splat's ground plane can be well below that.
193
- */
194
- topY = 2.5;
195
- segments = 96;
196
- gridSpacing = 0.5;
197
- /** Grid line thickness in world metres. */
198
- lineWidth = 0.025;
199
- /** Head distance, in world metres, at which the wall starts to appear. */
200
- fadeDistance = 1.5;
201
- /** Multiplier applied to fadeDistance while movement is being clamped. */
202
- blockedFadeScale = 2.5;
203
- calmColor = new Color(0.35, 0.7, 1);
204
- warnColor = new Color(1, 0.45, 0.2);
205
- _camera = null;
206
- _navigation = null;
207
- _headParam = new Float32Array(3);
208
- initialize() {
209
- this._material = new ShaderMaterial({
210
- uniqueName: 'boundary-wall',
211
- vertexGLSL: wallVertexGLSL,
212
- fragmentGLSL: wallFragmentGLSL,
213
- attributes: {
214
- vertex_position: SEMANTIC_POSITION,
215
- aUv0: SEMANTIC_TEXCOORD0
216
- }
217
- });
218
- this._material.blendType = BLEND_NORMAL;
219
- this._material.cull = CULLFACE_NONE;
220
- this._material.depthWrite = false;
221
- this._material.update();
222
- this._resolveDependencies();
223
- this.rebuild();
224
- this.once('destroy', () => {
225
- this._clearMesh();
226
- this._material?.destroy();
227
- this._material = undefined;
228
- this._camera = null;
229
- this._navigation = null;
230
- });
231
- }
232
-
233
- /**
234
- * The camera and the navigation script both live on entities this one does not own. They are
235
- * resolved lazily rather than once, because script initialize order across sibling entities is not
236
- * guaranteed and this entity mounts when an XR session starts.
237
- */
238
- _resolveDependencies() {
239
- if (!this._camera) {
240
- this._camera = this.app.root.findComponent('camera')?.entity ?? null;
122
+ static scriptName = 'boundaryWall';
123
+ center = new Vec3();
124
+ radius = 0;
125
+ groundPlane = [];
126
+ baseY = 0;
127
+ topY = 2.5;
128
+ segments = 96;
129
+ gridSpacing = 0.5;
130
+ lineWidth = 0.025;
131
+ fadeDistance = 1.5;
132
+ blockedFadeScale = 2.5;
133
+ calmColor = new Color(0.35, 0.7, 1);
134
+ warnColor = new Color(1, 0.45, 0.2);
135
+ _material;
136
+ _mesh;
137
+ _camera = null;
138
+ _navigation = null;
139
+ _headParam = new Float32Array(3);
140
+ initialize() {
141
+ this._material = new ShaderMaterial({
142
+ uniqueName: 'boundary-wall',
143
+ vertexGLSL: wallVertexGLSL,
144
+ fragmentGLSL: wallFragmentGLSL,
145
+ attributes: {
146
+ vertex_position: SEMANTIC_POSITION,
147
+ aUv0: SEMANTIC_TEXCOORD0
148
+ }
149
+ });
150
+ this._material.blendType = BLEND_NORMAL;
151
+ this._material.cull = CULLFACE_NONE;
152
+ this._material.depthWrite = false;
153
+ this._material.update();
154
+ this._resolveDependencies();
155
+ this.rebuild();
156
+ this.once('destroy', ()=>{
157
+ this._clearMesh();
158
+ this._material?.destroy();
159
+ this._material = void 0;
160
+ this._camera = null;
161
+ this._navigation = null;
162
+ });
241
163
  }
242
- if (!this._navigation) {
243
- const rig = this.app.root.findByName('camera-root');
244
- // @ts-expect-error - scripts are added dynamically to the entity
245
- this._navigation = rig?.script?.tfXrNavigation ?? null;
164
+ _resolveDependencies() {
165
+ if (!this._camera) this._camera = this.app.root.findComponent('camera')?.entity ?? null;
166
+ if (!this._navigation) {
167
+ const rig = this.app.root.findByName('camera-root');
168
+ this._navigation = rig?.script?.tfXrNavigation ?? null;
169
+ }
246
170
  }
247
- }
248
- rebuild() {
249
- this._clearMesh();
250
- const geometry = boundaryWallMesh({
251
- center: this.center,
252
- radius: this.radius,
253
- groundPlane: this.groundPlane,
254
- baseY: this.baseY,
255
- topY: this.topY,
256
- gridSpacing: this.gridSpacing,
257
- segments: this.segments
258
- });
259
- if (geometry.positions.length === 0 || !this._material) {
260
- return;
171
+ rebuild() {
172
+ this._clearMesh();
173
+ const geometry = boundaryWallMesh({
174
+ center: this.center,
175
+ radius: this.radius,
176
+ groundPlane: this.groundPlane,
177
+ baseY: this.baseY,
178
+ topY: this.topY,
179
+ gridSpacing: this.gridSpacing,
180
+ segments: this.segments
181
+ });
182
+ if (0 === geometry.positions.length || !this._material) return;
183
+ this._material.setParameter('uLineHalfWidth', this.lineWidth / this.gridSpacing * 0.5);
184
+ this._material.setParameter('uFadeDistance', this.fadeDistance);
185
+ this._material.setParameter('uBlockedFadeScale', this.blockedFadeScale);
186
+ this._material.setParameter('uRows', geometry.rows);
187
+ this._material.setParameter('uCalmColor', [
188
+ this.calmColor.r,
189
+ this.calmColor.g,
190
+ this.calmColor.b
191
+ ]);
192
+ this._material.setParameter('uWarnColor', [
193
+ this.warnColor.r,
194
+ this.warnColor.g,
195
+ this.warnColor.b
196
+ ]);
197
+ this._material.setParameter('uBlocked', 0);
198
+ this._material.setParameter('uHeadPos', [
199
+ 0,
200
+ 0,
201
+ 0
202
+ ]);
203
+ const mesh = new Mesh(this.app.graphicsDevice);
204
+ mesh.setPositions(geometry.positions);
205
+ mesh.setUvs(0, geometry.uvs);
206
+ mesh.setIndices(geometry.indices);
207
+ mesh.update();
208
+ this._mesh = mesh;
209
+ const meshInstance = new MeshInstance(mesh, this._material);
210
+ if (this.entity.render) this.entity.render.meshInstances = [
211
+ meshInstance
212
+ ];
213
+ else this.entity.addComponent('render', {
214
+ meshInstances: [
215
+ meshInstance
216
+ ],
217
+ layers: [
218
+ LAYERID_IMMEDIATE
219
+ ]
220
+ });
261
221
  }
262
-
263
- // The shader measures distance in grid cells, so convert. A cell is gridSpacing across only
264
- // approximately - columns and rows are rounded to whole numbers - but that error is well under
265
- // a line width, so it is not worth carrying the exact cell size through as a second uniform.
266
- this._material.setParameter('uLineHalfWidth', this.lineWidth / this.gridSpacing * 0.5);
267
- this._material.setParameter('uFadeDistance', this.fadeDistance);
268
- this._material.setParameter('uBlockedFadeScale', this.blockedFadeScale);
269
- this._material.setParameter('uRows', geometry.rows);
270
- this._material.setParameter('uCalmColor', [this.calmColor.r, this.calmColor.g, this.calmColor.b]);
271
- this._material.setParameter('uWarnColor', [this.warnColor.r, this.warnColor.g, this.warnColor.b]);
272
- this._material.setParameter('uBlocked', 0);
273
- this._material.setParameter('uHeadPos', [0, 0, 0]);
274
- const mesh = new Mesh(this.app.graphicsDevice);
275
- mesh.setPositions(geometry.positions);
276
- mesh.setUvs(0, geometry.uvs);
277
- mesh.setIndices(geometry.indices);
278
- mesh.update();
279
- this._mesh = mesh;
280
- const meshInstance = new MeshInstance(mesh, this._material);
281
- if (this.entity.render) {
282
- this.entity.render.meshInstances = [meshInstance];
283
- } else {
284
- // Immediate layer, drawn after the World layer where the splats render, so the wall is not
285
- // composited behind them - same reason BoundaryRingScript uses it.
286
- this.entity.addComponent('render', {
287
- meshInstances: [meshInstance],
288
- layers: [LAYERID_IMMEDIATE]
289
- });
222
+ update() {
223
+ this._resolveDependencies();
224
+ const material = this._material;
225
+ const camera = this._camera;
226
+ if (!material || !camera || !this.entity.render) return;
227
+ const navigation = this._navigation;
228
+ const blocked = navigation?.enabled !== false && (navigation?.clampDistance ?? 0) > 0 ? 1 : 0;
229
+ const reveal = this.fadeDistance * (blocked ? this.blockedFadeScale : 1);
230
+ const head = camera.getPosition();
231
+ const edgeDistance = distanceToCircleEdge(head.x, head.z, this.center.x, this.center.z, this.radius);
232
+ this.entity.render.enabled = this.radius > 0 && edgeDistance > -reveal;
233
+ if (!this.entity.render.enabled) return;
234
+ this._headParam[0] = head.x;
235
+ this._headParam[1] = head.y;
236
+ this._headParam[2] = head.z;
237
+ material.setParameter('uHeadPos', this._headParam);
238
+ material.setParameter('uBlocked', blocked);
290
239
  }
291
- }
292
- update() {
293
- this._resolveDependencies();
294
- const material = this._material;
295
- const camera = this._camera;
296
- if (!material || !camera || !this.entity.render) {
297
- return;
240
+ _clearMesh() {
241
+ if (this.entity.render) this.entity.render.meshInstances = [];
242
+ if (this._mesh) {
243
+ this._mesh.destroy();
244
+ this._mesh = void 0;
245
+ }
298
246
  }
299
- const navigation = this._navigation;
300
- // A disabled navigation script (e.g. edit mode) stops receiving postUpdate, so its
301
- // clampDistance would otherwise latch whatever value it last held.
302
- const blocked = navigation?.enabled !== false && (navigation?.clampDistance ?? 0) > 0 ? 1 : 0;
303
- const reveal = this.fadeDistance * (blocked ? this.blockedFadeScale : 1);
304
- const head = camera.getPosition();
305
-
306
- // Skip the transparent draw entirely while the head is deeper inside than the widest reveal
307
- // radius - every fragment would discard anyway. Toggling render.enabled rather than
308
- // entity.enabled keeps this update running so the wall can come back.
309
- const edgeDistance = distanceToCircleEdge(head.x, head.z, this.center.x, this.center.z, this.radius);
310
- this.entity.render.enabled = this.radius > 0 && edgeDistance > -reveal;
311
- if (!this.entity.render.enabled) {
312
- return;
313
- }
314
- this._headParam[0] = head.x;
315
- this._headParam[1] = head.y;
316
- this._headParam[2] = head.z;
317
- material.setParameter('uHeadPos', this._headParam);
318
- material.setParameter('uBlocked', blocked);
319
- }
320
- _clearMesh() {
321
- if (this.entity.render) {
322
- this.entity.render.meshInstances = [];
323
- }
324
- if (this._mesh) {
325
- this._mesh.destroy();
326
- this._mesh = undefined;
327
- }
328
- }
329
247
  }
330
-
331
248
  export { BoundaryWallScript, boundaryWallMesh };
@@ -1,66 +1,20 @@
1
- /**
2
- * The pose an adopted camera is put back to when the walkthrough releases it.
3
- */
4
-
5
- /**
6
- * A camera the walkthrough took over, and the call that hands it back.
7
- */
8
-
9
- /**
10
- * Hands the scene's existing camera to a walkthrough rig that hasn't been given one.
11
- *
12
- * A caller mounting the viewer into a scene it doesn't own passes `camera={null}`, because an XR
13
- * session is welded to the camera component it was started on — `XrManager.camera` is read-only and
14
- * `start` refuses while a session is running — so a walkthrough that brought a camera of its own
15
- * could only become the session's camera by ending it, which drops the user out of the headset.
16
- *
17
- * That leaves the rig with no camera under it, and everything that poses or solves against the head
18
- * resolves it with `findComponent('camera')` on the rig: `WalkthroughCamera`'s pointer and keyboard
19
- * walk, `XrStartPose`, `XrNavigation`'s thumbstick movement and turning, and the bounds clamp all go
20
- * inert, so the head is never placed at the walkthrough's start point and can never be moved from
21
- * wherever the host scene left it. Splat LOD is chosen from
22
- * the camera's distance to each octree node, so the far side of the model stays coarse for the whole
23
- * session no matter which way the headset turns.
24
- *
25
- * Reparenting carries the live camera component with it, so the session, its views, and the host's
26
- * own clear color and clip planes are all untouched — only which entity the camera hangs off
27
- * changes. The camera becomes a direct child of the rig, which is exactly where a camera the viewer
28
- * owns sits, so the rig means the same thing either way.
29
- *
30
- * @param root - The scene root to look for a camera in, expected to hold exactly one camera.
31
- * @param rig - The entity the walkthrough's XR scripts move, `camera-root`.
32
- * @param ownCamera - The entity the viewer mounts its own camera on. Nothing is adopted when it
33
- * already carries one.
34
- * @returns The adopted camera and the call that puts it back, or null when there was nothing to
35
- * adopt.
36
- */
37
- const adoptSceneCamera = (root, rig, ownCamera) => {
38
- if (ownCamera.camera) {
39
- return null;
40
- }
41
- const camera = root.findComponent('camera')?.entity;
42
- // isDescendantOf covers a host that mounted the walkthrough inside its own camera, where
43
- // reparenting would make a cycle.
44
- if (!camera || camera === rig || !camera.parent || rig.isDescendantOf(camera)) {
45
- return null;
46
- }
47
- const previous = {
48
- parent: camera.parent,
49
- position: camera.getLocalPosition().clone(),
50
- rotation: camera.getLocalRotation().clone()
51
- };
52
-
53
- // addChild keeps the local transform, so the host's authored camera pose survives the move and
54
- // only has to be restored because a session overwrites it with the head pose every frame.
55
- rig.addChild(camera);
56
- return {
57
- camera,
58
- release: () => {
59
- previous.parent.addChild(camera);
60
- camera.setLocalPosition(previous.position);
61
- camera.setLocalRotation(previous.rotation);
62
- }
63
- };
1
+ const adoptSceneCamera = (root, rig, ownCamera)=>{
2
+ if (ownCamera.camera) return null;
3
+ const camera = root.findComponent('camera')?.entity;
4
+ if (!camera || camera === rig || !camera.parent || rig.isDescendantOf(camera)) return null;
5
+ const previous = {
6
+ parent: camera.parent,
7
+ position: camera.getLocalPosition().clone(),
8
+ rotation: camera.getLocalRotation().clone()
9
+ };
10
+ rig.addChild(camera);
11
+ return {
12
+ camera,
13
+ release: ()=>{
14
+ previous.parent.addChild(camera);
15
+ camera.setLocalPosition(previous.position);
16
+ camera.setLocalRotation(previous.rotation);
17
+ }
18
+ };
64
19
  };
65
-
66
20
  export { adoptSceneCamera };
@@ -1,50 +1,35 @@
1
- import { Vec3 } from 'playcanvas';
2
-
3
- /** Compute Y on the plane defined by `normal` passing through `point` at world XZ coords (x, z). */
4
- const yOnPlane = (x, z, normal, point, fallback) => {
5
- if (Math.abs(normal.y) < 1e-6) {
6
- return fallback;
7
- }
8
- return point.y - (normal.x * (x - point.x) + normal.z * (z - point.z)) / normal.y;
1
+ import { Vec3 } from "playcanvas";
2
+ const yOnPlane = (x, z, normal, point, fallback)=>{
3
+ if (Math.abs(normal.y) < 1e-6) return fallback;
4
+ return point.y - (normal.x * (x - point.x) + normal.z * (z - point.z)) / normal.y;
9
5
  };
10
-
11
- /**
12
- * Derive a ground plane from 3 world-space points.
13
- * Returns a normalized normal (flipped so normal.y >= 0) and a point on the plane,
14
- * or null when fewer than 3 points are supplied or they are degenerate/collinear.
15
- */
16
- const computeGroundPlane = points => {
17
- if (points.length < 3) {
18
- return null;
19
- }
20
- const p0 = points[0];
21
- const p1 = points[1];
22
- const p2 = points[2];
23
- const v1x = p1.x - p0.x;
24
- const v1y = p1.y - p0.y;
25
- const v1z = p1.z - p0.z;
26
- const v2x = p2.x - p0.x;
27
- const v2y = p2.y - p0.y;
28
- const v2z = p2.z - p0.z;
29
- let nx = v1y * v2z - v1z * v2y;
30
- let ny = v1z * v2x - v1x * v2z;
31
- let nz = v1x * v2y - v1y * v2x;
32
- const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
33
- if (len < 1e-10) {
34
- return null;
35
- }
36
- nx /= len;
37
- ny /= len;
38
- nz /= len;
39
- if (ny < 0) {
40
- nx = -nx;
41
- ny = -ny;
42
- nz = -nz;
43
- }
44
- return {
45
- normal: new Vec3(nx, ny, nz),
46
- point: new Vec3(p0.x, p0.y, p0.z)
47
- };
6
+ const computeGroundPlane = (points)=>{
7
+ if (points.length < 3) return null;
8
+ const p0 = points[0];
9
+ const p1 = points[1];
10
+ const p2 = points[2];
11
+ const v1x = p1.x - p0.x;
12
+ const v1y = p1.y - p0.y;
13
+ const v1z = p1.z - p0.z;
14
+ const v2x = p2.x - p0.x;
15
+ const v2y = p2.y - p0.y;
16
+ const v2z = p2.z - p0.z;
17
+ let nx = v1y * v2z - v1z * v2y;
18
+ let ny = v1z * v2x - v1x * v2z;
19
+ let nz = v1x * v2y - v1y * v2x;
20
+ const len = Math.sqrt(nx * nx + ny * ny + nz * nz);
21
+ if (len < 1e-10) return null;
22
+ nx /= len;
23
+ ny /= len;
24
+ nz /= len;
25
+ if (ny < 0) {
26
+ nx = -nx;
27
+ ny = -ny;
28
+ nz = -nz;
29
+ }
30
+ return {
31
+ normal: new Vec3(nx, ny, nz),
32
+ point: new Vec3(p0.x, p0.y, p0.z)
33
+ };
48
34
  };
49
-
50
35
  export { computeGroundPlane, yOnPlane };