Programming camera motion

Hello! I’m trying to figure out if it’s possible to program camera motion into a simulation animation. I’m mainly using the GUI but could switch over to python scripting if that’s more flexible. But staying with the GUI, even if I were to go frame-by-frame and load a new .vc3 camera settings file for each frame, that would suffice for me. But I need to have a sense of how the camera controls are calculated, how does that transfer to what I’m seeing in the .vc3 file. To keep things simple, I’d like to keep the camera centered on a point, and rotate the camera 360 degrees around the object. Thanks.

1 Like

Hi there,

The camera controls can be found in the Scene->Viewpoint tab, where you can apply values for the position, camera direction, and up vector. This can also be done through the Python API through the “camera” module, which has setters and getters for these parameters as well.

That said, Vapor2 used to support keyframing and we currently have an open issue for it on github. Our requirements are entirely driven by our users, so if you comment on that issue about your need for this feature, we will get to it faster than otherwise. I want this feature too, so please help push us towards implementing it!

PS - I’m a big fan of your work!

1 Like

Hi Scott,

I’ve prepared a python script that, given a Vapor session file, will render a rotating view while also optionally animating the data. Please let me know if this works for you or if you have any questions.

One additional note is that Vapor’s python API does not currently support ZFP compressed datasets out of the box, so you will need to point it to a ZFP HDF5 plugin. The vapor application has this bundled so you could use that one by running export HDF5_PLUGIN_PATH=/Applications/vapor.app/Contents/share/plugins right before running the python script.

import cv2, os
from vapor.session import Session
from vapor.animation import Animation
from numpy import cross, eye, dot, radians, asarray, array
from scipy.linalg import expm, norm
# Hot patch
Animation.SaveMP4 = lambda self, path, framerate: [[v.write(cv2.cvtColor(array(f), cv2.COLOR_RGB2BGR)) for f in self._frames] for v in [cv2.VideoWriter(path, cv2.VideoWriter_fourcc(*'avc1'), framerate, self._frames[0].size)]]
UseValueFromSessionFile = None

###############################################################################
#                              Configuration
###############################################################################
# Make sure you have Vapor's python API installed (macOS and Linux):
# conda create -y -n vapor -c conda-forge -c ncar-vapor vapor && conda activate vapor

session_path = "/Users/stasj/Work/sessions/maycontrol-volume.vs3"
output = "animation.mp4"
video_framerate = 30
video_resolution = (640, 480)
data_timestep_framerate = 6  # set to zero to disable
duration = 4  # seconds
rotate_speed = 90  # deg/s
rotation_axis = [0,0,1]  # Z (up)
rotation_center = UseValueFromSessionFile  # Can be overridden with [x,y,z] coordinates
save_individual_frames = False

###############################################################################
###############################################################################


session_path, output = [os.path.expanduser(p) for p in (session_path, output)]
n_frames = video_framerate * duration

ses = Session()
ses.Load(session_path)
ses.SetResolution(*video_resolution)
cam = ses.GetCamera()
pos, dir, up, tgt = [asarray(x) for x in [cam.GetPosition(), cam.GetDirection(), cam.GetUp(), cam.GetTarget()]]
if rotation_center:
    tgt = asarray(rotation_center)

def rotation_matrix(axis, theta):
    return expm(cross(eye(3), axis / norm(axis) * theta))

anim = Animation(ses)
for i in range(0, n_frames):
    print(f"Rendering... [{'#'*round(40*i/(n_frames-1))}{' '*round(40*(1-i/(n_frames-1)))}] {100*(i+1)/n_frames:.0f}%", end="\r" if i < n_frames-1 else "\n")

    ses.SetTimestep(int(data_timestep_framerate * i / video_framerate))

    M = rotation_matrix(rotation_axis, radians(rotate_speed) * i / video_framerate)
    cam.SetPosition(dot(M, pos - tgt) + tgt)
    cam.SetDirection(dot(M, dir))
    cam.SetUp(dot(M, up))
    anim.CaptureFrame()
    if save_individual_frames:
        ses.Render(f"{output}_{i:04}.png")

anim.SaveMP4(output, video_framerate)

Example Output
animation

2 Likes

Wow, thank you so much StasJ, I will give this a try and report back with any successes or questions. Much appreciated.

Hi Scott,

Just wanted to update you that we have released a new version of the Vapor python API (3.8.3) that now supports ZFP compressed datasets out of the box.

Best,
Stas

2 Likes

It worked! Thank you so much.
:partying_face: :partying_face: :partying_face: