@terraware/web-components 4.2.13 → 4.2.14-rc.1
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 +8 -14
- package/components/Autocomplete/Autocomplete.js +67 -70
- package/components/Badge/index.js +16 -20
- package/components/BusySpinner/index.js +30 -35
- package/components/Button/Button.js +23 -27
- package/components/Button/button.test.js +8 -10
- package/components/Checkbox.js +10 -19
- package/components/Confirm/index.js +44 -51
- package/components/DatePicker/DatePicker.js +116 -117
- package/components/DialogBox/DialogBox.js +76 -66
- package/components/Divisor.js +15 -20
- package/components/Dropdown.js +54 -61
- package/components/EditableTable/index.js +129 -135
- package/components/ErrorBox/ErrorBox.js +37 -36
- package/components/FileChooser/index.js +73 -77
- package/components/FormBottomBar/index.js +63 -69
- package/components/Icon/Icon.js +14 -21
- package/components/Icon/icons/index.js +251 -257
- package/components/IconTooltip/index.js +29 -34
- package/components/Map/MapBox.js +199 -220
- package/components/Map/MapContainer.js +14 -18
- package/components/Map/MapDateSliderControl.js +44 -46
- package/components/Map/MapDrawer.js +32 -33
- package/components/Map/MapLegend.js +96 -95
- package/components/Map/MapViewStyleControl.js +38 -48
- package/components/Map/ReactMapboxWrapper.js +2 -9
- package/components/Map/index.js +47 -55
- package/components/Map/types.js +2 -8
- package/components/Map/useMaintainLayerOrder.js +4 -11
- package/components/Markdown/index.js +14 -21
- package/components/Message/Message.js +59 -51
- package/components/MultiSelect/MultiSelect.test.js +22 -24
- package/components/MultiSelect/index.js +73 -69
- package/components/Navbar/NavFooter.js +8 -13
- package/components/Navbar/NavItem.js +43 -37
- package/components/Navbar/NavSection.js +13 -17
- package/components/Navbar/Navbar.js +35 -38
- package/components/Navbar/SubNavbar.js +8 -13
- package/components/Note.js +14 -19
- package/components/OverlayModal/OverlayModal.js +36 -37
- package/components/PageForm/index.js +30 -35
- package/components/PhotoChooser/index.js +93 -98
- package/components/Pill/index.js +29 -31
- package/components/PillList/index.js +16 -22
- package/components/PlacementWrapper/index.js +20 -25
- package/components/PopoverMenu/Popover.js +33 -37
- package/components/PopoverMenu/index.js +41 -46
- package/components/PopoverMultiSelect/index.js +18 -21
- package/components/ProgressCircle/ProgressCircle.js +23 -26
- package/components/RadioButton.js +7 -14
- package/components/Select/Select.js +8 -13
- package/components/Select/SelectT.js +110 -108
- package/components/Separator/index.js +8 -14
- package/components/Size.js +1 -5
- package/components/Slider/index.js +7 -13
- package/components/SummaryBox.js +36 -39
- package/components/Tabs/index.js +75 -77
- package/components/TextTruncated/index.js +64 -65
- package/components/Textfield/Textfield.js +100 -95
- package/components/Textfield/TruncatedTextArea.js +44 -46
- package/components/TimelineSlider/TimelineMarker.js +24 -28
- package/components/TimelineSlider/TimelineRail.js +5 -11
- package/components/TimelineSlider/index.js +47 -51
- package/components/Tooltip/Tooltip.js +21 -26
- package/components/ViewPhotosDialog/index.js +58 -62
- package/components/VirtualWalkthrough/Annotation.d.ts +41 -0
- package/components/VirtualWalkthrough/Annotation.d.ts.map +1 -0
- package/components/VirtualWalkthrough/Annotation.js +197 -0
- package/components/VirtualWalkthrough/AnnotationEditPane.d.ts +20 -0
- package/components/VirtualWalkthrough/AnnotationEditPane.d.ts.map +1 -0
- package/components/VirtualWalkthrough/AnnotationEditPane.js +136 -0
- package/components/VirtualWalkthrough/AnnotationPanel.d.ts +13 -0
- package/components/VirtualWalkthrough/AnnotationPanel.d.ts.map +1 -0
- package/components/VirtualWalkthrough/AnnotationPanel.js +196 -0
- package/components/VirtualWalkthrough/Application.d.ts +9 -0
- package/components/VirtualWalkthrough/Application.d.ts.map +1 -0
- package/components/VirtualWalkthrough/Application.js +32 -0
- package/components/VirtualWalkthrough/AutoRotator.d.ts +109 -0
- package/components/VirtualWalkthrough/AutoRotator.d.ts.map +1 -0
- package/components/VirtualWalkthrough/AutoRotator.js +217 -0
- package/components/VirtualWalkthrough/BlockingSpinner.d.ts +3 -0
- package/components/VirtualWalkthrough/BlockingSpinner.d.ts.map +1 -0
- package/components/VirtualWalkthrough/BlockingSpinner.js +27 -0
- package/components/VirtualWalkthrough/BoundaryRing.d.ts +9 -0
- package/components/VirtualWalkthrough/BoundaryRing.d.ts.map +1 -0
- package/components/VirtualWalkthrough/BoundaryRing.js +33 -0
- package/components/VirtualWalkthrough/CameraInfo.d.ts +16 -0
- package/components/VirtualWalkthrough/CameraInfo.d.ts.map +1 -0
- package/components/VirtualWalkthrough/CameraInfo.js +88 -0
- package/components/VirtualWalkthrough/ControlsInfoPane.d.ts +37 -0
- package/components/VirtualWalkthrough/ControlsInfoPane.d.ts.map +1 -0
- package/components/VirtualWalkthrough/ControlsInfoPane.js +196 -0
- package/components/VirtualWalkthrough/GradientSky.d.ts +9 -0
- package/components/VirtualWalkthrough/GradientSky.d.ts.map +1 -0
- package/components/VirtualWalkthrough/GradientSky.js +73 -0
- package/components/VirtualWalkthrough/SplatControls.d.ts +50 -0
- package/components/VirtualWalkthrough/SplatControls.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatControls.js +252 -0
- package/components/VirtualWalkthrough/SplatCrop.d.ts +8 -0
- package/components/VirtualWalkthrough/SplatCrop.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatCrop.js +18 -0
- package/components/VirtualWalkthrough/SplatFadeCrop.d.ts +8 -0
- package/components/VirtualWalkthrough/SplatFadeCrop.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatFadeCrop.js +18 -0
- package/components/VirtualWalkthrough/SplatModel.d.ts +15 -0
- package/components/VirtualWalkthrough/SplatModel.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatModel.js +60 -0
- package/components/VirtualWalkthrough/SplatRevealRain.d.ts +18 -0
- package/components/VirtualWalkthrough/SplatRevealRain.d.ts.map +1 -0
- package/components/VirtualWalkthrough/SplatRevealRain.js +38 -0
- package/components/VirtualWalkthrough/TfAnnotationManager.d.ts +110 -0
- package/components/VirtualWalkthrough/TfAnnotationManager.d.ts.map +1 -0
- package/components/VirtualWalkthrough/TfAnnotationManager.js +368 -0
- package/components/VirtualWalkthrough/TfXrNavigation.d.ts +15 -0
- package/components/VirtualWalkthrough/TfXrNavigation.d.ts.map +1 -0
- package/components/VirtualWalkthrough/TfXrNavigation.js +24 -0
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts +27 -0
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.d.ts.map +1 -0
- package/components/VirtualWalkthrough/VirtualWalkthroughViewer.js +282 -0
- package/components/VirtualWalkthrough/annotation-styles.css +5 -0
- package/components/VirtualWalkthrough/application-styles.css +4 -0
- package/components/VirtualWalkthrough/boundary-ring.d.ts +47 -0
- package/components/VirtualWalkthrough/boundary-ring.d.ts.map +1 -0
- package/components/VirtualWalkthrough/boundary-ring.js +125 -0
- package/components/VirtualWalkthrough/boundary-ring.test.js +109 -0
- package/components/VirtualWalkthrough/groundPlane.d.ts +14 -0
- package/components/VirtualWalkthrough/groundPlane.d.ts.map +1 -0
- package/components/VirtualWalkthrough/groundPlane.js +47 -0
- package/components/VirtualWalkthrough/groundPlane.test.js +45 -0
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.d.ts +19 -0
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.d.ts.map +1 -0
- package/components/VirtualWalkthrough/gsplat-fade-crop-effect.js +119 -0
- package/components/VirtualWalkthrough/walkthrough-camera.d.ts +77 -0
- package/components/VirtualWalkthrough/walkthrough-camera.d.ts.map +1 -0
- package/components/VirtualWalkthrough/walkthrough-camera.js +320 -0
- package/components/svg/BlobbyGrayIconImage.js +36 -41
- package/components/svg/BlobbyGrayIconImport.js +36 -41
- package/components/svg/BlobbyGrayIconUploadToTheCloud.js +36 -41
- package/components/svg/BlobbyIconCloudChecked.js +161 -158
- package/components/svg/BlobbyIconExportOrUploadPhoto.js +161 -158
- package/components/svg/BlobbyIconFolder.js +161 -158
- package/components/svg/BlobbyIconGraphReport.js +161 -158
- package/components/svg/BlobbyIconHappy.js +161 -158
- package/components/svg/BlobbyIconHeartMonitor.js +161 -158
- package/components/svg/BlobbyIconLeaf.js +161 -158
- package/components/svg/BlobbyIconLibrary.js +161 -158
- package/components/svg/BlobbyIconNursery.js +163 -160
- package/components/svg/BlobbyIconOrganization.js +161 -158
- package/components/svg/BlobbyIconParchment.js +161 -158
- package/components/svg/BlobbyIconPeople.js +161 -158
- package/components/svg/BlobbyIconSeedBank.js +161 -158
- package/components/svg/BlobbyIconSeedling.js +161 -158
- package/components/svg/BlobbyIconSeeds.js +161 -158
- package/components/svg/BlobbyIconSite.js +164 -161
- package/components/svg/BlobbyIconSparkles.js +161 -158
- package/components/svg/BlobbyIconWrench.js +161 -158
- package/components/svg/Bug.js +18 -27
- package/components/svg/Calendar.js +18 -27
- package/components/svg/CaretDown.js +18 -27
- package/components/svg/CaretLeft.js +18 -27
- package/components/svg/CaretRight.js +18 -27
- package/components/svg/CaretUp.js +18 -27
- package/components/svg/ChevronDown.js +18 -27
- package/components/svg/ChevronUp.js +18 -27
- package/components/svg/Close.js +18 -27
- package/components/svg/Critical.js +18 -27
- package/components/svg/Edit.js +161 -158
- package/components/svg/Error.js +18 -27
- package/components/svg/Filter.js +18 -27
- package/components/svg/Folder.js +18 -27
- package/components/svg/Help.js +18 -27
- package/components/svg/Home.js +18 -27
- package/components/svg/IconAdd.js +18 -27
- package/components/svg/IconArrowRight.js +19 -28
- package/components/svg/IconBusinessNetwork.js +18 -27
- package/components/svg/IconCalendar.js +18 -27
- package/components/svg/IconCancel.js +18 -27
- package/components/svg/IconChargingBattery.js +18 -27
- package/components/svg/IconChecklist.js +19 -28
- package/components/svg/IconCheckmark.js +18 -27
- package/components/svg/IconCoinInHand.js +18 -27
- package/components/svg/IconColumns.js +18 -27
- package/components/svg/IconComment.js +18 -27
- package/components/svg/IconDashboard.js +18 -27
- package/components/svg/IconDataMigration.js +18 -27
- package/components/svg/IconDocument.js +19 -28
- package/components/svg/IconDownloadFromTheCloud.js +19 -28
- package/components/svg/IconEdit.js +19 -28
- package/components/svg/IconExpand.js +19 -28
- package/components/svg/IconExport.js +18 -27
- package/components/svg/IconExternalLink.js +18 -27
- package/components/svg/IconEye.js +18 -27
- package/components/svg/IconEyeOff.js +18 -27
- package/components/svg/IconFile.js +18 -27
- package/components/svg/IconFolder.js +18 -27
- package/components/svg/IconFullScreen.js +18 -27
- package/components/svg/IconFutures.js +18 -27
- package/components/svg/IconGraphReport.js +18 -27
- package/components/svg/IconHeartMonitor.js +18 -27
- package/components/svg/IconHelp.js +19 -28
- package/components/svg/IconHistory.js +18 -27
- package/components/svg/IconImport.js +18 -27
- package/components/svg/IconIndex.js +18 -27
- package/components/svg/IconLayers.js +18 -27
- package/components/svg/IconLibrary.js +18 -27
- package/components/svg/IconList.js +18 -27
- package/components/svg/IconLivePlant.js +18 -27
- package/components/svg/IconLocations.js +18 -27
- package/components/svg/IconMail.js +18 -27
- package/components/svg/IconManager.js +18 -27
- package/components/svg/IconMarker.js +18 -27
- package/components/svg/IconMenu.js +18 -27
- package/components/svg/IconMenuHorizontal.js +18 -27
- package/components/svg/IconModule.js +18 -27
- package/components/svg/IconMyLocation.js +18 -27
- package/components/svg/IconNote.js +19 -28
- package/components/svg/IconNursery.js +18 -27
- package/components/svg/IconOrg.js +18 -27
- package/components/svg/IconParchment.js +18 -27
- package/components/svg/IconPhoto.js +18 -27
- package/components/svg/IconPlantsFilled.js +18 -27
- package/components/svg/IconRedo.js +18 -27
- package/components/svg/IconRestorationSite.js +18 -27
- package/components/svg/IconSeedBank.js +18 -27
- package/components/svg/IconSeedling.js +18 -27
- package/components/svg/IconSettings.js +18 -27
- package/components/svg/IconSlice.js +18 -27
- package/components/svg/IconSubmit.js +18 -27
- package/components/svg/IconSubtract.js +19 -28
- package/components/svg/IconSynced.js +18 -27
- package/components/svg/IconTrashCan.js +18 -27
- package/components/svg/IconTreasureMap.js +18 -27
- package/components/svg/IconUnavailable.js +18 -27
- package/components/svg/IconUndo.js +18 -27
- package/components/svg/IconVariable.js +18 -27
- package/components/svg/IconVideo.js +19 -28
- package/components/svg/IconWifi.js +18 -27
- package/components/svg/Info.js +18 -27
- package/components/svg/Key.js +18 -27
- package/components/svg/Leaf.js +18 -27
- package/components/svg/Lock.js +18 -27
- package/components/svg/Logo.js +40 -45
- package/components/svg/MenuVertical.js +18 -27
- package/components/svg/Notification.js +18 -27
- package/components/svg/Person.js +18 -27
- package/components/svg/Plus.js +18 -27
- package/components/svg/RestorationSite.js +18 -27
- package/components/svg/Search.js +18 -27
- package/components/svg/Seeds.js +18 -27
- package/components/svg/Site.js +18 -27
- package/components/svg/Sparkles.js +18 -27
- package/components/svg/Species.js +18 -27
- package/components/svg/Species2.js +161 -158
- package/components/svg/Spinner.js +27 -36
- package/components/svg/Star.js +18 -27
- package/components/svg/Success.js +18 -27
- package/components/svg/SuccessFilled.js +18 -27
- package/components/svg/TerrawareLogoDesktop.js +44 -49
- package/components/svg/TerrawareLogoMobile.js +20 -29
- package/components/svg/Touchscreen.js +18 -27
- package/components/svg/UploadCloud.js +161 -158
- package/components/svg/Warning.js +18 -27
- package/components/svg/WelcomeClipboard.js +130 -135
- package/components/svg/index.js +127 -895
- package/components/table/EnhancedTableToolbar.js +45 -47
- package/components/table/EnhancedTableToolbarV2.js +18 -26
- package/components/table/TableCellRenderer.js +137 -143
- package/components/table/TableHeader.js +45 -47
- package/components/table/TableHeaderItem.js +65 -64
- package/components/table/density.js +3 -11
- package/components/table/index.js +225 -218
- package/components/table/sort.js +8 -18
- package/components/table/sort.test.js +6 -8
- package/components/table/types.js +1 -5
- package/components/types/index.js +1 -5
- package/hooks/useBoolean.d.ts +4 -0
- package/hooks/useBoolean.d.ts.map +1 -0
- package/hooks/useBoolean.js +12 -0
- package/hooks/useCameraPosition.d.ts +8 -0
- package/hooks/useCameraPosition.d.ts.map +1 -0
- package/hooks/useCameraPosition.js +33 -0
- package/hooks/useDevicePerformance.d.ts +9 -0
- package/hooks/useDevicePerformance.d.ts.map +1 -0
- package/hooks/useDevicePerformance.js +16 -0
- package/index.js +57 -398
- package/license-report.html +1 -1
- package/package.json +10 -3
- package/style-dictionary-dist/TerrawareTheme.js +1 -7
- package/theme.js +7 -14
- package/utils/color.d.ts +2 -0
- package/utils/color.d.ts.map +1 -0
- package/utils/color.js +14 -0
- package/utils/date.js +13 -24
- package/utils/date.test.js +40 -43
- package/utils/index.js +5 -53
- package/utils/preventDefaultEvent.js +1 -7
- package/utils/text.js +2 -9
- package/utils/text.test.js +9 -11
- package/utils/useDeviceInfo.js +10 -16
- package/virtualWalkthrough.d.ts +34 -0
- package/virtualWalkthrough.d.ts.map +1 -0
- package/virtualWalkthrough.js +30 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { computeGroundPlane, yOnPlane } from './groundPlane';
|
|
3
|
+
describe('computeGroundPlane', () => {
|
|
4
|
+
it('returns an up normal for a flat horizontal plane', () => {
|
|
5
|
+
const plane = computeGroundPlane([new Vec3(0, 5, 0), new Vec3(1, 5, 0), new Vec3(0, 5, 1)]);
|
|
6
|
+
expect(plane).not.toBeNull();
|
|
7
|
+
expect(plane.normal.x).toBeCloseTo(0);
|
|
8
|
+
expect(plane.normal.y).toBeCloseTo(1);
|
|
9
|
+
expect(plane.normal.z).toBeCloseTo(0);
|
|
10
|
+
expect(plane.point.y).toBeCloseTo(5);
|
|
11
|
+
});
|
|
12
|
+
it('flips a downward-facing normal so normal.y >= 0', () => {
|
|
13
|
+
const plane = computeGroundPlane([new Vec3(0, 0, 0), new Vec3(0, 0, 1), new Vec3(1, 0, 0)]);
|
|
14
|
+
expect(plane).not.toBeNull();
|
|
15
|
+
expect(plane.normal.y).toBeCloseTo(1);
|
|
16
|
+
});
|
|
17
|
+
it('computes a unit normal for a plane tilted 45deg about X (y = z)', () => {
|
|
18
|
+
const plane = computeGroundPlane([new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(0, 1, 1)]);
|
|
19
|
+
expect(plane).not.toBeNull();
|
|
20
|
+
const n = plane.normal;
|
|
21
|
+
expect(Math.sqrt(n.x * n.x + n.y * n.y + n.z * n.z)).toBeCloseTo(1);
|
|
22
|
+
expect(n.x).toBeCloseTo(0);
|
|
23
|
+
expect(n.y).toBeCloseTo(Math.SQRT1_2);
|
|
24
|
+
expect(n.z).toBeCloseTo(-Math.SQRT1_2);
|
|
25
|
+
});
|
|
26
|
+
it('returns null for fewer than 3 points', () => {
|
|
27
|
+
expect(computeGroundPlane([new Vec3(0, 0, 0), new Vec3(1, 0, 0)])).toBeNull();
|
|
28
|
+
});
|
|
29
|
+
it('returns null for collinear points', () => {
|
|
30
|
+
expect(computeGroundPlane([new Vec3(0, 0, 0), new Vec3(1, 0, 0), new Vec3(2, 0, 0)])).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('yOnPlane', () => {
|
|
34
|
+
it('returns the constant height on a flat plane', () => {
|
|
35
|
+
expect(yOnPlane(3, 7, new Vec3(0, 1, 0), new Vec3(0, 5, 0), -1)).toBeCloseTo(5);
|
|
36
|
+
});
|
|
37
|
+
it('interpolates height on a plane where y = z', () => {
|
|
38
|
+
const n = new Vec3(0, Math.SQRT1_2, -Math.SQRT1_2);
|
|
39
|
+
expect(yOnPlane(0, 2, n, new Vec3(0, 0, 0), 0)).toBeCloseTo(2);
|
|
40
|
+
expect(yOnPlane(10, -3, n, new Vec3(0, 0, 0), 0)).toBeCloseTo(-3);
|
|
41
|
+
});
|
|
42
|
+
it('returns the fallback when the normal is (near) horizontal', () => {
|
|
43
|
+
expect(yOnPlane(1, 1, new Vec3(1, 0, 0), new Vec3(0, 9, 0), 42)).toBe(42);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { GsplatShaderEffect } from 'playcanvas/scripts/esm/gsplat/gsplat-shader-effect.mjs';
|
|
3
|
+
/**
|
|
4
|
+
* Fade crop shader effect for gaussian splats.
|
|
5
|
+
* Applies a smooth fade to splats outside the AABB based on distance.
|
|
6
|
+
* Splats at the AABB edge are fully visible, and fade to invisible at fadeDistance away.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GsplatFadeCropShaderEffect extends GsplatShaderEffect {
|
|
9
|
+
static scriptName: string;
|
|
10
|
+
_aabbMinArray: number[];
|
|
11
|
+
_aabbMaxArray: number[];
|
|
12
|
+
aabbMin: Vec3;
|
|
13
|
+
aabbMax: Vec3;
|
|
14
|
+
fadeDistance: number;
|
|
15
|
+
getShaderGLSL(): string;
|
|
16
|
+
getShaderWGSL(): string;
|
|
17
|
+
updateEffect(): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=gsplat-fade-crop-effect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gsplat-fade-crop-effect.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/gsplat-fade-crop-effect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wDAAwD,CAAC;AA0F5F;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,kBAAkB;IAChE,MAAM,CAAC,UAAU,SAAgC;IAEjD,aAAa,WAAa;IAC1B,aAAa,WAAa;IAE1B,OAAO,OAA8B;IACrC,OAAO,OAA2B;IAClC,YAAY,SAAO;IAEnB,aAAa;IAIb,aAAa;IAIb,YAAY;CAab"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { Vec3 } from 'playcanvas';
|
|
2
|
+
import { GsplatShaderEffect } from 'playcanvas/scripts/esm/gsplat/gsplat-shader-effect.mjs';
|
|
3
|
+
const shaderGLSL = /* glsl */`
|
|
4
|
+
uniform vec3 uAabbMin;
|
|
5
|
+
uniform vec3 uAabbMax;
|
|
6
|
+
uniform float uFadeDistance;
|
|
7
|
+
|
|
8
|
+
void modifySplatCenter(inout vec3 center) {
|
|
9
|
+
// No modifications needed
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale) {
|
|
13
|
+
// Check if splat is inside AABB
|
|
14
|
+
bool insideAABB = all(greaterThanEqual(modifiedCenter, uAabbMin)) && all(lessThanEqual(modifiedCenter, uAabbMax));
|
|
15
|
+
|
|
16
|
+
if (insideAABB) {
|
|
17
|
+
// Inside the box, no fade needed
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Calculate distance to AABB for splats outside
|
|
22
|
+
vec3 distToMin = uAabbMin - modifiedCenter;
|
|
23
|
+
vec3 distToMax = modifiedCenter - uAabbMax;
|
|
24
|
+
|
|
25
|
+
// Get the maximum positive distance on each axis (how far outside we are)
|
|
26
|
+
vec3 outsideDistance = max(distToMin, distToMax);
|
|
27
|
+
outsideDistance = max(outsideDistance, vec3(0.0));
|
|
28
|
+
|
|
29
|
+
// Use the maximum distance across all axes
|
|
30
|
+
float maxOutsideDistance = max(max(outsideDistance.x, outsideDistance.y), outsideDistance.z);
|
|
31
|
+
|
|
32
|
+
// Calculate fade factor: 1.0 at edge, 0.0 at fadeDistance away
|
|
33
|
+
float fadeFactor = 1.0 - clamp(maxOutsideDistance / uFadeDistance, 0.0, 1.0);
|
|
34
|
+
|
|
35
|
+
// Apply smooth fade using smoothstep for nicer transition
|
|
36
|
+
fadeFactor = smoothstep(0.0, 1.0, fadeFactor);
|
|
37
|
+
|
|
38
|
+
// Scale the splat based on fade factor
|
|
39
|
+
scale *= fadeFactor;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void modifySplatColor(vec3 center, inout vec4 color) {
|
|
43
|
+
// No color modification needed
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
const shaderWGSL = /* wgsl */`
|
|
47
|
+
uniform uAabbMin: vec3f;
|
|
48
|
+
uniform uAabbMax: vec3f;
|
|
49
|
+
uniform uFadeDistance: f32;
|
|
50
|
+
|
|
51
|
+
fn modifySplatCenter(center: ptr<function, vec3f>) {
|
|
52
|
+
// No modifications needed
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
fn modifySplatRotationScale(originalCenter: vec3f, modifiedCenter: vec3f, rotation: ptr<function, vec4f>, scale: ptr<function, vec3f>) {
|
|
56
|
+
// Check if splat is inside AABB
|
|
57
|
+
let insideAABB = all(modifiedCenter >= uniform.uAabbMin) && all(modifiedCenter <= uniform.uAabbMax);
|
|
58
|
+
|
|
59
|
+
if (insideAABB) {
|
|
60
|
+
// Inside the box, no fade needed
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Calculate distance to AABB for splats outside
|
|
65
|
+
let distToMin = uniform.uAabbMin - modifiedCenter;
|
|
66
|
+
let distToMax = modifiedCenter - uniform.uAabbMax;
|
|
67
|
+
|
|
68
|
+
// Get the maximum positive distance on each axis (how far outside we are)
|
|
69
|
+
var outsideDistance = max(distToMin, distToMax);
|
|
70
|
+
outsideDistance = max(outsideDistance, vec3f(0.0));
|
|
71
|
+
|
|
72
|
+
// Use the maximum distance across all axes
|
|
73
|
+
let maxOutsideDistance = max(max(outsideDistance.x, outsideDistance.y), outsideDistance.z);
|
|
74
|
+
|
|
75
|
+
// Calculate fade factor: 1.0 at edge, 0.0 at fadeDistance away
|
|
76
|
+
var fadeFactor = 1.0 - clamp(maxOutsideDistance / uniform.uFadeDistance, 0.0, 1.0);
|
|
77
|
+
|
|
78
|
+
// Apply smooth fade using smoothstep for nicer transition
|
|
79
|
+
fadeFactor = smoothstep(0.0, 1.0, fadeFactor);
|
|
80
|
+
|
|
81
|
+
// Scale the splat based on fade factor
|
|
82
|
+
*scale = (*scale) * fadeFactor;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fn modifySplatColor(center: vec3f, color: ptr<function, vec4f>) {
|
|
86
|
+
// No color modification needed
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Fade crop shader effect for gaussian splats.
|
|
92
|
+
* Applies a smooth fade to splats outside the AABB based on distance.
|
|
93
|
+
* Splats at the AABB edge are fully visible, and fade to invisible at fadeDistance away.
|
|
94
|
+
*/
|
|
95
|
+
export class GsplatFadeCropShaderEffect extends GsplatShaderEffect {
|
|
96
|
+
static scriptName = 'gsplatFadeCropShaderEffect';
|
|
97
|
+
_aabbMinArray = [0, 0, 0];
|
|
98
|
+
_aabbMaxArray = [0, 0, 0];
|
|
99
|
+
aabbMin = new Vec3(-0.5, -0.5, -0.5);
|
|
100
|
+
aabbMax = new Vec3(0.5, 0.5, 0.5);
|
|
101
|
+
fadeDistance = 0.5;
|
|
102
|
+
getShaderGLSL() {
|
|
103
|
+
return shaderGLSL;
|
|
104
|
+
}
|
|
105
|
+
getShaderWGSL() {
|
|
106
|
+
return shaderWGSL;
|
|
107
|
+
}
|
|
108
|
+
updateEffect() {
|
|
109
|
+
this._aabbMinArray[0] = this.aabbMin.x;
|
|
110
|
+
this._aabbMinArray[1] = this.aabbMin.y;
|
|
111
|
+
this._aabbMinArray[2] = this.aabbMin.z;
|
|
112
|
+
this.setUniform('uAabbMin', this._aabbMinArray);
|
|
113
|
+
this._aabbMaxArray[0] = this.aabbMax.x;
|
|
114
|
+
this._aabbMaxArray[1] = this.aabbMax.y;
|
|
115
|
+
this._aabbMaxArray[2] = this.aabbMax.z;
|
|
116
|
+
this.setUniform('uAabbMax', this._aabbMaxArray);
|
|
117
|
+
this.setUniform('uFadeDistance', this.fadeDistance);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Script, Vec3 } from 'playcanvas';
|
|
2
|
+
export declare class WalkthroughCamera extends Script {
|
|
3
|
+
static scriptName: string;
|
|
4
|
+
/** @attribute */
|
|
5
|
+
moveSpeed: number;
|
|
6
|
+
/** @attribute */
|
|
7
|
+
moveFastSpeed: number;
|
|
8
|
+
/** @attribute */
|
|
9
|
+
moveSlowSpeed: number;
|
|
10
|
+
/** @attribute */
|
|
11
|
+
lookSensitivity: number;
|
|
12
|
+
/** @attribute */
|
|
13
|
+
rotateDamping: number;
|
|
14
|
+
/** @attribute */
|
|
15
|
+
pitchMin: number;
|
|
16
|
+
/** @attribute */
|
|
17
|
+
pitchMax: number;
|
|
18
|
+
/** @attribute */
|
|
19
|
+
scrollSpeed: number;
|
|
20
|
+
/** @attribute */
|
|
21
|
+
boundsCenter: Vec3;
|
|
22
|
+
/** @attribute */
|
|
23
|
+
boundsRadius: number;
|
|
24
|
+
/** @attribute */
|
|
25
|
+
freeFly: boolean;
|
|
26
|
+
/** @attribute */
|
|
27
|
+
enableFly: boolean;
|
|
28
|
+
/** @attribute */
|
|
29
|
+
averageCameraHeight: number;
|
|
30
|
+
/**
|
|
31
|
+
* Three world-space points defining the ground plane.
|
|
32
|
+
* When provided, the camera Y position is derived from this plane during non-freeFly movement.
|
|
33
|
+
*
|
|
34
|
+
* Not a React prop — must be set directly on the script instance via useEffect. The @playcanvas/react
|
|
35
|
+
* Script component is wrapped in memo() whose shallowEquals comparator returns early on the first prop
|
|
36
|
+
* with a .equals() method (Vec3), so any prop listed after boundsCenter is never compared and the
|
|
37
|
+
* component won't re-render when it changes. Set via:
|
|
38
|
+
* app.root.findByName('camera')?.script?.walkthroughCamera.groundPlane = points
|
|
39
|
+
* Should be updated if shallowEquals is fixed (see https://github.com/playcanvas/react/pull/298)
|
|
40
|
+
*/
|
|
41
|
+
groundPlane: Vec3[];
|
|
42
|
+
private _pitch;
|
|
43
|
+
private _yaw;
|
|
44
|
+
private _targetPitch;
|
|
45
|
+
private _targetYaw;
|
|
46
|
+
private _isDragging;
|
|
47
|
+
private _lastX;
|
|
48
|
+
private _lastY;
|
|
49
|
+
private _scrollDelta;
|
|
50
|
+
private _keys;
|
|
51
|
+
private _removeListeners;
|
|
52
|
+
private _planeNormal;
|
|
53
|
+
private _planePoint;
|
|
54
|
+
private _hasGroundPlane;
|
|
55
|
+
private _syncGroundPlane;
|
|
56
|
+
initialize(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Position the camera at `position` looking toward `focus`.
|
|
59
|
+
* Compatible with the useCameraPosition hook's reset() call.
|
|
60
|
+
*/
|
|
61
|
+
reset(focus: Vec3, position: Vec3): void;
|
|
62
|
+
get currentYaw(): number;
|
|
63
|
+
get currentPitch(): number;
|
|
64
|
+
/**
|
|
65
|
+
* Orbit the camera by `yawDelta` degrees around `boundsCenter`.
|
|
66
|
+
* Called by AutoRotator
|
|
67
|
+
*/
|
|
68
|
+
orbitStep(yawDelta: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* Returns the point directly in front of the camera.
|
|
71
|
+
* Compatible with the useCameraPosition hook's focusPoint read.
|
|
72
|
+
*/
|
|
73
|
+
get focusPoint(): Vec3;
|
|
74
|
+
update(dt: number): void;
|
|
75
|
+
destroy(): void;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=walkthrough-camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walkthrough-camera.d.ts","sourceRoot":"","sources":["../../../src/components/VirtualWalkthrough/walkthrough-camera.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAE,IAAI,EAAQ,MAAM,YAAY,CAAC;AA8BtD,qBAAa,iBAAkB,SAAQ,MAAM;IAC3C,MAAM,CAAC,UAAU,SAAuB;IAExC,iBAAiB;IACjB,SAAS,SAAO;IAEhB,iBAAiB;IACjB,aAAa,SAAO;IAEpB,iBAAiB;IACjB,aAAa,SAAQ;IAErB,iBAAiB;IACjB,eAAe,SAAO;IAEtB,iBAAiB;IACjB,aAAa,SAAQ;IAErB,iBAAiB;IACjB,QAAQ,SAAO;IAEf,iBAAiB;IACjB,QAAQ,SAAM;IAEd,iBAAiB;IACjB,WAAW,SAAS;IAEpB,iBAAiB;IACjB,YAAY,OAAqB;IAEjC,iBAAiB;IACjB,YAAY,SAAM;IAElB,iBAAiB;IACjB,OAAO,UAAS;IAEhB,iBAAiB;IACjB,SAAS,UAAQ;IAEjB,iBAAiB;IACjB,mBAAmB,SAAK;IAExB;;;;;;;;;;OAUG;IACH,WAAW,EAAE,IAAI,EAAE,CAAM;IAGzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,IAAI,CAAK;IAGjB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,gBAAgB,CAAsB;IAG9C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,eAAe,CAAS;IAEhC,OAAO,CAAC,gBAAgB;IAaxB,UAAU;IAkFV;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI;IAsBjC,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM;IAsB1B;;;OAGG;IACH,IAAI,UAAU,IAAI,IAAI,CAMrB;IAED,MAAM,CAAC,EAAE,EAAE,MAAM;IA4FjB,OAAO;CAIR"}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { Quat, Script, Vec3, math } from 'playcanvas';
|
|
2
|
+
import { computeGroundPlane, yOnPlane } from './groundPlane';
|
|
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
|
+
|
|
17
|
+
const _quat = new Quat();
|
|
18
|
+
const _flatForward = new Vec3();
|
|
19
|
+
const _flatRight = new Vec3();
|
|
20
|
+
|
|
21
|
+
/** Frame-rate-independent damping lerp rate — matches PlayCanvas CameraControls. */
|
|
22
|
+
const dampRate = (damping, dt) => 1 - Math.pow(damping, dt * 1000);
|
|
23
|
+
|
|
24
|
+
/** Below this angular delta (degrees) the damped lerp snaps to its target and stops writing. */
|
|
25
|
+
const ANGLE_EPSILON = 1e-3;
|
|
26
|
+
|
|
27
|
+
/** Below this positional delta (world units) an idle frame skips the transform write. */
|
|
28
|
+
const POSITION_EPSILON = 1e-5;
|
|
29
|
+
export class WalkthroughCamera extends Script {
|
|
30
|
+
static scriptName = 'walkthroughCamera';
|
|
31
|
+
|
|
32
|
+
/** @attribute */
|
|
33
|
+
moveSpeed = 0.3;
|
|
34
|
+
|
|
35
|
+
/** @attribute */
|
|
36
|
+
moveFastSpeed = 0.5;
|
|
37
|
+
|
|
38
|
+
/** @attribute */
|
|
39
|
+
moveSlowSpeed = 0.15;
|
|
40
|
+
|
|
41
|
+
/** @attribute */
|
|
42
|
+
lookSensitivity = 0.1;
|
|
43
|
+
|
|
44
|
+
/** @attribute */
|
|
45
|
+
rotateDamping = 0.98;
|
|
46
|
+
|
|
47
|
+
/** @attribute */
|
|
48
|
+
pitchMin = -85;
|
|
49
|
+
|
|
50
|
+
/** @attribute */
|
|
51
|
+
pitchMax = 85;
|
|
52
|
+
|
|
53
|
+
/** @attribute */
|
|
54
|
+
scrollSpeed = 0.001;
|
|
55
|
+
|
|
56
|
+
/** @attribute */
|
|
57
|
+
boundsCenter = new Vec3(0, 0, 0);
|
|
58
|
+
|
|
59
|
+
/** @attribute */
|
|
60
|
+
boundsRadius = 10;
|
|
61
|
+
|
|
62
|
+
/** @attribute */
|
|
63
|
+
freeFly = false;
|
|
64
|
+
|
|
65
|
+
/** @attribute */
|
|
66
|
+
enableFly = true;
|
|
67
|
+
|
|
68
|
+
/** @attribute */
|
|
69
|
+
averageCameraHeight = 0;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Three world-space points defining the ground plane.
|
|
73
|
+
* When provided, the camera Y position is derived from this plane during non-freeFly movement.
|
|
74
|
+
*
|
|
75
|
+
* Not a React prop — must be set directly on the script instance via useEffect. The @playcanvas/react
|
|
76
|
+
* Script component is wrapped in memo() whose shallowEquals comparator returns early on the first prop
|
|
77
|
+
* with a .equals() method (Vec3), so any prop listed after boundsCenter is never compared and the
|
|
78
|
+
* component won't re-render when it changes. Set via:
|
|
79
|
+
* app.root.findByName('camera')?.script?.walkthroughCamera.groundPlane = points
|
|
80
|
+
* Should be updated if shallowEquals is fixed (see https://github.com/playcanvas/react/pull/298)
|
|
81
|
+
*/
|
|
82
|
+
groundPlane = [];
|
|
83
|
+
|
|
84
|
+
// Current (damped) angles applied to the entity each frame.
|
|
85
|
+
_pitch = 0;
|
|
86
|
+
_yaw = 0;
|
|
87
|
+
|
|
88
|
+
// Target angles — updated immediately on pointer move, entity lerps toward them.
|
|
89
|
+
_targetPitch = 0;
|
|
90
|
+
_targetYaw = 0;
|
|
91
|
+
_isDragging = false;
|
|
92
|
+
_lastX = 0;
|
|
93
|
+
_lastY = 0;
|
|
94
|
+
_scrollDelta = 0;
|
|
95
|
+
_keys = {};
|
|
96
|
+
_removeListeners = [];
|
|
97
|
+
|
|
98
|
+
// Cached plane derived from groundPlane attribute. Computed once when 3 points become available.
|
|
99
|
+
_planeNormal = new Vec3(0, 1, 0);
|
|
100
|
+
_planePoint = new Vec3(0, 0, 0);
|
|
101
|
+
_hasGroundPlane = false;
|
|
102
|
+
_syncGroundPlane() {
|
|
103
|
+
if (this._hasGroundPlane || this.groundPlane.length < 3) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const plane = computeGroundPlane(this.groundPlane);
|
|
107
|
+
if (!plane) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this._planeNormal.copy(plane.normal);
|
|
111
|
+
this._planePoint.copy(plane.point);
|
|
112
|
+
this._hasGroundPlane = true;
|
|
113
|
+
}
|
|
114
|
+
initialize() {
|
|
115
|
+
// Start horizontal; reset() will set the correct pose once splat data loads.
|
|
116
|
+
this._pitch = 0;
|
|
117
|
+
this._yaw = 0;
|
|
118
|
+
this._targetPitch = 0;
|
|
119
|
+
this._targetYaw = 0;
|
|
120
|
+
const canvas = this.app.graphicsDevice.canvas;
|
|
121
|
+
const onPointerDown = e => {
|
|
122
|
+
this._isDragging = true;
|
|
123
|
+
this._lastX = e.clientX;
|
|
124
|
+
this._lastY = e.clientY;
|
|
125
|
+
try {
|
|
126
|
+
canvas.setPointerCapture(e.pointerId);
|
|
127
|
+
} catch {
|
|
128
|
+
// ignore — canvas may not be focusable yet
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const onPointerMove = e => {
|
|
132
|
+
if (!this._isDragging) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const dx = e.clientX - this._lastX;
|
|
136
|
+
const dy = e.clientY - this._lastY;
|
|
137
|
+
this._lastX = e.clientX;
|
|
138
|
+
this._lastY = e.clientY;
|
|
139
|
+
this._targetYaw -= dx * this.lookSensitivity;
|
|
140
|
+
this._targetPitch = math.clamp(this._targetPitch - dy * this.lookSensitivity, this.pitchMin, this.pitchMax);
|
|
141
|
+
};
|
|
142
|
+
const onPointerUp = e => {
|
|
143
|
+
this._isDragging = false;
|
|
144
|
+
try {
|
|
145
|
+
canvas.releasePointerCapture(e.pointerId);
|
|
146
|
+
} catch {
|
|
147
|
+
// ignore
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const onWheel = e => {
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
this._scrollDelta += e.deltaY;
|
|
153
|
+
};
|
|
154
|
+
const onContextMenu = e => e.preventDefault();
|
|
155
|
+
const onKeyDown = e => {
|
|
156
|
+
this._keys[e.code] = true;
|
|
157
|
+
};
|
|
158
|
+
const onKeyUp = e => {
|
|
159
|
+
this._keys[e.code] = false;
|
|
160
|
+
};
|
|
161
|
+
const onBlur = () => {
|
|
162
|
+
this._keys = {};
|
|
163
|
+
this._isDragging = false;
|
|
164
|
+
};
|
|
165
|
+
canvas.addEventListener('pointerdown', onPointerDown);
|
|
166
|
+
canvas.addEventListener('pointermove', onPointerMove);
|
|
167
|
+
canvas.addEventListener('pointerup', onPointerUp);
|
|
168
|
+
canvas.addEventListener('pointercancel', onPointerUp);
|
|
169
|
+
canvas.addEventListener('wheel', onWheel, {
|
|
170
|
+
passive: false
|
|
171
|
+
});
|
|
172
|
+
canvas.addEventListener('contextmenu', onContextMenu);
|
|
173
|
+
window.addEventListener('keydown', onKeyDown);
|
|
174
|
+
window.addEventListener('keyup', onKeyUp);
|
|
175
|
+
window.addEventListener('blur', onBlur);
|
|
176
|
+
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)];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Position the camera at `position` looking toward `focus`.
|
|
181
|
+
* Compatible with the useCameraPosition hook's reset() call.
|
|
182
|
+
*/
|
|
183
|
+
reset(focus, position) {
|
|
184
|
+
this.entity.setPosition(position);
|
|
185
|
+
const dx = focus.x - position.x;
|
|
186
|
+
const dy = focus.y - position.y;
|
|
187
|
+
const dz = focus.z - position.z;
|
|
188
|
+
const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
189
|
+
if (len > 0) {
|
|
190
|
+
// PlayCanvas uses right-handed CCW Y rotation: forward = (-sinθ, 0, -cosθ),
|
|
191
|
+
// so to look toward (dx, 0, dz): θ = atan2(-dx, -dz).
|
|
192
|
+
this._yaw = Math.atan2(-dx, -dz) * (180 / Math.PI);
|
|
193
|
+
this._pitch = Math.atan2(-dy, Math.sqrt(dx * dx + dz * dz)) * (180 / Math.PI);
|
|
194
|
+
this._pitch = math.clamp(this._pitch, this.pitchMin, this.pitchMax);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Snap targets so there's no damped drift from the previous pose.
|
|
198
|
+
this._targetPitch = this._pitch;
|
|
199
|
+
this._targetYaw = this._yaw;
|
|
200
|
+
this.entity.setEulerAngles(this._pitch, this._yaw, 0);
|
|
201
|
+
}
|
|
202
|
+
get currentYaw() {
|
|
203
|
+
return this._targetYaw;
|
|
204
|
+
}
|
|
205
|
+
get currentPitch() {
|
|
206
|
+
return this._targetPitch;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Orbit the camera by `yawDelta` degrees around `boundsCenter`.
|
|
211
|
+
* Called by AutoRotator
|
|
212
|
+
*/
|
|
213
|
+
orbitStep(yawDelta) {
|
|
214
|
+
this._syncGroundPlane();
|
|
215
|
+
const pos = this.entity.getPosition();
|
|
216
|
+
const dx = pos.x - this.boundsCenter.x;
|
|
217
|
+
const dz = pos.z - this.boundsCenter.z;
|
|
218
|
+
const radius = Math.sqrt(dx * dx + dz * dz) || this.boundsRadius * 0.5;
|
|
219
|
+
const currentAngle = Math.atan2(dz, dx);
|
|
220
|
+
const newAngle = currentAngle + yawDelta * Math.PI / 180;
|
|
221
|
+
const nx = this.boundsCenter.x + radius * Math.cos(newAngle);
|
|
222
|
+
const nz = this.boundsCenter.z + radius * Math.sin(newAngle);
|
|
223
|
+
const ny = this._hasGroundPlane ? yOnPlane(nx, nz, this._planeNormal, this._planePoint, this.boundsCenter.y) + this.averageCameraHeight : this.boundsCenter.y;
|
|
224
|
+
this.entity.setPosition(nx, ny, nz);
|
|
225
|
+
const faceDx = this.boundsCenter.x - nx;
|
|
226
|
+
const faceDz = this.boundsCenter.z - nz;
|
|
227
|
+
this._yaw = Math.atan2(-faceDx, -faceDz) * (180 / Math.PI);
|
|
228
|
+
this._targetYaw = this._yaw;
|
|
229
|
+
this.entity.setEulerAngles(this._pitch, this._yaw, 0);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Returns the point directly in front of the camera.
|
|
234
|
+
* Compatible with the useCameraPosition hook's focusPoint read.
|
|
235
|
+
*/
|
|
236
|
+
get focusPoint() {
|
|
237
|
+
const pos = this.entity.getPosition();
|
|
238
|
+
_quat.setFromEulerAngles(this._pitch, this._yaw, 0);
|
|
239
|
+
_quat.transformVector(Vec3.FORWARD, _flatForward);
|
|
240
|
+
return new Vec3(pos.x + _flatForward.x, pos.y + _flatForward.y, pos.z + _flatForward.z);
|
|
241
|
+
}
|
|
242
|
+
update(dt) {
|
|
243
|
+
this._syncGroundPlane();
|
|
244
|
+
|
|
245
|
+
// Lerp current angles toward targets using the same damping as CameraControls.
|
|
246
|
+
// Snap to the target once within ANGLE_EPSILON so the asymptotic lerp actually
|
|
247
|
+
// settles instead of emitting ever-smaller micro-rotations for ~0.4s after each
|
|
248
|
+
// drag. The gsplat sorter guards its own re-sorts with an epsilon, so this mainly
|
|
249
|
+
// trims that sort tail; it does not affect per-frame rendering (autoRender does).
|
|
250
|
+
const r = dampRate(this.rotateDamping, dt);
|
|
251
|
+
let nextPitch = this._pitch + (this._targetPitch - this._pitch) * r;
|
|
252
|
+
let nextYaw = this._yaw + (this._targetYaw - this._yaw) * r;
|
|
253
|
+
if (Math.abs(this._targetPitch - nextPitch) < ANGLE_EPSILON) {
|
|
254
|
+
nextPitch = this._targetPitch;
|
|
255
|
+
}
|
|
256
|
+
if (Math.abs(this._targetYaw - nextYaw) < ANGLE_EPSILON) {
|
|
257
|
+
nextYaw = this._targetYaw;
|
|
258
|
+
}
|
|
259
|
+
nextPitch = math.clamp(nextPitch, this.pitchMin, this.pitchMax);
|
|
260
|
+
|
|
261
|
+
// Only write the rotation when it actually changed, to avoid needlessly dirtying
|
|
262
|
+
// the scene node once the angles have settled.
|
|
263
|
+
const anglesChanged = nextPitch !== this._pitch || nextYaw !== this._yaw;
|
|
264
|
+
this._pitch = nextPitch;
|
|
265
|
+
this._yaw = nextYaw;
|
|
266
|
+
if (anglesChanged) {
|
|
267
|
+
this.entity.setEulerAngles(this._pitch, this._yaw, 0);
|
|
268
|
+
}
|
|
269
|
+
const pos = this.entity.getPosition();
|
|
270
|
+
let nx = pos.x;
|
|
271
|
+
let ny = pos.y;
|
|
272
|
+
let nz = pos.z;
|
|
273
|
+
if (this.enableFly) {
|
|
274
|
+
const fast = this._keys.ShiftLeft || this._keys.ShiftRight;
|
|
275
|
+
const slow = this._keys.ControlLeft || this._keys.ControlRight;
|
|
276
|
+
const speed = (fast ? this.moveFastSpeed : slow ? this.moveSlowSpeed : this.moveSpeed) * dt;
|
|
277
|
+
const fwd = (this._keys.KeyW || this._keys.ArrowUp ? 1 : 0) - (this._keys.KeyS || this._keys.ArrowDown ? 1 : 0);
|
|
278
|
+
const strafe = (this._keys.KeyD || this._keys.ArrowRight ? 1 : 0) - (this._keys.KeyA || this._keys.ArrowLeft ? 1 : 0);
|
|
279
|
+
const scrollFwd = -this._scrollDelta * this.scrollSpeed;
|
|
280
|
+
if (fwd !== 0 || strafe !== 0 || scrollFwd !== 0) {
|
|
281
|
+
// Use yaw-only rotation so movement is always horizontal.
|
|
282
|
+
_quat.setFromEulerAngles(0, this._yaw, 0);
|
|
283
|
+
_quat.transformVector(Vec3.FORWARD, _flatForward);
|
|
284
|
+
_quat.transformVector(Vec3.RIGHT, _flatRight);
|
|
285
|
+
nx += _flatForward.x * (fwd * speed + scrollFwd) + _flatRight.x * strafe * speed;
|
|
286
|
+
nz += _flatForward.z * (fwd * speed + scrollFwd) + _flatRight.z * strafe * speed;
|
|
287
|
+
}
|
|
288
|
+
if (this.freeFly) {
|
|
289
|
+
const up = (this._keys.KeyE ? 1 : 0) - (this._keys.KeyQ ? 1 : 0);
|
|
290
|
+
if (up !== 0) {
|
|
291
|
+
ny += up * speed;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
this._scrollDelta = 0;
|
|
296
|
+
if (!this.freeFly) {
|
|
297
|
+
// Circular XZ clamp: project back onto the circle edge if outside.
|
|
298
|
+
const cdx = nx - this.boundsCenter.x;
|
|
299
|
+
const cdz = nz - this.boundsCenter.z;
|
|
300
|
+
const dist = Math.sqrt(cdx * cdx + cdz * cdz);
|
|
301
|
+
if (dist > this.boundsRadius) {
|
|
302
|
+
const scale = this.boundsRadius / dist;
|
|
303
|
+
nx = this.boundsCenter.x + cdx * scale;
|
|
304
|
+
nz = this.boundsCenter.z + cdz * scale;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const groundY = this._hasGroundPlane ? yOnPlane(nx, nz, this._planeNormal, this._planePoint, this.boundsCenter.y) + this.averageCameraHeight : this.boundsCenter.y;
|
|
308
|
+
const targetY = this.freeFly ? ny : groundY;
|
|
309
|
+
// Only write the position when it actually moved. This keeps the scene node clean on
|
|
310
|
+
// idle frames; note it does not stop per-frame rasterization — while autoRender is on
|
|
311
|
+
// the app renders every frame regardless of whether the transform changed.
|
|
312
|
+
if (Math.abs(nx - pos.x) > POSITION_EPSILON || Math.abs(targetY - pos.y) > POSITION_EPSILON || Math.abs(nz - pos.z) > POSITION_EPSILON) {
|
|
313
|
+
this.entity.setPosition(nx, targetY, nz);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
destroy() {
|
|
317
|
+
this._removeListeners.forEach(fn => fn());
|
|
318
|
+
this._removeListeners = [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
@@ -1,42 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
const SvgBlobbyGrayIconImage = ({
|
|
4
|
+
title,
|
|
5
|
+
titleId,
|
|
6
|
+
...props
|
|
7
|
+
}) => /*#__PURE__*/_jsxs("svg", {
|
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
9
|
+
fill: "none",
|
|
10
|
+
viewBox: "0 0 200 128",
|
|
11
|
+
"aria-labelledby": titleId,
|
|
12
|
+
...props,
|
|
13
|
+
children: [title ? /*#__PURE__*/_jsx("title", {
|
|
14
|
+
id: titleId,
|
|
15
|
+
children: title
|
|
16
|
+
}) : null, /*#__PURE__*/_jsxs("g", {
|
|
17
|
+
clipPath: "url(#blobby-gray-icon-image_svg__a)",
|
|
18
|
+
children: [/*#__PURE__*/_jsx("path", {
|
|
19
|
+
fill: "#F1F0EC",
|
|
20
|
+
fillRule: "evenodd",
|
|
21
|
+
d: "M113.962 6.222c26.535 2.074 38.447 30.862 44.693 56.735 4.621 19.146-2.901 38.537-18.932 49.978-15.595 11.131-35.55 10.486-51.986.639C63.732 99.19 34.311 80.943 40.954 53.757c7.619-31.19 41-50.037 73.009-47.535",
|
|
22
|
+
clipRule: "evenodd"
|
|
23
|
+
}), /*#__PURE__*/_jsx("path", {
|
|
24
|
+
fill: "#7F775B",
|
|
25
|
+
d: "M83.333 40C79.307 40 76 43.307 76 47.333v33.334C76 84.693 79.307 88 83.333 88h33.334c4.026 0 7.333-3.307 7.333-7.333V47.333c0-4.026-3.307-7.333-7.333-7.333zm0 4h33.334A3.304 3.304 0 0 1 120 47.333v27.274l-8.016-7.755-.002-.003a5.4 5.4 0 0 0-3.75-1.516 5.4 5.4 0 0 0-3.75 1.519l-2.732 2.643-8.24-7.972A5.4 5.4 0 0 0 89.758 60a5.4 5.4 0 0 0-3.75 1.52l-.003.003L80 67.333v-20A3.304 3.304 0 0 1 83.333 44m25.334 5.333c-1.834 0-3.462.76-4.495 1.922-1.033 1.163-1.505 2.634-1.505 4.078s.472 2.916 1.505 4.078 2.661 1.922 4.495 1.922c1.833 0 3.461-.76 4.494-1.922 1.034-1.162 1.506-2.633 1.506-4.078 0-1.444-.472-2.915-1.506-4.078-1.033-1.162-2.661-1.922-4.494-1.922m0 4c.833 0 1.205.24 1.505.578s.495.867.495 1.422c0 .556-.195 1.085-.495 1.422-.3.338-.672.578-1.505.578-.834 0-1.205-.24-1.506-.578s-.494-.866-.494-1.422.194-1.084.494-1.422c.301-.337.672-.578 1.506-.578M89.757 64a1.37 1.37 0 0 1 .972.398l8.143 7.878L86.752 84h-3.419A3.304 3.304 0 0 1 80 80.667v-7.769l8.787-8.5c.283-.274.626-.398.97-.398m18.475 5.333c.353 0 .691.123.971.394L120 80.172v.495A3.304 3.304 0 0 1 116.667 84h-24.16l14.756-14.273c.277-.268.618-.394.969-.394"
|
|
26
|
+
})]
|
|
27
|
+
}), /*#__PURE__*/_jsx("defs", {
|
|
28
|
+
children: /*#__PURE__*/_jsx("clipPath", {
|
|
29
|
+
id: "blobby-gray-icon-image_svg__a",
|
|
30
|
+
children: /*#__PURE__*/_jsx("path", {
|
|
31
|
+
fill: "#fff",
|
|
32
|
+
d: "M0 0h200v128H0z"
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
})]
|
|
7
36
|
});
|
|
8
|
-
|
|
9
|
-
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectWithoutProperties"));
|
|
10
|
-
var React = _interopRequireWildcard(require("react"));
|
|
11
|
-
const _excluded = ["title", "titleId"];
|
|
12
|
-
const SvgBlobbyGrayIconImage = _ref => {
|
|
13
|
-
let {
|
|
14
|
-
title,
|
|
15
|
-
titleId
|
|
16
|
-
} = _ref,
|
|
17
|
-
props = (0, _objectWithoutProperties2.default)(_ref, _excluded);
|
|
18
|
-
return /*#__PURE__*/React.createElement("svg", Object.assign({
|
|
19
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
20
|
-
fill: "none",
|
|
21
|
-
viewBox: "0 0 200 128",
|
|
22
|
-
"aria-labelledby": titleId
|
|
23
|
-
}, props), title ? /*#__PURE__*/React.createElement("title", {
|
|
24
|
-
id: titleId
|
|
25
|
-
}, title) : null, /*#__PURE__*/React.createElement("g", {
|
|
26
|
-
clipPath: "url(#blobby-gray-icon-image_svg__a)"
|
|
27
|
-
}, /*#__PURE__*/React.createElement("path", {
|
|
28
|
-
fill: "#F1F0EC",
|
|
29
|
-
fillRule: "evenodd",
|
|
30
|
-
d: "M113.962 6.222c26.535 2.074 38.447 30.862 44.693 56.735 4.621 19.146-2.901 38.537-18.932 49.978-15.595 11.131-35.55 10.486-51.986.639C63.732 99.19 34.311 80.943 40.954 53.757c7.619-31.19 41-50.037 73.009-47.535",
|
|
31
|
-
clipRule: "evenodd"
|
|
32
|
-
}), /*#__PURE__*/React.createElement("path", {
|
|
33
|
-
fill: "#7F775B",
|
|
34
|
-
d: "M83.333 40C79.307 40 76 43.307 76 47.333v33.334C76 84.693 79.307 88 83.333 88h33.334c4.026 0 7.333-3.307 7.333-7.333V47.333c0-4.026-3.307-7.333-7.333-7.333zm0 4h33.334A3.304 3.304 0 0 1 120 47.333v27.274l-8.016-7.755-.002-.003a5.4 5.4 0 0 0-3.75-1.516 5.4 5.4 0 0 0-3.75 1.519l-2.732 2.643-8.24-7.972A5.4 5.4 0 0 0 89.758 60a5.4 5.4 0 0 0-3.75 1.52l-.003.003L80 67.333v-20A3.304 3.304 0 0 1 83.333 44m25.334 5.333c-1.834 0-3.462.76-4.495 1.922-1.033 1.163-1.505 2.634-1.505 4.078s.472 2.916 1.505 4.078 2.661 1.922 4.495 1.922c1.833 0 3.461-.76 4.494-1.922 1.034-1.162 1.506-2.633 1.506-4.078 0-1.444-.472-2.915-1.506-4.078-1.033-1.162-2.661-1.922-4.494-1.922m0 4c.833 0 1.205.24 1.505.578s.495.867.495 1.422c0 .556-.195 1.085-.495 1.422-.3.338-.672.578-1.505.578-.834 0-1.205-.24-1.506-.578s-.494-.866-.494-1.422.194-1.084.494-1.422c.301-.337.672-.578 1.506-.578M89.757 64a1.37 1.37 0 0 1 .972.398l8.143 7.878L86.752 84h-3.419A3.304 3.304 0 0 1 80 80.667v-7.769l8.787-8.5c.283-.274.626-.398.97-.398m18.475 5.333c.353 0 .691.123.971.394L120 80.172v.495A3.304 3.304 0 0 1 116.667 84h-24.16l14.756-14.273c.277-.268.618-.394.969-.394"
|
|
35
|
-
})), /*#__PURE__*/React.createElement("defs", null, /*#__PURE__*/React.createElement("clipPath", {
|
|
36
|
-
id: "blobby-gray-icon-image_svg__a"
|
|
37
|
-
}, /*#__PURE__*/React.createElement("path", {
|
|
38
|
-
fill: "#fff",
|
|
39
|
-
d: "M0 0h200v128H0z"
|
|
40
|
-
}))));
|
|
41
|
-
};
|
|
42
|
-
var _default = exports.default = SvgBlobbyGrayIconImage;
|
|
37
|
+
export default SvgBlobbyGrayIconImage;
|