/* Camera shake loader and interpolator. Version 5 2021-07-13. Loading a single track: CameraShakeTrack stillTrack; bool success = loadCameraShakeTrack("shaketracks/still.bin", &stillTrack); Loading all the tracks: enum ShakeType { CAMSHAKE_STILL = 0, CAMSHAKE_ONE_HAND = 1, CAMSHAKE_SLOW_WALK = 2, CAMSHAKE_FAST_WALK = 3, CAMSHAKE_MAX = 4, }; static CameraShakeTrack shakeTracks[CAMSHAKE_MAX]; bool loadAllTracks() { bool shakeSuccess = true; shakeSuccess = shakeSuccess && loadCameraShakeTrack("shaketracks/still.bin", &shakeTracks[CAMSHAKE_STILL]); shakeSuccess = shakeSuccess && loadCameraShakeTrack("shaketracks/onehand.bin", &shakeTracks[CAMSHAKE_ONE_HAND]); shakeSuccess = shakeSuccess && loadCameraShakeTrack("shaketracks/slowwalk.bin", &shakeTracks[CAMSHAKE_SLOW_WALK]); shakeSuccess = shakeSuccess && loadCameraShakeTrack("shaketracks/fastwalk.bin", &shakeTracks[CAMSHAKE_FAST_WALK]); if (!shakeSuccess) { puts("failed to load camera shake data"); return false; } return true; } Animating a shake track: CameraShakePoint shake = interpolateTrack(track, secs); // now do something with shake.x, shake.y and shake.roll Animating a camera pose with the 'shakePose()' helper function: // Assume we have a camera struct 'cam' with a normalized direction 'pose.dir' and // basis vectors 'cam.right' and 'cam.up'. void animate(Camera& cam, double time) { CameraShakeTrack& track = slowWalkTrack; // Copy 'cam' into 'pose'. CameraShakePose pose; pose.dir = { cam.dir.x, cam.dir.y, cam.dir.z }; pose.up = { cam.up.x, cam.up.y, cam.up.z }; pose.right = { cam.right.x, cam.right.y, cam.right.z }; float strength = 2.0; // change this // Twist 'pose' according to the given track. shakePose(track, time, strength, &pose); // Copy 'pose' back into 'cam'. cam.dir = vec3(pose.dir.x, pose.dir.y, pose.dir.z); cam.up = vec3(pose.up.x, pose.up.y, pose.up.z); cam.right = vec3(pose.right.x, pose.right.y, pose.right.z); } */ #pragma once #include #include #pragma pack(push, 1) struct CameraShakeHeader { char fourcc[4]; uint32_t version; uint32_t num_frames; uint32_t frame_width; // in pixels uint32_t frame_height; float focal_length; // in mm float framerate; }; struct CameraShakePoint { float x = 0.f; // displacement to right, in pixels float y = 0.f; // displacement upwards, in pixels float roll = 0.f; // roll in radians float cosine = 0.f; // roll expressed as a normalized direction vector float sine = 0.f; }; #pragma pack(pop) static_assert(sizeof(CameraShakePoint) == 5 * sizeof(float)); struct CameraShakeTrack { CameraShakeHeader hdr; std::vector points; }; // A simple orthogonal camera basis used in 'shakePose()'. struct CameraShakePose { struct Vec { float x, y, z; }; Vec dir; Vec up; Vec right; }; // Loads a binary camera shake track file into 'outTrack'. // Returns true on success. bool loadCameraShakeTrack(const char* path, CameraShakeTrack* outTrack); // Linearly interpolates the given track at time 'secs'. CameraShakePoint interpolateTrack(const CameraShakeTrack& track, double secs); // Shakes the given camera pose with the interpolated animation. Modifies 'pose' in-place. // The pose is expected to be in a right-handed coordinate space like in OpenGL. // The parameter 'strength' should be in range [0, 10]. void shakePose(const CameraShakeTrack& track, double secs, double strength, CameraShakePose* pose);