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