Add a trackball camera setting (#4764)
* Add a setting that does nothing * Make the setting actually change the interaction type * fmt * Bump `@kittycad/lib` to get the proper camera drag interaction types * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Fix camera orientation bugs to support proper camera resetting on "camera orbit" setting change (#5031) * Add a setting that does nothing * Make the setting actually change the interaction type * fmt * fix: up vector bug fix and camera reset fix. Pushing code to cleanup after debugging * fix: deleting debugging code * fix: removing debugging code * fix: removing debugging console log * fix: removing console log debugs * fix: adding comment, restoring code from debugging * fix: removed lookAt when the orientation is already set from the engine.. I do not think we should be recomputing it? * fix: this fixes the bug because I was pointing to the getter not the value * Remove unused imports * Fix lint for unawaited Promise * Remove pointless change --------- Co-authored-by: Frank Noirot <frank@kittycad.io> Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com> * Re-run CI * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * Re-run CI * Add display attributes to try to fix cargo test * Remove backwards compat test case it's failing because I didn't add cameraOrbit to that type and I don't want to * Fix test value (prev user value would have been Spherical before Trackball) --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com> Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
This commit is contained in:
@ -29,6 +29,7 @@ import * as TWEEN from '@tweenjs/tween.js'
|
|||||||
import { isQuaternionVertical } from './helpers'
|
import { isQuaternionVertical } from './helpers'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||||
|
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
||||||
|
|
||||||
const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
const ORTHOGRAPHIC_CAMERA_SIZE = 20
|
||||||
const FRAMES_TO_ANIMATE_IN = 30
|
const FRAMES_TO_ANIMATE_IN = 30
|
||||||
@ -406,7 +407,7 @@ export class CameraControls {
|
|||||||
.sub(this.mouseDownPosition)
|
.sub(this.mouseDownPosition)
|
||||||
this.mouseDownPosition.copy(this.mouseNewPosition)
|
this.mouseDownPosition.copy(this.mouseNewPosition)
|
||||||
|
|
||||||
const interaction = this.getInteractionType(event)
|
let interaction = this.getInteractionType(event)
|
||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
|
|
||||||
// If there's a valid interaction and the mouse is moving,
|
// If there's a valid interaction and the mouse is moving,
|
||||||
@ -753,8 +754,6 @@ export class CameraControls {
|
|||||||
didChange = true
|
didChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.safeLookAtTarget(this.camera.up)
|
|
||||||
|
|
||||||
// Update the camera's matrices
|
// Update the camera's matrices
|
||||||
this.camera.updateMatrixWorld()
|
this.camera.updateMatrixWorld()
|
||||||
if (didChange || forceUpdate) {
|
if (didChange || forceUpdate) {
|
||||||
@ -1189,14 +1188,24 @@ export class CameraControls {
|
|||||||
this.deferReactUpdate(this.reactCameraProperties)
|
this.deferReactUpdate(this.reactCameraProperties)
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
getInteractionType = (event: MouseEvent) =>
|
getInteractionType = (
|
||||||
_getInteractionType(
|
event: MouseEvent
|
||||||
|
): CameraDragInteractionType_type | 'none' => {
|
||||||
|
const initialInteractionType = _getInteractionType(
|
||||||
this.interactionGuards,
|
this.interactionGuards,
|
||||||
event,
|
event,
|
||||||
this.enablePan,
|
this.enablePan,
|
||||||
this.enableRotate,
|
this.enableRotate,
|
||||||
this.enableZoom
|
this.enableZoom
|
||||||
)
|
)
|
||||||
|
if (
|
||||||
|
initialInteractionType === 'rotate' &&
|
||||||
|
this.engineCommandManager.settings.cameraOrbit === 'trackball'
|
||||||
|
) {
|
||||||
|
return 'rotatetrackball'
|
||||||
|
}
|
||||||
|
return initialInteractionType
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pure function helpers
|
// Pure function helpers
|
||||||
|
@ -119,6 +119,7 @@ export const ModelingMachineProvider = ({
|
|||||||
cameraProjection,
|
cameraProjection,
|
||||||
highlightEdges,
|
highlightEdges,
|
||||||
showScaleGrid,
|
showScaleGrid,
|
||||||
|
cameraOrbit,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1154,6 +1155,7 @@ export const ModelingMachineProvider = ({
|
|||||||
enableSSAO: enableSSAO.current,
|
enableSSAO: enableSSAO.current,
|
||||||
showScaleGrid: showScaleGrid.current,
|
showScaleGrid: showScaleGrid.current,
|
||||||
cameraProjection: cameraProjection.current,
|
cameraProjection: cameraProjection.current,
|
||||||
|
cameraOrbit: cameraOrbit.current,
|
||||||
},
|
},
|
||||||
token
|
token
|
||||||
)
|
)
|
||||||
@ -1183,6 +1185,13 @@ export const ModelingMachineProvider = ({
|
|||||||
editorManager.selectionRanges = modelingState.context.selectionRanges
|
editorManager.selectionRanges = modelingState.context.selectionRanges
|
||||||
}, [modelingState.context.selectionRanges])
|
}, [modelingState.context.selectionRanges])
|
||||||
|
|
||||||
|
// When changing camera modes reset the camera to the default orientation to correct
|
||||||
|
// the up vector otherwise the conconical orientation for the camera modes will be
|
||||||
|
// wrong
|
||||||
|
useEffect(() => {
|
||||||
|
sceneInfra.camControls.resetCameraPosition().catch(reportRejection)
|
||||||
|
}, [cameraOrbit.current])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
const onConnectionStateChanged = ({ detail }: CustomEvent) => {
|
||||||
// If we are in sketch mode we need to exit it.
|
// If we are in sketch mode we need to exit it.
|
||||||
|
@ -16,14 +16,15 @@ export function useSetupEngineManager(
|
|||||||
streamRef: React.RefObject<HTMLDivElement>,
|
streamRef: React.RefObject<HTMLDivElement>,
|
||||||
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
modelingSend: ReturnType<typeof useModelingContext>['send'],
|
||||||
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
modelingContext: ReturnType<typeof useModelingContext>['context'],
|
||||||
settings = {
|
settings: SettingsViaQueryString = {
|
||||||
pool: null,
|
pool: null,
|
||||||
theme: Themes.System,
|
theme: Themes.System,
|
||||||
highlightEdges: true,
|
highlightEdges: true,
|
||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
cameraProjection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
} as SettingsViaQueryString,
|
cameraOrbit: 'spherical',
|
||||||
|
},
|
||||||
token?: string
|
token?: string
|
||||||
) {
|
) {
|
||||||
const networkContext = useNetworkContext()
|
const networkContext = useNetworkContext()
|
||||||
|
@ -1389,6 +1389,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
cameraProjection: 'perspective',
|
cameraProjection: 'perspective',
|
||||||
|
cameraOrbit: 'spherical',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1437,6 +1438,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
enableSSAO: true,
|
enableSSAO: true,
|
||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
cameraProjection: 'orthographic',
|
cameraProjection: 'orthographic',
|
||||||
|
cameraOrbit: 'spherical',
|
||||||
},
|
},
|
||||||
// When passed, use a completely separate connecting code path that simply
|
// When passed, use a completely separate connecting code path that simply
|
||||||
// opens a websocket and this is a function that is called when connected.
|
// opens a websocket and this is a function that is called when connected.
|
||||||
|
@ -20,6 +20,7 @@ import { toSync } from 'lib/utils'
|
|||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||||
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
|
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
|
||||||
|
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A setting that can be set at the user or project level
|
* A setting that can be set at the user or project level
|
||||||
@ -380,6 +381,30 @@ export function createSettings() {
|
|||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
/**
|
||||||
|
* What methodology to use for orbiting the camera
|
||||||
|
*/
|
||||||
|
cameraOrbit: new Setting<CameraOrbitType>({
|
||||||
|
defaultValue: 'spherical',
|
||||||
|
hideOnLevel: 'project',
|
||||||
|
description: 'What methodology to use for orbiting the camera',
|
||||||
|
validate: (v) => ['spherical', 'trackball'].includes(v),
|
||||||
|
commandConfig: {
|
||||||
|
inputType: 'options',
|
||||||
|
defaultValueFromContext: (context) =>
|
||||||
|
context.modeling.cameraOrbit.current,
|
||||||
|
options: (cmdContext, settingsContext) =>
|
||||||
|
(['spherical', 'trackball'] as const).map((v) => ({
|
||||||
|
name: v.charAt(0).toUpperCase() + v.slice(1),
|
||||||
|
value: v,
|
||||||
|
isCurrent:
|
||||||
|
settingsContext.modeling.cameraOrbit.shouldShowCurrentLabel(
|
||||||
|
cmdContext.argumentsToSubmit.level as SettingsLevel,
|
||||||
|
v
|
||||||
|
),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}),
|
||||||
/**
|
/**
|
||||||
* Whether to highlight edges of 3D objects
|
* Whether to highlight edges of 3D objects
|
||||||
*/
|
*/
|
||||||
|
@ -4,6 +4,7 @@ import { AtLeast, PathValue, Paths } from 'lib/types'
|
|||||||
import { CommandArgumentConfig } from 'lib/commandTypes'
|
import { CommandArgumentConfig } from 'lib/commandTypes'
|
||||||
import { Themes } from 'lib/theme'
|
import { Themes } from 'lib/theme'
|
||||||
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
import { CameraProjectionType } from 'wasm-lib/kcl/bindings/CameraProjectionType'
|
||||||
|
import { CameraOrbitType } from 'wasm-lib/kcl/bindings/CameraOrbitType'
|
||||||
|
|
||||||
export interface SettingsViaQueryString {
|
export interface SettingsViaQueryString {
|
||||||
pool: string | null
|
pool: string | null
|
||||||
@ -12,6 +13,7 @@ export interface SettingsViaQueryString {
|
|||||||
enableSSAO: boolean
|
enableSSAO: boolean
|
||||||
showScaleGrid: boolean
|
showScaleGrid: boolean
|
||||||
cameraProjection: CameraProjectionType
|
cameraProjection: CameraProjectionType
|
||||||
|
cameraOrbit: CameraOrbitType
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum UnitSystem {
|
export enum UnitSystem {
|
||||||
|
@ -49,6 +49,7 @@ export function configurationToSettingsPayload(
|
|||||||
modeling: {
|
modeling: {
|
||||||
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
defaultUnit: configuration?.settings?.modeling?.base_unit,
|
||||||
cameraProjection: configuration?.settings?.modeling?.camera_projection,
|
cameraProjection: configuration?.settings?.modeling?.camera_projection,
|
||||||
|
cameraOrbit: configuration?.settings?.modeling?.camera_orbit,
|
||||||
mouseControls: mouseControlsToCameraSystem(
|
mouseControls: mouseControlsToCameraSystem(
|
||||||
configuration?.settings?.modeling?.mouse_controls
|
configuration?.settings?.modeling?.mouse_controls
|
||||||
),
|
),
|
||||||
|
@ -259,6 +259,9 @@ pub struct ModelingSettings {
|
|||||||
/// The projection mode the camera should use while modeling.
|
/// The projection mode the camera should use while modeling.
|
||||||
#[serde(default, alias = "cameraProjection", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "cameraProjection", skip_serializing_if = "is_default")]
|
||||||
pub camera_projection: CameraProjectionType,
|
pub camera_projection: CameraProjectionType,
|
||||||
|
/// The methodology the camera should use to orbit around the model.
|
||||||
|
#[serde(default, alias = "cameraOrbit", skip_serializing_if = "is_default")]
|
||||||
|
pub camera_orbit: CameraOrbitType,
|
||||||
/// The controls for how to navigate the 3D view.
|
/// The controls for how to navigate the 3D view.
|
||||||
#[serde(default, alias = "mouseControls", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "mouseControls", skip_serializing_if = "is_default")]
|
||||||
pub mouse_controls: MouseControlType,
|
pub mouse_controls: MouseControlType,
|
||||||
@ -415,6 +418,21 @@ pub enum CameraProjectionType {
|
|||||||
Orthographic,
|
Orthographic,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The types of camera orbit methods.
|
||||||
|
#[derive(Debug, Default, Eq, PartialEq, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, Display, FromStr)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[display(style = "snake_case")]
|
||||||
|
pub enum CameraOrbitType {
|
||||||
|
/// Orbit using a spherical camera movement.
|
||||||
|
#[default]
|
||||||
|
#[display("spherical")]
|
||||||
|
Spherical,
|
||||||
|
/// Orbit using a trackball camera movement.
|
||||||
|
#[display("trackball")]
|
||||||
|
Trackball,
|
||||||
|
}
|
||||||
|
|
||||||
/// Settings that affect the behavior of the KCL text editor.
|
/// Settings that affect the behavior of the KCL text editor.
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, JsonSchema, ts_rs::TS, PartialEq, Eq, Validate)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
@ -543,6 +561,8 @@ mod tests {
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
|
use crate::settings::types::CameraOrbitType;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
AppColor, AppSettings, AppTheme, AppearanceSettings, CameraProjectionType, CommandBarSettings, Configuration,
|
AppColor, AppSettings, AppTheme, AppearanceSettings, CameraProjectionType, CommandBarSettings, Configuration,
|
||||||
ModelingSettings, OnboardingStatus, ProjectSettings, Settings, TextEditorSettings, UnitLength,
|
ModelingSettings, OnboardingStatus, ProjectSettings, Settings, TextEditorSettings, UnitLength,
|
||||||
@ -594,6 +614,7 @@ textWrapping = true
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::In,
|
base_unit: UnitLength::In,
|
||||||
camera_projection: CameraProjectionType::Orthographic,
|
camera_projection: CameraProjectionType::Orthographic,
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
@ -656,6 +677,7 @@ includeSettings = false
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
@ -723,6 +745,7 @@ defaultProjectName = "projects-$nnn"
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: CameraOrbitType::Spherical,
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
@ -802,6 +825,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Mm,
|
base_unit: UnitLength::Mm,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: true.into(),
|
highlight_edges: true.into(),
|
||||||
show_debug_panel: false,
|
show_debug_panel: false,
|
||||||
|
@ -129,6 +129,7 @@ includeSettings = false
|
|||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
camera_projection: Default::default(),
|
camera_projection: Default::default(),
|
||||||
|
camera_orbit: Default::default(),
|
||||||
mouse_controls: Default::default(),
|
mouse_controls: Default::default(),
|
||||||
highlight_edges: Default::default(),
|
highlight_edges: Default::default(),
|
||||||
show_debug_panel: true,
|
show_debug_panel: true,
|
||||||
|
Reference in New Issue
Block a user