Skip to content

Add View Cube to the screen #1134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion fission/src/Synthesis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ import GraphicsSettings from "./ui/panels/GraphicsSettingsPanel.tsx"
import MainMenuModal from "@/modals/MainMenuModal"

function Synthesis() {
const { openModal, closeModal, getActiveModalElement, registerModal } = useModalManager(initialModals)
const { openModal, closeModal, getActiveModalElement, registerModal, activeModalId } =
useModalManager(initialModals)
const { openPanel, closePanel, closeAllPanels, getActivePanelElements } = usePanelManager(initialPanels)
const { showTooltip } = useTooltipManager()

Expand Down Expand Up @@ -167,6 +168,7 @@ function Synthesis() {
openModal(modalId)
}}
closeModal={closeModal}
activeModalId={activeModalId}
>
<PanelControlProvider
key={"panel-control-provider"}
Expand Down
2 changes: 2 additions & 0 deletions fission/src/systems/preferences/PreferenceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type GlobalPreference =
| "RenderScoreboard"
| "SubsystemGravity"
| "SimAutoReconnect"
| "ShowViewCube"
| "MuteAllSound"
| "SFXVolume"

Expand All @@ -38,6 +39,7 @@ export const DefaultGlobalPreferences: { [key: string]: unknown } = {
RenderScoreboard: true,
SubsystemGravity: false,
SimAutoReconnect: false,
ShowViewCube: true,
MuteAllSound: false,
SFXVolume: 25,
}
Expand Down
50 changes: 50 additions & 0 deletions fission/src/systems/scene/CameraControls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,56 @@ export class CustomOrbitControls extends CameraControls {
}
}

public getCurrentCoordinates(): SphericalCoords {
return { ...this._coords }
}

public setTargetCoordinates(coords: Partial<SphericalCoords>) {
if (coords.theta !== undefined) this._nextCoords.theta = coords.theta
if (coords.phi !== undefined) this._nextCoords.phi = coords.phi
if (coords.r !== undefined) this._nextCoords.r = coords.r
}

public setImmediateCoordinates(coords: Partial<SphericalCoords>) {
if (coords.theta !== undefined) {
this._coords.theta = coords.theta
this._nextCoords.theta = coords.theta
}
if (coords.phi !== undefined) {
this._coords.phi = Math.min(CO_MAX_PHI, Math.max(CO_MIN_PHI, coords.phi))
this._nextCoords.phi = this._coords.phi
}
if (coords.r !== undefined) {
this._coords.r = Math.min(CO_MAX_ZOOM, Math.max(CO_MIN_ZOOM, coords.r))
this._nextCoords.r = this._coords.r
}
}

public animateToOrientation(theta: number, phi: number, duration: number = 500) {
const startCoords = { ...this._coords }
const targetCoords = { theta, phi, r: this._coords.r }

let startTime: number | null = null

const animate = (timestamp: number) => {
if (!startTime) startTime = timestamp

const elapsed = timestamp - startTime
const progress = Math.min(elapsed / duration, 1)

const easeOut = 1 - Math.pow(1 - progress, 3)

this._coords.theta = startCoords.theta + (targetCoords.theta - startCoords.theta) * easeOut
this._coords.phi = startCoords.phi + (targetCoords.phi - startCoords.phi) * easeOut

if (progress < 1) {
requestAnimationFrame(animate)
}
}

requestAnimationFrame(animate)
}

public update(deltaT: number): void {
deltaT = Math.max(1.0 / 60.0, Math.min(1 / 144.0, deltaT))

Expand Down
31 changes: 31 additions & 0 deletions fission/src/systems/scene/ScreenInteractionHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class ScreenInteractionHandler {

private _domElement: HTMLElement

private _globalPointerUp: (ev: PointerEvent) => void
private _isInteracting: boolean = false

public interactionStart: ((i: InteractionStart) => void) | undefined
public interactionEnd: ((i: InteractionEnd) => void) | undefined
public interactionMove: ((i: InteractionMove) => void) | undefined
Expand Down Expand Up @@ -99,6 +102,12 @@ class ScreenInteractionHandler {
this._contextMenu = e => e.preventDefault()
this._touchMove = e => e.preventDefault()

this._globalPointerUp = e => {
if (this._isInteracting) {
this.pointerUp(e)
}
}

this._domElement.addEventListener("pointermove", this._pointerMove)
this._domElement.addEventListener(
"wheel",
Expand Down Expand Up @@ -133,6 +142,11 @@ class ScreenInteractionHandler {
this._domElement.removeEventListener("pointerleave", this._pointerUp)

this._domElement.removeEventListener("touchmove", this._touchMove)

if (this._isInteracting) {
document.removeEventListener("pointerup", this._globalPointerUp)
this._isInteracting = false
}
}

/**
Expand Down Expand Up @@ -248,6 +262,11 @@ class ScreenInteractionHandler {
return
}

if (!this._isInteracting) {
this._isInteracting = true
document.addEventListener("pointerup", this._globalPointerUp)
}

if (e.pointerType == "touch") {
if (this._primaryTouch == undefined) {
this._primaryTouch = e.pointerId
Expand Down Expand Up @@ -327,8 +346,20 @@ class ScreenInteractionHandler {
if (e.button == SECONDARY_MOUSE_INTERACTION && !this._movementThresholdMet && this.contextMenu) {
this.contextMenu(end)
}

this._pointerPosition = undefined
}
}

if (
this._isInteracting &&
this._primaryTouch === undefined &&
this._secondaryTouch === undefined &&
this._pointerPosition === undefined
) {
this._isInteracting = false
document.removeEventListener("pointerup", this._globalPointerUp)
}
}

/**
Expand Down
30 changes: 30 additions & 0 deletions fission/src/ui/components/SceneOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,27 @@ import {
SceneOverlayTagEventKey,
} from "./SceneOverlayEvents"
import Label, { LabelSize } from "./Label"
import ViewCube from "./ViewCube"
import PreferencesSystem, { PreferenceEvent } from "@/systems/preferences/PreferencesSystem"
import { useModalControlContext } from "@/ui/helpers/UseModalManager"

const tagMap = new Map<number, SceneOverlayTag>()

function SceneOverlay() {
/* State to determine if the overlay is disabled */
const [isDisabled, setIsDisabled] = useState<boolean>(false)

/* State to determine if the ViewCube should be shown */
const [showViewCube, setShowViewCube] = useState<boolean>(
PreferencesSystem.getGlobalPreference<boolean>("ShowViewCube")
)

/* Get the active modal context to check if main menu is open */
const { activeModalId } = useModalControlContext()

/* Check if the main menu modal is active */
const isMainMenuOpen = activeModalId === "main-menu"

/* h1 text for each tagMap tag */
const [components, updateComponents] = useReducer(() => {
if (isDisabled) return <></> // if the overlay is disabled, return nothing
Expand Down Expand Up @@ -86,6 +100,21 @@ function SceneOverlay() {
}
}, [])

/* Update ViewCube visibility when preferences change */
useEffect(() => {
const handlePreferenceChange = (e: PreferenceEvent) => {
if (e.prefName === "ShowViewCube") {
setShowViewCube(e.prefValue as boolean)
}
}

PreferencesSystem.addEventListener(handlePreferenceChange)

return () => {
window.removeEventListener("preferenceChanged", handlePreferenceChange as EventListener)
}
}, [])

/* Render the overlay as a box that spans the entire screen and does not intercept any user interaction */
return (
<Box
Expand All @@ -102,6 +131,7 @@ function SceneOverlay() {
}}
>
{components ?? <></>}
{showViewCube && !isMainMenuOpen && <ViewCube position={{ top: 20, right: 20 }} />}
</Box>
)
}
Expand Down
Loading
Loading