Skip to content

feat: Enhance camera orbiting - #1

Open
Luisao-official wants to merge 1 commit into
nivekmai:mainfrom
Luisao-official:main
Open

feat: Enhance camera orbiting#1
Luisao-official wants to merge 1 commit into
nivekmai:mainfrom
Luisao-official:main

Conversation

@Luisao-official

Copy link
Copy Markdown

This commit introduces a new orbiting functionality by implementing a polar coordinate system (constrained to the Z-axis). This new method makes the orbit camera a lil more intuitive

Key enhancements:

  • A new moveCamForAxes function for smoother, more predictable orbiting.
  • Improved exception handling with the addition of a log_exception function.
  • Constrained camera pitch to prevent unexpected camera rolls.

This commit introduces a new orbiting functionality by implementing a polar coordinate system (constrained to the Z-axis). This new method makes the orbit camera a lil more intuitive

Key enhancements:
- A new `moveCamForAxes` function for smoother, more predictable orbiting.
- Improved exception handling with the addition of a `log_exception` function.
- Constrained camera pitch to prevent unexpected camera rolls.
Comment thread Joystick Control.py
Comment thread Joystick Control.py
Comment thread Joystick Control.py
Comment thread Joystick Control.py

# starting global orbit state (simple polar coordinates)
# this is the reason why the default camera buttons are conflicting with the orbit
# when you press them, right now they ain't updating these states, so as you move the joystick, you go back to where these were

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*aren't

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shame on me

@Luisao-official

Copy link
Copy Markdown
Author

If I remember correctly, these except block changes were just to avoid letting bare excepts (mainly to get rid of the IDE warnings). My implementation was to take whatever text the fusion360 handler gave to that error and print on the API shell, centralizing that to the log_exception. All of those can be removed safely. The orbiting implementation itself was working correctly, with the caveat of not allowing full 360 loops on the XY plane, due to the reference (cross vector) being the Z axis (that cross vector breaks when the view angle is parallel to the Z axis)

@nivekmai nivekmai left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Required:

  • update yaw/pitch/orbit when orientCam called

Nits:

  • move global variable definitions to be like the others (globalize and init in run)
  • use pascalCase instead of lower_snake to have consistent variable/method naming

Other than that, it works well! If you wanna address the required and nits I'll merge, otherwise I'll just rewrite using your new maths and my preferred style.

Comment thread Joystick Control.py
cam.isSmoothTransition = True
else:
cam.viewOrientation = nextOrientation
setCam(cam)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you now store separate variables for the camera location instead of updating the existing camera, you need to update the local variables to match the camera when hitting HAT buttons.

Comment thread Joystick Control.py
@@ -582,28 +609,6 @@ def constrain(vector: Vector3D) -> Vector3D:
return vector

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All unused now right? Can delete

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes these were replaced by implementations inside the new moveCamForAxes itself

Comment thread Joystick Control.py

yaw_angle += rotateXAxis * 0.05
pitch_angle += rotateYAxis * 0.05

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract 0.05 to a constant (maybe update ROTATION_AXIS_SCALE since you don't use it anymore?)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah definitely. Also, that needed to be fine-tuned, but I personally found it quite fluid with that scaling factor

Comment thread Joystick Control.py
yaw_angle += rotateXAxis * 0.05
pitch_angle += rotateYAxis * 0.05

MAX_PITCH = radians(89.9)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be a constant up w/ the others?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's much closely related to the orbiting context, and that constant is just there to prevent the glitches when trying to loop on the object (over XY plane). In that regard, this constant could even be ditched and replaced with a hardcoded max angle value on the relevant section, maybe with a comment explaining that option (L442)

Comment thread Joystick Control.py
# you'll se that the 360 orbit is not smooth
pitch_angle = max(-MAX_PITCH, min(MAX_PITCH, pitch_angle))

orbit_radius *= (1 - zoomAxis * 0.05)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this use the same ROTATION_AXIS_SCALE constant as L435, L436?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nop that controls how fast the zoom occurs when a zooming signal is detected [-1,1] * zooming factor (in this case 0.05)

Comment thread Joystick Control.py
Comment on lines +446 to +451
cx = cos(yaw_angle) * cos(pitch_angle)
cy = sin(yaw_angle) * cos(pitch_angle)
cz = sin(pitch_angle)
orbit_offset = Vector3D.create(cx, cy, cz)
orbit_offset.normalize()
orbit_offset.scaleBy(orbit_radius)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to pull this out to a method (getOrbitOffset)

Comment thread Joystick Control.py
Comment on lines +465 to +472
if panXAxis != 0:
pan_h = side_vec.copy()
pan_h.scaleBy(scalePanAxis(panXAxis) * orbit_radius * PAN_ZOOM_COMPENSATION)
pan_vec.add(pan_h)
if panYAxis != 0:
pan_v = local_up.copy()
pan_v.scaleBy(scalePanAxis(panYAxis) * orbit_radius * PAN_ZOOM_COMPENSATION)
pan_vec.add(pan_v)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pull duplicated code to a method

...
    addScaledPanComponent(pan_vec, panXAxis, side_vec, orbit_radius) 
    addScaledPanComponent(pan_vec, panYAxis, local_up, orbit_radius) 
...

addScaledPanComponent(panVec, axis, inputVec, orbitRadius):
    panComponent = inputVec.copy()
    panComponent.scaleBy(scalePanAxis(axis) * orbitRadius * PAN_ZOOM_COMPENSATION)
    panVec.add(panComponent)

@Luisao-official

Copy link
Copy Markdown
Author

I'm having trouble making the new moveCamForAxes compatible with the jump-to-view button functionality. The outcome I'm getting is that when I press the button and wait for some time, the camera position remains persistent, but this is not consistent, and the tendency is for the camera to snap back to where moveCamForAxes left it.

My implementation was to basically reverse engineer the camera position that fusion gives, into polar terms, using atan/asin, and then updating the respective variables (which are now global, initialized inside run())

A brief summary is:

inside orientCam()


   global yawAngle, pitchAngle, orbitRadius
   # other orientCam() stuff...
   setCam(cam)
   
   doEvents()  # allowing Fusion to process the camera change
    app.activeViewport.refresh()  # ensuring (in hopes) that the view is updated

    # recalculating orbit state from the new camera position (from fusion)
    cam = app.activeViewport.camera
    target = cam.target.copy()
    eye = cam.eye.copy()
    view_vec = target.vectorTo(eye)

    orbitRadius = view_vec.length

    # Now it's the reverse-engineering part to get the yaw and pitch angles
    # for pitch, we can use the Z component of the view vector and the orbit radius

    pitchAngle = asin(view_vec.z / orbitRadius)

    # for yaw, we need to consider the X and Y components.
    # it's crucial to get the angle in the XY plane relative to the positive X-axis. (our reference direction)
    # we create a vector from the target to eye projected onto the XY plane.
    xy_projected_vec = Vector3D.create(view_vec.x, view_vec.y, 0)

    # just a little check to avoid issues with atan2(0,0)
    if xy_projected_vec.length > 1e-6:
        yawAngle = atan2(xy_projected_vec.y, xy_projected_vec.x)
    else:
        # If the camera is directly above or below the target, yaw is ambiguous.
        # We can keep the current yaw or set it to a default (like 0)
        # My approach is to keep the current yawAngle
        pass

My controller has got some serious drift, so that isn't helping either

@nivekmai

Copy link
Copy Markdown
Owner

My controller has got some serious drift

I'd assume that's why you see it "snap back after a second", probably the drift triggering a move, I find it only snapping back if I start moving joysticks. Maybe try increasing the dead zone to compensate for the drift?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants