@terraware/web-components 5.0.26-rc.1 → 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.
- package/components/AntSwitch/index.js +19 -22
- package/components/Autocomplete/Autocomplete.js +106 -130
- package/components/Badge/index.js +28 -29
- package/components/BusySpinner/index.js +40 -45
- package/components/Button/Button.js +41 -47
- package/components/Checkbox.js +64 -63
- package/components/Confirm/index.js +74 -83
- package/components/DatePicker/DatePicker.js +138 -141
- package/components/DialogBox/DialogBox.js +111 -118
- package/components/Divisor.js +18 -18
- package/components/Dropdown.js +66 -94
- package/components/EditableTable/index.js +211 -271
- package/components/ErrorBox/ErrorBox.js +47 -49
- package/components/FileChooser/index.js +117 -135
- package/components/FormBottomBar/index.js +75 -84
- package/components/Icon/Icon.js +12 -20
- package/components/Icon/icons/index.js +256 -256
- package/components/IconTooltip/index.js +49 -60
- package/components/Map/MapBox.js +545 -452
- package/components/Map/MapContainer.js +25 -29
- package/components/Map/MapDateSliderControl.js +64 -64
- package/components/Map/MapDrawer.js +41 -47
- package/components/Map/MapLegend.js +176 -172
- package/components/Map/MapViewStyleControl.js +59 -63
- package/components/Map/ReactMapboxWrapper.js +3 -2
- package/components/Map/index.js +161 -180
- package/components/Map/types.js +12 -17
- package/components/Map/useMaintainLayerOrder.js +16 -26
- package/components/Markdown/index.js +25 -26
- package/components/Message/Message.js +73 -76
- package/components/MultiSelect/index.js +112 -124
- package/components/Navbar/NavFooter.js +9 -13
- package/components/Navbar/NavItem.js +70 -76
- package/components/Navbar/NavSection.js +17 -19
- package/components/Navbar/Navbar.js +39 -46
- package/components/Navbar/SubNavbar.js +9 -13
- package/components/Note.js +18 -22
- package/components/OverlayModal/OverlayModal.js +56 -63
- package/components/PageForm/index.js +33 -41
- package/components/PhotoChooser/index.js +165 -173
- package/components/PhotosCarousel/PhotoSlide.js +32 -35
- package/components/PhotosCarousel/index.js +80 -85
- package/components/PhotosCarousel/usePhotosCarousel.js +69 -76
- package/components/Pill/index.js +41 -45
- package/components/PillList/index.js +17 -24
- package/components/PlacementWrapper/index.js +38 -59
- package/components/PopoverMenu/Popover.js +68 -85
- package/components/PopoverMenu/index.js +51 -56
- package/components/PopoverMultiSelect/index.js +52 -59
- package/components/ProgressCircle/ProgressCircle.js +28 -32
- package/components/RadioButton.js +17 -19
- package/components/Select/Select.js +13 -15
- package/components/Select/SelectT.js +235 -250
- package/components/Separator/index.js +14 -17
- package/components/Size.js +0 -1
- package/components/Slider/index.js +43 -56
- package/components/SummaryBox.js +70 -72
- package/components/Tabs/index.js +133 -135
- package/components/TextTruncated/index.js +117 -130
- package/components/Textfield/Textfield.js +158 -190
- package/components/Textfield/TruncatedTextArea.js +70 -84
- package/components/TimelineSlider/TimelineMarker.js +25 -30
- package/components/TimelineSlider/TimelineRail.js +8 -11
- package/components/TimelineSlider/index.js +64 -66
- package/components/TimelineSliderV2/ClusterBadge.js +25 -29
- package/components/TimelineSliderV2/TimelineDot.js +31 -35
- package/components/TimelineSliderV2/TimelineRail.js +8 -11
- package/components/TimelineSliderV2/index.js +121 -103
- package/components/TimelineSliderV2/layout.js +124 -147
- package/components/TimelineSliderV2/types.js +0 -1
- package/components/Tooltip/Tooltip.js +47 -56
- package/components/ViewPhotosDialog/index.js +28 -35
- package/components/VirtualWalkthrough/Annotation.js +163 -207
- package/components/VirtualWalkthrough/AnnotationEditPane.js +198 -184
- package/components/VirtualWalkthrough/AnnotationPanel.js +187 -199
- package/components/VirtualWalkthrough/Application.js +31 -34
- package/components/VirtualWalkthrough/AutoRotator.js +85 -223
- package/components/VirtualWalkthrough/BlockingSpinner.js +26 -28
- package/components/VirtualWalkthrough/BoundaryRing.js +30 -34
- package/components/VirtualWalkthrough/BoundaryWall.js +32 -36
- package/components/VirtualWalkthrough/CameraInfo.js +100 -89
- package/components/VirtualWalkthrough/ControlsInfoPane.js +254 -195
- package/components/VirtualWalkthrough/GradientSky.js +72 -73
- package/components/VirtualWalkthrough/SplatControls.js +209 -221
- package/components/VirtualWalkthrough/SplatCrop.js +21 -20
- package/components/VirtualWalkthrough/SplatFadeCrop.js +21 -20
- package/components/VirtualWalkthrough/SplatModel.js +65 -85
- package/components/VirtualWalkthrough/SplatRevealRain.js +41 -46
- package/components/VirtualWalkthrough/TfAnnotationManager.js +197 -379
- package/components/VirtualWalkthrough/TfXrNavigation.js +73 -162
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +438 -431
- package/components/VirtualWalkthrough/VrAnnotationPanel.js +18 -22
- package/components/VirtualWalkthrough/XrAnnotationInteraction.js +14 -17
- package/components/VirtualWalkthrough/XrExitButton.js +14 -17
- package/components/VirtualWalkthrough/XrGazeDwell.js +14 -17
- package/components/VirtualWalkthrough/XrPointerRay.js +14 -17
- package/components/VirtualWalkthrough/XrStartPose.js +67 -121
- package/components/VirtualWalkthrough/boundary-ring.js +87 -117
- package/components/VirtualWalkthrough/boundary-wall.js +175 -258
- package/components/VirtualWalkthrough/camera-adoption.js +18 -64
- package/components/VirtualWalkthrough/groundPlane.js +32 -47
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +35 -35
- package/components/VirtualWalkthrough/splatFormat.js +13 -47
- package/components/VirtualWalkthrough/useAdoptedCamera.js +23 -44
- package/components/VirtualWalkthrough/useRestartOnVr.js +26 -37
- package/components/VirtualWalkthrough/useXrRenderTuning.js +33 -54
- package/components/VirtualWalkthrough/vr-annotation-panel-layout.js +67 -99
- package/components/VirtualWalkthrough/vr-annotation-panel.js +403 -433
- package/components/VirtualWalkthrough/vr-annotation-scroll.js +10 -28
- package/components/VirtualWalkthrough/walkthrough-camera.js +204 -329
- package/components/VirtualWalkthrough/xr-annotation-candidate-buffer.js +39 -80
- package/components/VirtualWalkthrough/xr-annotation-candidates.js +6 -18
- package/components/VirtualWalkthrough/xr-annotation-interaction.js +31 -56
- package/components/VirtualWalkthrough/xr-annotation-targeting.js +22 -38
- package/components/VirtualWalkthrough/xr-button-arming.js +7 -17
- package/components/VirtualWalkthrough/xr-button-hold.js +22 -36
- package/components/VirtualWalkthrough/xr-exit-button-mask.js +22 -36
- package/components/VirtualWalkthrough/xr-exit-button.js +148 -207
- package/components/VirtualWalkthrough/xr-face-buttons.js +19 -29
- package/components/VirtualWalkthrough/xr-gaze-dwell-state.js +23 -34
- package/components/VirtualWalkthrough/xr-gaze-dwell.js +107 -144
- package/components/VirtualWalkthrough/xr-interactive-ui.js +12 -24
- package/components/VirtualWalkthrough/xr-pointer-ray.js +13 -20
- package/components/VirtualWalkthrough/xr-progress-pie-material.js +117 -88
- package/components/VirtualWalkthrough/xr-progress-pie.js +1 -10
- package/components/VirtualWalkthrough/xr-ray-quad.js +16 -30
- package/components/VirtualWalkthrough/xr-render-tuning.js +17 -48
- package/components/VirtualWalkthrough/xr-scene-bounds.js +21 -37
- package/components/VirtualWalkthrough/xr-start-pose.js +27 -73
- package/components/VirtualWalkthrough/xr-teleport-gesture.js +16 -42
- package/components/svg/BlobbyGrayIconImage.js +41 -39
- package/components/svg/BlobbyGrayIconImport.js +41 -39
- package/components/svg/BlobbyGrayIconUploadToTheCloud.js +41 -39
- package/components/svg/BlobbyIconCloudChecked.js +202 -164
- package/components/svg/BlobbyIconExportOrUploadPhoto.js +202 -164
- package/components/svg/BlobbyIconFolder.js +202 -164
- package/components/svg/BlobbyIconGraphReport.js +202 -164
- package/components/svg/BlobbyIconHappy.js +202 -164
- package/components/svg/BlobbyIconHeartMonitor.js +202 -164
- package/components/svg/BlobbyIconLeaf.js +202 -164
- package/components/svg/BlobbyIconLibrary.js +202 -164
- package/components/svg/BlobbyIconNursery.js +204 -166
- package/components/svg/BlobbyIconOrganization.js +202 -164
- package/components/svg/BlobbyIconParchment.js +202 -164
- package/components/svg/BlobbyIconPeople.js +202 -164
- package/components/svg/BlobbyIconSeedBank.js +202 -164
- package/components/svg/BlobbyIconSeedling.js +202 -164
- package/components/svg/BlobbyIconSeeds.js +202 -164
- package/components/svg/BlobbyIconSite.js +206 -167
- package/components/svg/BlobbyIconSparkles.js +202 -164
- package/components/svg/BlobbyIconWrench.js +202 -164
- package/components/svg/Bug.js +19 -21
- package/components/svg/Calendar.js +19 -21
- package/components/svg/CaretDown.js +19 -21
- package/components/svg/CaretLeft.js +19 -21
- package/components/svg/CaretRight.js +19 -21
- package/components/svg/CaretUp.js +19 -21
- package/components/svg/ChevronDown.js +19 -21
- package/components/svg/ChevronUp.js +19 -21
- package/components/svg/Close.js +19 -21
- package/components/svg/Critical.js +19 -21
- package/components/svg/Edit.js +202 -164
- package/components/svg/Error.js +19 -21
- package/components/svg/Filter.js +19 -21
- package/components/svg/Folder.js +19 -21
- package/components/svg/Help.js +19 -21
- package/components/svg/Home.js +19 -21
- package/components/svg/IconAdd.js +19 -21
- package/components/svg/IconArrowRight.js +21 -23
- package/components/svg/IconBusinessNetwork.js +19 -21
- package/components/svg/IconCalendar.js +19 -21
- package/components/svg/IconCancel.js +19 -21
- package/components/svg/IconChargingBattery.js +19 -21
- package/components/svg/IconChecklist.js +21 -23
- package/components/svg/IconCheckmark.js +19 -21
- package/components/svg/IconCoinInHand.js +19 -21
- package/components/svg/IconColumns.js +19 -21
- package/components/svg/IconComment.js +20 -22
- package/components/svg/IconDashboard.js +19 -21
- package/components/svg/IconDataMigration.js +19 -21
- package/components/svg/IconDocument.js +21 -23
- package/components/svg/IconDownloadFromTheCloud.js +21 -23
- package/components/svg/IconEdit.js +21 -23
- package/components/svg/IconExpand.js +21 -23
- package/components/svg/IconExport.js +19 -21
- package/components/svg/IconExternalLink.js +19 -21
- package/components/svg/IconEye.js +19 -21
- package/components/svg/IconEyeOff.js +19 -21
- package/components/svg/IconFile.js +19 -21
- package/components/svg/IconFolder.js +19 -21
- package/components/svg/IconFullScreen.js +20 -22
- package/components/svg/IconFutures.js +19 -21
- package/components/svg/IconGraphReport.js +19 -21
- package/components/svg/IconGrid.js +19 -21
- package/components/svg/IconHeartMonitor.js +19 -21
- package/components/svg/IconHelp.js +21 -23
- package/components/svg/IconHistory.js +19 -21
- package/components/svg/IconImport.js +19 -21
- package/components/svg/IconIndex.js +19 -21
- package/components/svg/IconLayers.js +19 -21
- package/components/svg/IconLibrary.js +20 -22
- package/components/svg/IconList.js +19 -21
- package/components/svg/IconLivePlant.js +19 -21
- package/components/svg/IconLocations.js +19 -21
- package/components/svg/IconMail.js +19 -21
- package/components/svg/IconManager.js +19 -21
- package/components/svg/IconMap.js +19 -21
- package/components/svg/IconMarker.js +19 -21
- package/components/svg/IconMenu.js +19 -21
- package/components/svg/IconMenuHorizontal.js +19 -21
- package/components/svg/IconModule.js +19 -21
- package/components/svg/IconMyLocation.js +19 -21
- package/components/svg/IconNote.js +21 -23
- package/components/svg/IconNursery.js +21 -23
- package/components/svg/IconOrg.js +19 -21
- package/components/svg/IconParchment.js +19 -21
- package/components/svg/IconPhoto.js +19 -21
- package/components/svg/IconPlantsFilled.js +19 -21
- package/components/svg/IconRedo.js +19 -21
- package/components/svg/IconRestorationSite.js +19 -21
- package/components/svg/IconSeedBank.js +19 -21
- package/components/svg/IconSeedling.js +19 -21
- package/components/svg/IconSettings.js +19 -21
- package/components/svg/IconSlice.js +19 -21
- package/components/svg/IconSubmit.js +19 -21
- package/components/svg/IconSubtract.js +21 -23
- package/components/svg/IconSynced.js +19 -21
- package/components/svg/IconTrashCan.js +19 -21
- package/components/svg/IconTreasureMap.js +19 -21
- package/components/svg/IconUnavailable.js +19 -21
- package/components/svg/IconUndo.js +19 -21
- package/components/svg/IconVariable.js +19 -21
- package/components/svg/IconVideo.js +30 -31
- package/components/svg/IconWifi.js +19 -21
- package/components/svg/Info.js +19 -21
- package/components/svg/Key.js +19 -21
- package/components/svg/Leaf.js +19 -21
- package/components/svg/Lock.js +19 -21
- package/components/svg/Logo.js +48 -43
- package/components/svg/MenuVertical.js +19 -21
- package/components/svg/Notification.js +19 -21
- package/components/svg/Person.js +19 -21
- package/components/svg/Plus.js +19 -21
- package/components/svg/RestorationSite.js +19 -21
- package/components/svg/Search.js +19 -21
- package/components/svg/Seeds.js +19 -21
- package/components/svg/Site.js +19 -21
- package/components/svg/Sparkles.js +19 -21
- package/components/svg/Species.js +19 -21
- package/components/svg/Species2.js +202 -164
- package/components/svg/Spinner.js +54 -48
- package/components/svg/Star.js +19 -21
- package/components/svg/Success.js +19 -21
- package/components/svg/SuccessFilled.js +19 -21
- package/components/svg/TerrawareLogoDesktop.js +55 -49
- package/components/svg/TerrawareLogoMobile.js +25 -26
- package/components/svg/Touchscreen.js +19 -21
- package/components/svg/UploadCloud.js +202 -164
- package/components/svg/Warning.js +19 -21
- package/components/svg/WelcomeClipboard.js +156 -133
- package/components/svg/index.js +129 -129
- package/components/table/EnhancedTableToolbar.js +68 -74
- package/components/table/EnhancedTableToolbarV2.js +31 -41
- package/components/table/TableCellRenderer.js +196 -231
- package/components/table/TableHeader.js +71 -73
- package/components/table/TableHeaderItem.js +97 -102
- package/components/table/density.js +18 -19
- package/components/table/index.js +350 -369
- package/components/table/sort.js +27 -52
- package/components/table/types.js +0 -1
- package/components/types/index.js +0 -1
- package/hooks/cameraEntityContext.js +2 -13
- package/hooks/useBoolean.js +17 -13
- package/hooks/useCameraPosition.js +56 -56
- package/hooks/useDevicePerformance.js +17 -17
- package/hooks/useXr.js +85 -87
- package/index.js +58 -58
- package/license-report.html +1 -1
- package/package.json +3 -3
- package/setupTests.js +0 -4
- package/strings/export.js +111 -170
- package/strings/index.js +25 -54
- package/strings/locales.js +21 -41
- package/strings/strings-en.js +49 -51
- package/strings/strings-es.js +49 -51
- package/strings/strings-fr.js +49 -51
- package/strings/strings-gx.js +49 -51
- package/style-dictionary-dist/TerrawareTheme.js +186 -186
- package/theme.js +74 -76
- package/utils/color.js +8 -15
- package/utils/date.js +42 -108
- package/utils/index.js +4 -4
- package/utils/preventDefaultEvent.js +3 -3
- package/utils/text.js +11 -11
- package/utils/useDeviceInfo.js +42 -79
- package/virtualWalkthrough.js +23 -23
|
@@ -1,109 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { computeGroundPlane, yOnPlane } from
|
|
3
|
-
import { distanceToCircleEdge } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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 =
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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 };
|