@terraware/web-components 5.0.27 → 5.1.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/package.json +1 -1
- 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,33 +1,15 @@
|
|
|
1
|
-
/** Deflection the stick must pass before the text moves, so a resting thumb doesn't drift it. */
|
|
2
1
|
const SCROLL_DEADZONE = 0.15;
|
|
3
|
-
|
|
4
|
-
/** Canvas px per second at full deflection. */
|
|
5
2
|
const SCROLL_SPEED = 1200;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
const readScrollAxis = inputSource => {
|
|
12
|
-
const axes = inputSource.gamepad?.axes;
|
|
13
|
-
if (inputSource.handedness !== 'right' || !axes || axes.length < 4) {
|
|
14
|
-
return 0;
|
|
15
|
-
}
|
|
16
|
-
return axes[3];
|
|
3
|
+
const readScrollAxis = (inputSource)=>{
|
|
4
|
+
const axes = inputSource.gamepad?.axes;
|
|
5
|
+
if ('right' !== inputSource.handedness || !axes || axes.length < 4) return 0;
|
|
6
|
+
return axes[3];
|
|
17
7
|
};
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const magnitude = Math.abs(axisY);
|
|
25
|
-
if (magnitude <= SCROLL_DEADZONE) {
|
|
26
|
-
return Math.min(Math.max(scrollY, 0), maxScroll);
|
|
27
|
-
}
|
|
28
|
-
const ramped = (magnitude - SCROLL_DEADZONE) / (1 - SCROLL_DEADZONE);
|
|
29
|
-
const delta = -Math.sign(axisY) * ramped * SCROLL_SPEED * dt;
|
|
30
|
-
return Math.min(Math.max(scrollY + delta, 0), maxScroll);
|
|
8
|
+
const nextScrollY = (scrollY, axisY, dt, maxScroll)=>{
|
|
9
|
+
const magnitude = Math.abs(axisY);
|
|
10
|
+
if (magnitude <= SCROLL_DEADZONE) return Math.min(Math.max(scrollY, 0), maxScroll);
|
|
11
|
+
const ramped = (magnitude - SCROLL_DEADZONE) / (1 - SCROLL_DEADZONE);
|
|
12
|
+
const delta = -Math.sign(axisY) * ramped * SCROLL_SPEED * dt;
|
|
13
|
+
return Math.min(Math.max(scrollY + delta, 0), maxScroll);
|
|
31
14
|
};
|
|
32
|
-
|
|
33
15
|
export { SCROLL_DEADZONE, SCROLL_SPEED, nextScrollY, readScrollAxis };
|
|
@@ -1,348 +1,223 @@
|
|
|
1
|
-
import { Quat,
|
|
2
|
-
import { computeGroundPlane, yOnPlane } from
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* First-person walkthrough camera for Gaussian Splat scenes.
|
|
6
|
-
*
|
|
7
|
-
* - Pointer drag (mouse or touch) rotates the camera (pitch + yaw) with damping
|
|
8
|
-
* matching PlayCanvas CameraControls' rotateDamping=0.98.
|
|
9
|
-
* - WASD / arrow keys move along the XZ plane using yaw-only direction, so
|
|
10
|
-
* movement is always horizontal regardless of where the camera is pointing.
|
|
11
|
-
* - Mouse wheel moves forward/backward along the XZ plane.
|
|
12
|
-
* - Y position is clamped between boundsMin.y and boundsMax.y; set them equal
|
|
13
|
-
* to lock vertical position entirely.
|
|
14
|
-
* - Exposes reset() and focusPoint to stay compatible with useCameraPosition.
|
|
15
|
-
*
|
|
16
|
-
* The pose is written to the camera under this script's entity rather than to the entity itself, so
|
|
17
|
-
* mounting it on the walkthrough's rig drives whichever camera the rig is holding — one the viewer
|
|
18
|
-
* brought, or one it adopted from the host scene. Mounted straight onto a camera entity it still
|
|
19
|
-
* drives that entity, which `findComponent` resolves to itself.
|
|
20
|
-
*/
|
|
21
|
-
|
|
1
|
+
import { Quat, Script, Vec3, math } from "playcanvas";
|
|
2
|
+
import { computeGroundPlane, yOnPlane } from "./groundPlane.js";
|
|
22
3
|
const _quat = new Quat();
|
|
23
4
|
const _flatForward = new Vec3();
|
|
24
5
|
const _flatRight = new Vec3();
|
|
25
|
-
|
|
26
|
-
/** Frame-rate-independent damping lerp rate — matches PlayCanvas CameraControls. */
|
|
27
|
-
const dampRate = (damping, dt) => 1 - Math.pow(damping, dt * 1000);
|
|
28
|
-
|
|
29
|
-
/** Below this angular delta (degrees) the damped lerp snaps to its target and stops writing. */
|
|
6
|
+
const dampRate = (damping, dt)=>1 - Math.pow(damping, 1000 * dt);
|
|
30
7
|
const ANGLE_EPSILON = 1e-3;
|
|
31
|
-
|
|
32
|
-
/** Below this positional delta (world units) an idle frame skips the transform write. */
|
|
33
8
|
const POSITION_EPSILON = 1e-5;
|
|
34
9
|
class WalkthroughCamera extends Script {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/** @attribute */
|
|
69
|
-
enableFly = true;
|
|
70
|
-
|
|
71
|
-
/** @attribute */
|
|
72
|
-
averageCameraHeight = 0;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Three world-space points defining the ground plane.
|
|
76
|
-
* When provided, the camera Y position is derived from this plane during non-freeFly movement.
|
|
77
|
-
*
|
|
78
|
-
* Not a React prop — must be set directly on the script instance via useEffect. The @playcanvas/react
|
|
79
|
-
* Script component is wrapped in memo() whose shallowEquals comparator returns early on the first prop
|
|
80
|
-
* with a .equals() method (Vec3), so any prop listed after boundsCenter is never compared and the
|
|
81
|
-
* component won't re-render when it changes. Set via:
|
|
82
|
-
* cameraRoot.script.walkthroughCamera.groundPlane = points
|
|
83
|
-
* Should be updated if shallowEquals is fixed (see https://github.com/playcanvas/react/pull/298)
|
|
84
|
-
*/
|
|
85
|
-
groundPlane = [];
|
|
86
|
-
|
|
87
|
-
// Current (damped) angles applied to the entity each frame.
|
|
88
|
-
_pitch = 0;
|
|
89
|
-
_yaw = 0;
|
|
90
|
-
|
|
91
|
-
// Target angles — updated immediately on pointer move, entity lerps toward them.
|
|
92
|
-
_targetPitch = 0;
|
|
93
|
-
_targetYaw = 0;
|
|
94
|
-
_isDragging = false;
|
|
95
|
-
_lastX = 0;
|
|
96
|
-
_lastY = 0;
|
|
97
|
-
_scrollDelta = 0;
|
|
98
|
-
_keys = {};
|
|
99
|
-
_removeListeners = [];
|
|
100
|
-
|
|
101
|
-
// The camera being posed, resolved lazily. Held until it exists: a camera adopted from the host
|
|
102
|
-
// scene is reparented into the rig by an effect that runs after the scripts are mounted.
|
|
103
|
-
_camera = null;
|
|
104
|
-
|
|
105
|
-
// Cached plane derived from groundPlane attribute. Computed once when 3 points become available.
|
|
106
|
-
_planeNormal = new Vec3(0, 1, 0);
|
|
107
|
-
_planePoint = new Vec3(0, 0, 0);
|
|
108
|
-
_hasGroundPlane = false;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* The camera to pose, or null while there is none to pose. The entity this script is mounted on is
|
|
112
|
-
* never posed in its place: on the walkthrough that is the rig, which is the frame an XR session
|
|
113
|
-
* is measured from and whose height the session's floor is taken from.
|
|
114
|
-
*/
|
|
115
|
-
_posedCamera() {
|
|
116
|
-
this._camera ??= this.entity.findComponent('camera')?.entity ?? null;
|
|
117
|
-
return this._camera;
|
|
118
|
-
}
|
|
119
|
-
_syncGroundPlane() {
|
|
120
|
-
if (this._hasGroundPlane || this.groundPlane.length < 3) {
|
|
121
|
-
return;
|
|
10
|
+
static scriptName = 'walkthroughCamera';
|
|
11
|
+
moveSpeed = 4.5;
|
|
12
|
+
moveFastSpeed = 7.5;
|
|
13
|
+
moveSlowSpeed = 2.25;
|
|
14
|
+
lookSensitivity = 0.1;
|
|
15
|
+
rotateDamping = 0.98;
|
|
16
|
+
pitchMin = -85;
|
|
17
|
+
pitchMax = 85;
|
|
18
|
+
scrollSpeed = 0.001;
|
|
19
|
+
boundsCenter = new Vec3(0, 0, 0);
|
|
20
|
+
boundsRadius = 10;
|
|
21
|
+
freeFly = false;
|
|
22
|
+
enableFly = true;
|
|
23
|
+
averageCameraHeight = 0;
|
|
24
|
+
groundPlane = [];
|
|
25
|
+
_pitch = 0;
|
|
26
|
+
_yaw = 0;
|
|
27
|
+
_targetPitch = 0;
|
|
28
|
+
_targetYaw = 0;
|
|
29
|
+
_isDragging = false;
|
|
30
|
+
_lastX = 0;
|
|
31
|
+
_lastY = 0;
|
|
32
|
+
_scrollDelta = 0;
|
|
33
|
+
_keys = {};
|
|
34
|
+
_removeListeners = [];
|
|
35
|
+
_camera = null;
|
|
36
|
+
_planeNormal = new Vec3(0, 1, 0);
|
|
37
|
+
_planePoint = new Vec3(0, 0, 0);
|
|
38
|
+
_hasGroundPlane = false;
|
|
39
|
+
_posedCamera() {
|
|
40
|
+
this._camera ??= this.entity.findComponent('camera')?.entity ?? null;
|
|
41
|
+
return this._camera;
|
|
122
42
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
43
|
+
_syncGroundPlane() {
|
|
44
|
+
if (this._hasGroundPlane || this.groundPlane.length < 3) return;
|
|
45
|
+
const plane = computeGroundPlane(this.groundPlane);
|
|
46
|
+
if (!plane) return;
|
|
47
|
+
this._planeNormal.copy(plane.normal);
|
|
48
|
+
this._planePoint.copy(plane.point);
|
|
49
|
+
this._hasGroundPlane = true;
|
|
126
50
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
this._removeListeners = [() => canvas.removeEventListener('pointerdown', onPointerDown), () => canvas.removeEventListener('pointermove', onPointerMove), () => canvas.removeEventListener('pointerup', onPointerUp), () => canvas.removeEventListener('pointercancel', onPointerUp), () => canvas.removeEventListener('wheel', onWheel), () => canvas.removeEventListener('contextmenu', onContextMenu), () => window.removeEventListener('keydown', onKeyDown), () => window.removeEventListener('keyup', onKeyUp), () => window.removeEventListener('blur', onBlur)];
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Position the camera at `position` looking toward `focus`.
|
|
198
|
-
* Compatible with the useCameraPosition hook's reset() call.
|
|
199
|
-
*/
|
|
200
|
-
reset(focus, position) {
|
|
201
|
-
const camera = this._posedCamera();
|
|
202
|
-
camera?.setPosition(position);
|
|
203
|
-
const dx = focus.x - position.x;
|
|
204
|
-
const dy = focus.y - position.y;
|
|
205
|
-
const dz = focus.z - position.z;
|
|
206
|
-
const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
207
|
-
if (len > 0) {
|
|
208
|
-
// PlayCanvas uses right-handed CCW Y rotation: forward = (-sinθ, 0, -cosθ),
|
|
209
|
-
// so to look toward (dx, 0, dz): θ = atan2(-dx, -dz).
|
|
210
|
-
this._yaw = Math.atan2(-dx, -dz) * (180 / Math.PI);
|
|
211
|
-
this._pitch = Math.atan2(-dy, Math.sqrt(dx * dx + dz * dz)) * (180 / Math.PI);
|
|
212
|
-
this._pitch = math.clamp(this._pitch, this.pitchMin, this.pitchMax);
|
|
51
|
+
initialize() {
|
|
52
|
+
this._pitch = 0;
|
|
53
|
+
this._yaw = 0;
|
|
54
|
+
this._targetPitch = 0;
|
|
55
|
+
this._targetYaw = 0;
|
|
56
|
+
const canvas = this.app.graphicsDevice.canvas;
|
|
57
|
+
const onPointerDown = (e)=>{
|
|
58
|
+
this._isDragging = true;
|
|
59
|
+
this._lastX = e.clientX;
|
|
60
|
+
this._lastY = e.clientY;
|
|
61
|
+
try {
|
|
62
|
+
canvas.setPointerCapture(e.pointerId);
|
|
63
|
+
} catch {}
|
|
64
|
+
};
|
|
65
|
+
const onPointerMove = (e)=>{
|
|
66
|
+
if (!this._isDragging) return;
|
|
67
|
+
const dx = e.clientX - this._lastX;
|
|
68
|
+
const dy = e.clientY - this._lastY;
|
|
69
|
+
this._lastX = e.clientX;
|
|
70
|
+
this._lastY = e.clientY;
|
|
71
|
+
this._targetYaw -= dx * this.lookSensitivity;
|
|
72
|
+
this._targetPitch = math.clamp(this._targetPitch - dy * this.lookSensitivity, this.pitchMin, this.pitchMax);
|
|
73
|
+
};
|
|
74
|
+
const onPointerUp = (e)=>{
|
|
75
|
+
this._isDragging = false;
|
|
76
|
+
try {
|
|
77
|
+
canvas.releasePointerCapture(e.pointerId);
|
|
78
|
+
} catch {}
|
|
79
|
+
};
|
|
80
|
+
const onWheel = (e)=>{
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
this._scrollDelta += e.deltaY;
|
|
83
|
+
};
|
|
84
|
+
const onContextMenu = (e)=>e.preventDefault();
|
|
85
|
+
const onKeyDown = (e)=>{
|
|
86
|
+
this._keys[e.code] = true;
|
|
87
|
+
};
|
|
88
|
+
const onKeyUp = (e)=>{
|
|
89
|
+
this._keys[e.code] = false;
|
|
90
|
+
};
|
|
91
|
+
const onBlur = ()=>{
|
|
92
|
+
this._keys = {};
|
|
93
|
+
this._isDragging = false;
|
|
94
|
+
};
|
|
95
|
+
canvas.addEventListener('pointerdown', onPointerDown);
|
|
96
|
+
canvas.addEventListener('pointermove', onPointerMove);
|
|
97
|
+
canvas.addEventListener('pointerup', onPointerUp);
|
|
98
|
+
canvas.addEventListener('pointercancel', onPointerUp);
|
|
99
|
+
canvas.addEventListener('wheel', onWheel, {
|
|
100
|
+
passive: false
|
|
101
|
+
});
|
|
102
|
+
canvas.addEventListener('contextmenu', onContextMenu);
|
|
103
|
+
window.addEventListener('keydown', onKeyDown);
|
|
104
|
+
window.addEventListener('keyup', onKeyUp);
|
|
105
|
+
window.addEventListener('blur', onBlur);
|
|
106
|
+
this._removeListeners = [
|
|
107
|
+
()=>canvas.removeEventListener('pointerdown', onPointerDown),
|
|
108
|
+
()=>canvas.removeEventListener('pointermove', onPointerMove),
|
|
109
|
+
()=>canvas.removeEventListener('pointerup', onPointerUp),
|
|
110
|
+
()=>canvas.removeEventListener('pointercancel', onPointerUp),
|
|
111
|
+
()=>canvas.removeEventListener('wheel', onWheel),
|
|
112
|
+
()=>canvas.removeEventListener('contextmenu', onContextMenu),
|
|
113
|
+
()=>window.removeEventListener('keydown', onKeyDown),
|
|
114
|
+
()=>window.removeEventListener('keyup', onKeyUp),
|
|
115
|
+
()=>window.removeEventListener('blur', onBlur)
|
|
116
|
+
];
|
|
213
117
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
* Called by AutoRotator
|
|
230
|
-
*/
|
|
231
|
-
orbitStep(yawDelta) {
|
|
232
|
-
this._syncGroundPlane();
|
|
233
|
-
const camera = this._posedCamera();
|
|
234
|
-
if (!camera) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
const pos = camera.getPosition();
|
|
238
|
-
const dx = pos.x - this.boundsCenter.x;
|
|
239
|
-
const dz = pos.z - this.boundsCenter.z;
|
|
240
|
-
const radius = Math.sqrt(dx * dx + dz * dz) || this.boundsRadius * 0.5;
|
|
241
|
-
const currentAngle = Math.atan2(dz, dx);
|
|
242
|
-
const newAngle = currentAngle + yawDelta * Math.PI / 180;
|
|
243
|
-
const nx = this.boundsCenter.x + radius * Math.cos(newAngle);
|
|
244
|
-
const nz = this.boundsCenter.z + radius * Math.sin(newAngle);
|
|
245
|
-
const ny = this._hasGroundPlane ? yOnPlane(nx, nz, this._planeNormal, this._planePoint, this.boundsCenter.y) + this.averageCameraHeight : this.boundsCenter.y;
|
|
246
|
-
camera.setPosition(nx, ny, nz);
|
|
247
|
-
const faceDx = this.boundsCenter.x - nx;
|
|
248
|
-
const faceDz = this.boundsCenter.z - nz;
|
|
249
|
-
this._yaw = Math.atan2(-faceDx, -faceDz) * (180 / Math.PI);
|
|
250
|
-
this._targetYaw = this._yaw;
|
|
251
|
-
camera.setEulerAngles(this._pitch, this._yaw, 0);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Returns the point directly in front of the camera.
|
|
256
|
-
* Compatible with the useCameraPosition hook's focusPoint read.
|
|
257
|
-
*/
|
|
258
|
-
get focusPoint() {
|
|
259
|
-
const pos = (this._posedCamera() ?? this.entity).getPosition();
|
|
260
|
-
_quat.setFromEulerAngles(this._pitch, this._yaw, 0);
|
|
261
|
-
_quat.transformVector(Vec3.FORWARD, _flatForward);
|
|
262
|
-
return new Vec3(pos.x + _flatForward.x, pos.y + _flatForward.y, pos.z + _flatForward.z);
|
|
263
|
-
}
|
|
264
|
-
update(dt) {
|
|
265
|
-
this._syncGroundPlane();
|
|
266
|
-
const camera = this._posedCamera();
|
|
267
|
-
if (!camera) {
|
|
268
|
-
return;
|
|
118
|
+
reset(focus, position) {
|
|
119
|
+
const camera = this._posedCamera();
|
|
120
|
+
camera?.setPosition(position);
|
|
121
|
+
const dx = focus.x - position.x;
|
|
122
|
+
const dy = focus.y - position.y;
|
|
123
|
+
const dz = focus.z - position.z;
|
|
124
|
+
const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
125
|
+
if (len > 0) {
|
|
126
|
+
this._yaw = 180 / Math.PI * Math.atan2(-dx, -dz);
|
|
127
|
+
this._pitch = 180 / Math.PI * Math.atan2(-dy, Math.sqrt(dx * dx + dz * dz));
|
|
128
|
+
this._pitch = math.clamp(this._pitch, this.pitchMin, this.pitchMax);
|
|
129
|
+
}
|
|
130
|
+
this._targetPitch = this._pitch;
|
|
131
|
+
this._targetYaw = this._yaw;
|
|
132
|
+
camera?.setEulerAngles(this._pitch, this._yaw, 0);
|
|
269
133
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
// Snap to the target once within ANGLE_EPSILON so the asymptotic lerp actually
|
|
273
|
-
// settles instead of emitting ever-smaller micro-rotations for ~0.4s after each
|
|
274
|
-
// drag. The gsplat sorter guards its own re-sorts with an epsilon, so this mainly
|
|
275
|
-
// trims that sort tail; it does not affect per-frame rendering (autoRender does).
|
|
276
|
-
const r = dampRate(this.rotateDamping, dt);
|
|
277
|
-
let nextPitch = this._pitch + (this._targetPitch - this._pitch) * r;
|
|
278
|
-
let nextYaw = this._yaw + (this._targetYaw - this._yaw) * r;
|
|
279
|
-
if (Math.abs(this._targetPitch - nextPitch) < ANGLE_EPSILON) {
|
|
280
|
-
nextPitch = this._targetPitch;
|
|
134
|
+
get currentYaw() {
|
|
135
|
+
return this._targetYaw;
|
|
281
136
|
}
|
|
282
|
-
|
|
283
|
-
|
|
137
|
+
get currentPitch() {
|
|
138
|
+
return this._targetPitch;
|
|
284
139
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
140
|
+
orbitStep(yawDelta) {
|
|
141
|
+
this._syncGroundPlane();
|
|
142
|
+
const camera = this._posedCamera();
|
|
143
|
+
if (!camera) return;
|
|
144
|
+
const pos = camera.getPosition();
|
|
145
|
+
const dx = pos.x - this.boundsCenter.x;
|
|
146
|
+
const dz = pos.z - this.boundsCenter.z;
|
|
147
|
+
const radius = Math.sqrt(dx * dx + dz * dz) || 0.5 * this.boundsRadius;
|
|
148
|
+
const currentAngle = Math.atan2(dz, dx);
|
|
149
|
+
const newAngle = currentAngle + yawDelta * Math.PI / 180;
|
|
150
|
+
const nx = this.boundsCenter.x + radius * Math.cos(newAngle);
|
|
151
|
+
const nz = this.boundsCenter.z + radius * Math.sin(newAngle);
|
|
152
|
+
const ny = this._hasGroundPlane ? yOnPlane(nx, nz, this._planeNormal, this._planePoint, this.boundsCenter.y) + this.averageCameraHeight : this.boundsCenter.y;
|
|
153
|
+
camera.setPosition(nx, ny, nz);
|
|
154
|
+
const faceDx = this.boundsCenter.x - nx;
|
|
155
|
+
const faceDz = this.boundsCenter.z - nz;
|
|
156
|
+
this._yaw = 180 / Math.PI * Math.atan2(-faceDx, -faceDz);
|
|
157
|
+
this._targetYaw = this._yaw;
|
|
158
|
+
camera.setEulerAngles(this._pitch, this._yaw, 0);
|
|
294
159
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
let nz = pos.z;
|
|
299
|
-
if (this.enableFly) {
|
|
300
|
-
const fast = this._keys.ShiftLeft || this._keys.ShiftRight;
|
|
301
|
-
const slow = this._keys.ControlLeft || this._keys.ControlRight;
|
|
302
|
-
const speed = (fast ? this.moveFastSpeed : slow ? this.moveSlowSpeed : this.moveSpeed) * dt;
|
|
303
|
-
const fwd = (this._keys.KeyW || this._keys.ArrowUp ? 1 : 0) - (this._keys.KeyS || this._keys.ArrowDown ? 1 : 0);
|
|
304
|
-
const strafe = (this._keys.KeyD || this._keys.ArrowRight ? 1 : 0) - (this._keys.KeyA || this._keys.ArrowLeft ? 1 : 0);
|
|
305
|
-
const scrollFwd = -this._scrollDelta * this.scrollSpeed;
|
|
306
|
-
if (fwd !== 0 || strafe !== 0 || scrollFwd !== 0) {
|
|
307
|
-
// Use yaw-only rotation so movement is always horizontal.
|
|
308
|
-
_quat.setFromEulerAngles(0, this._yaw, 0);
|
|
160
|
+
get focusPoint() {
|
|
161
|
+
const pos = (this._posedCamera() ?? this.entity).getPosition();
|
|
162
|
+
_quat.setFromEulerAngles(this._pitch, this._yaw, 0);
|
|
309
163
|
_quat.transformVector(Vec3.FORWARD, _flatForward);
|
|
310
|
-
|
|
311
|
-
nx += _flatForward.x * (fwd * speed + scrollFwd) + _flatRight.x * strafe * speed;
|
|
312
|
-
nz += _flatForward.z * (fwd * speed + scrollFwd) + _flatRight.z * strafe * speed;
|
|
313
|
-
}
|
|
314
|
-
if (this.freeFly) {
|
|
315
|
-
const up = (this._keys.KeyE ? 1 : 0) - (this._keys.KeyQ ? 1 : 0);
|
|
316
|
-
if (up !== 0) {
|
|
317
|
-
ny += up * speed;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
164
|
+
return new Vec3(pos.x + _flatForward.x, pos.y + _flatForward.y, pos.z + _flatForward.z);
|
|
320
165
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
166
|
+
update(dt) {
|
|
167
|
+
this._syncGroundPlane();
|
|
168
|
+
const camera = this._posedCamera();
|
|
169
|
+
if (!camera) return;
|
|
170
|
+
const r = dampRate(this.rotateDamping, dt);
|
|
171
|
+
let nextPitch = this._pitch + (this._targetPitch - this._pitch) * r;
|
|
172
|
+
let nextYaw = this._yaw + (this._targetYaw - this._yaw) * r;
|
|
173
|
+
if (Math.abs(this._targetPitch - nextPitch) < ANGLE_EPSILON) nextPitch = this._targetPitch;
|
|
174
|
+
if (Math.abs(this._targetYaw - nextYaw) < ANGLE_EPSILON) nextYaw = this._targetYaw;
|
|
175
|
+
nextPitch = math.clamp(nextPitch, this.pitchMin, this.pitchMax);
|
|
176
|
+
const anglesChanged = nextPitch !== this._pitch || nextYaw !== this._yaw;
|
|
177
|
+
this._pitch = nextPitch;
|
|
178
|
+
this._yaw = nextYaw;
|
|
179
|
+
if (anglesChanged) camera.setEulerAngles(this._pitch, this._yaw, 0);
|
|
180
|
+
const pos = camera.getPosition();
|
|
181
|
+
let nx = pos.x;
|
|
182
|
+
let ny = pos.y;
|
|
183
|
+
let nz = pos.z;
|
|
184
|
+
if (this.enableFly) {
|
|
185
|
+
const fast = this._keys.ShiftLeft || this._keys.ShiftRight;
|
|
186
|
+
const slow = this._keys.ControlLeft || this._keys.ControlRight;
|
|
187
|
+
const speed = (fast ? this.moveFastSpeed : slow ? this.moveSlowSpeed : this.moveSpeed) * dt;
|
|
188
|
+
const fwd = (this._keys.KeyW || this._keys.ArrowUp ? 1 : 0) - (this._keys.KeyS || this._keys.ArrowDown ? 1 : 0);
|
|
189
|
+
const strafe = (this._keys.KeyD || this._keys.ArrowRight ? 1 : 0) - (this._keys.KeyA || this._keys.ArrowLeft ? 1 : 0);
|
|
190
|
+
const scrollFwd = -this._scrollDelta * this.scrollSpeed;
|
|
191
|
+
if (0 !== fwd || 0 !== strafe || 0 !== scrollFwd) {
|
|
192
|
+
_quat.setFromEulerAngles(0, this._yaw, 0);
|
|
193
|
+
_quat.transformVector(Vec3.FORWARD, _flatForward);
|
|
194
|
+
_quat.transformVector(Vec3.RIGHT, _flatRight);
|
|
195
|
+
nx += _flatForward.x * (fwd * speed + scrollFwd) + _flatRight.x * strafe * speed;
|
|
196
|
+
nz += _flatForward.z * (fwd * speed + scrollFwd) + _flatRight.z * strafe * speed;
|
|
197
|
+
}
|
|
198
|
+
if (this.freeFly) {
|
|
199
|
+
const up = (this._keys.KeyE ? 1 : 0) - (this._keys.KeyQ ? 1 : 0);
|
|
200
|
+
if (0 !== up) ny += up * speed;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
this._scrollDelta = 0;
|
|
204
|
+
if (!this.freeFly) {
|
|
205
|
+
const cdx = nx - this.boundsCenter.x;
|
|
206
|
+
const cdz = nz - this.boundsCenter.z;
|
|
207
|
+
const dist = Math.sqrt(cdx * cdx + cdz * cdz);
|
|
208
|
+
if (dist > this.boundsRadius) {
|
|
209
|
+
const scale = this.boundsRadius / dist;
|
|
210
|
+
nx = this.boundsCenter.x + cdx * scale;
|
|
211
|
+
nz = this.boundsCenter.z + cdz * scale;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const groundY = this._hasGroundPlane ? yOnPlane(nx, nz, this._planeNormal, this._planePoint, this.boundsCenter.y) + this.averageCameraHeight : this.boundsCenter.y;
|
|
215
|
+
const targetY = this.freeFly ? ny : groundY;
|
|
216
|
+
if (Math.abs(nx - pos.x) > POSITION_EPSILON || Math.abs(targetY - pos.y) > POSITION_EPSILON || Math.abs(nz - pos.z) > POSITION_EPSILON) camera.setPosition(nx, targetY, nz);
|
|
332
217
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
// idle frames; note it does not stop per-frame rasterization — while autoRender is on
|
|
337
|
-
// the app renders every frame regardless of whether the transform changed.
|
|
338
|
-
if (Math.abs(nx - pos.x) > POSITION_EPSILON || Math.abs(targetY - pos.y) > POSITION_EPSILON || Math.abs(nz - pos.z) > POSITION_EPSILON) {
|
|
339
|
-
camera.setPosition(nx, targetY, nz);
|
|
218
|
+
destroy() {
|
|
219
|
+
this._removeListeners.forEach((fn)=>fn());
|
|
220
|
+
this._removeListeners = [];
|
|
340
221
|
}
|
|
341
|
-
}
|
|
342
|
-
destroy() {
|
|
343
|
-
this._removeListeners.forEach(fn => fn());
|
|
344
|
-
this._removeListeners = [];
|
|
345
|
-
}
|
|
346
222
|
}
|
|
347
|
-
|
|
348
223
|
export { WalkthroughCamera };
|