Skip to content

Commit 49ad60f

Browse files
committed
GPU Display: Improve animations and support blending of multiple animation modes
1 parent 50b0ce0 commit 49ad60f

7 files changed

Lines changed: 103 additions & 59 deletions

File tree

‎GPU/GPUTracking/display/GPUDisplay.h‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class GPUDisplay : public GPUDisplayInterface
150150
};
151151

152152
struct animationVector { // Animation point, its time is in mCfgC.animationPoints.values
153-
float v[8]; // Camera, depending on cfgL.animationMode (setAnimationPoint)
153+
float v[6]; // Camera: x, y, z, pitch, yaw, roll (as mXYZ, mAngle), independent of the animation mode
154154
GPUSettingsDisplayLight cfgL; // Settings at the point
155155
};
156156

@@ -205,13 +205,14 @@ class GPUDisplay : public GPUDisplayInterface
205205
void rotateCamera(const float (&angles)[3]); // Yaw, pitch, roll in degrees relative to the view, as the keyboard
206206
hmm_mat4 cameraRotation(const float (&angles)[3]) const; // Rotation of rotateCamera, applied to the view matrix also for keyboard / mouse
207207
void resetAnimation();
208-
void removeAnimationPoint(int32_t i = -1); // -1: the selected one, the last one if none is selected
209-
void StopAnimation(); // Ends a running animation, camera and settings stay as they are; for changed settings (checkAndApplyConfigChanges), scene reset, changes of the animation points
210-
void setCameraFromAnimationPoint(const animationVector& p); // Camera stored in the point, as the animation shows it there
211-
void selectAnimationPoint(int32_t i); // -1: none, otherwise also loads its light settings and camera
212-
void setAnimationPointsFromGUI(const GPUDisplayMultiSliderValue& points); // New times from the Qt GUI: reorders the points, selection
213-
void updateAnimationPointsGUI(); // Range end of the GUI multi slider: last time + animationDelay, progress 0 without animation
214-
void animationViewMatrix(float (&vals)[8], int32_t mode, hmm_mat4& viewMatrix) const; // Camera of interpolated / stored animation values (v[1..8]), as animateCamera
208+
void removeAnimationPoint(int32_t i = -1); // -1: the selected one, the last one if none is selected
209+
void StopAnimation(); // Ends a running animation, camera and settings stay as they are; for changed settings (checkAndApplyConfigChanges), scene reset, changes of the animation points
210+
void setCameraFromAnimationPoint(const animationVector& p); // Camera stored in the point, as the animation shows it there
211+
void selectAnimationPoint(int32_t i); // -1: none, otherwise also loads its light settings and camera
212+
void setAnimationPointsFromGUI(const GPUDisplayMultiSliderValue& points); // New times from the Qt GUI: reorders the points, selection
213+
void updateAnimationPointsGUI(); // Range end of the GUI multi slider: last time + animationDelay, progress 0 without animation
214+
void animationPointValues(const animationVector& p, int32_t mode, const float* last, float (&vals)[8]); // The 8 interpolated values of the point in this animation mode, angles / quaternion continuous to the values of the previous point (last, nullptr if none)
215+
void animationViewMatrix(float (&vals)[8], int32_t mode, hmm_mat4& viewMatrix) const; // Camera of interpolated / stored animation values (v[1..8]), as animateCamera
215216
void startAnimation();
216217
int32_t animateCamera(bool suspended, float& animateTime, float& mixSlaveImage, hmm_mat4& nextViewMatrix);
217218
void showInfo(const char* info);
@@ -336,7 +337,7 @@ class GPUDisplay : public GPUDisplayInterface
336337
int32_t mAnimationMixBase = -1; // Target keyframe of the current config blend (mixing), -1 = none
337338
int32_t mAnimationExport = 0;
338339
vecpod<animationVector> mAnimationVectors;
339-
opengl_spline mAnimationSplines[8];
340+
std::vector<std::array<opengl_spline, 8>> mAnimationSplines; // By animation mode: the 8 values of animationPointValues
340341

341342
char mInfoText2[1024];
342343
std::string mGUIInfoStatus; // Latest status line (FPS etc.) for the Qt GUI

‎GPU/GPUTracking/display/GPUDisplayControl.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ BeginHiddenConfig(GPUDisplayControl, control)
4141
AddVariable(guiDarkMode, int8_t, -1) // Dark mode of the Qt GUI, -1 = not yet set: the GUI reports the system scheme when it starts
4242
AddVariable(guiScale, float, 1.f) // Scale factor of text and elements of the Qt GUI (on top of the system scaling), Ctrl + mouse wheel / Ctrl + 0 in the GUI
4343
AddVariable(showConsole, bool, false) // Console shown (^ key): all other keys go to the console
44+
AddVariable(applyToAllPoints, bool, false) // A change of the animation mode is applied to all animation points
4445
AddVariable(animationPoints, GPUDisplayMultiSliderValue, GPUDisplayMultiSliderValue()) // Times of the animation points (GPUDisplay::mAnimationVectors) and the selected one, shown in the Qt GUI
4546
AddVariable(dummy, bool, false) // dummy value to reference for keys that do not change variables
4647
EndConfig()

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfig.cxx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,6 @@ void GPUDisplay::checkAndApplyConfigChanges()
237237
if (oldCfgR.useGLIndirectDraw != mCfgR.useGLIndirectDraw) {
238238
mUpdateEventData = true;
239239
}
240-
if (oldCfgL.animationMode != mCfgL.animationMode) {
241-
resetAnimation();
242-
}
243240
if (oldCfgH.trackFilter != mCfgH.trackFilter) {
244241
mUpdateTrackFilter = true;
245242
}
@@ -254,6 +251,11 @@ void GPUDisplay::checkAndApplyConfigChanges()
254251
std::fill(std::begin(mFrontend->mKeys), std::end(mFrontend->mKeys), false);
255252
}
256253
}
254+
if (mCfgC.applyToAllPoints && oldCfgL.animationMode != mCfgL.animationMode) { // Also for keys; the animation's own changes are in mOldCfg, so this is only user input
255+
for (auto& p : mAnimationVectors) {
256+
p.cfgL.animationMode = mCfgL.animationMode;
257+
}
258+
}
257259
if (mCfgC.animationPoints.selected >= 0 && mCfgC.animationPoints.selected < (int32_t)mAnimationVectors.size()) { // Update animation settings of selected point
258260
mAnimationVectors[mCfgC.animationPoints.selected].cfgL = mCfgL;
259261
}

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigKeys.inc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ void GPUDisplay::buildConfigKeys()
250250
val = 0;
251251
}
252252
}), //
253-
"Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode (resets)");
253+
"Start Animation, Add / remove Animation point, Reset Points, Cycle animation camera mode");
254254

255255
mFrontend->add_key_binding(
256256
KEY_LAMBDA('>', "ANIMATION_CHANGE_CONFIG", mCfgC, animationChangeConfig, val ^= 1;),

‎GPU/GPUTracking/display/frontend/GPUDisplayFrontendConfigQtGUI.inc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ void GPUDisplay::buildConfigQtGUI()
179179
GUI_LAMBDA("Remove Point", mCfgC, dummy, GUI_BUTTON, removeAnimationPoint(); SetInfo("Removed Animation point", 1);),
180180
GUI_LAMBDA("Reset Animation", mCfgC, dummy, GUI_BUTTON, resetAnimation(); SetInfo("Reset Animation points", 1);),
181181
GUI_LAMBDA("Animation Mode", mCfgL, animationMode, GUI_DROPDOWN_NAMES(ANIMATION_MODE_NAMES_SHORT), guiAssign(val, guiVal); if (!mCfgC.guiPrintInfo) { SetInfo("Animation mode set to %s", ANIMATION_MODE_NAMES_LONG[val].c_str()); }), // clang-format off
182+
GUI_LAMBDA("Apply to all", mCfgC, applyToAllPoints, GUI_CHECKBOX),
182183
guiVertical{true},
183184
GUI_LAMBDA("Points", mCfgC, animationPoints, GUI_MULTI_SLIDER(0.05), setAnimationPointsFromGUI(std::get<GPUDisplayMultiSliderValue>(guiVal));), // clang-format on
184185
"Screenshots / Animation", true);

‎GPU/GPUTracking/display/helpers/GPUDisplayAnimation.cxx‎

Lines changed: 85 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,55 +27,64 @@ const std::array<std::string, 7> GPUDisplay::ANIMATION_MODE_NAMES_LONG = {"Posit
2727

2828
constexpr hmm_mat4 MY_HMM_FROM(float (&v)[16]) { return {{{v[0], v[1], v[2], v[3]}, {v[4], v[5], v[6], v[7]}, {v[8], v[9], v[10], v[11]}, {v[12], v[13], v[14], v[15]}}}; }
2929

30+
static hmm_mat4 cameraViewMatrix(const float (&xyz)[3], const float (&angles)[3]) // Inverse of calcXYZ
31+
{
32+
const float toDeg = 180.f / CAMath::Pi();
33+
return HMM_Rotate(-angles[2] * toDeg, {{0, 0, 1}}) * HMM_Rotate(-angles[0] * toDeg, {{1, 0, 0}}) * HMM_Rotate(angles[1] * toDeg, {{0, 1, 0}}) * HMM_Translate({{-xyz[0], -xyz[1], -xyz[2]}});
34+
}
35+
3036
void GPUDisplay::setAnimationPoint()
3137
{
3238
StopAnimation();
33-
animationVector p;
34-
const animationVector* last = mAnimationVectors.size() ? &mAnimationVectors.back() : nullptr;
3539
auto& times = mCfgC.animationPoints.values;
36-
if (mCfgL.animationMode & 4) // Spherical
40+
times.emplace_back(times.size() ? times.back() + ((int32_t)(mCfgC.animationDelay * 20)) / 20.f : 0.f);
41+
mAnimationVectors.push_back({{mXYZ[0], mXYZ[1], mXYZ[2], mAngle[0], mAngle[1], mAngle[2]}, mCfgL});
42+
mCfgC.animationPoints.selected = -1;
43+
}
44+
45+
void GPUDisplay::animationPointValues(const animationVector& p, int32_t mode, const float* last, float (&vals)[8])
46+
{
47+
const float xyz[3] = {p.v[0], p.v[1], p.v[2]}, angles[3] = {p.v[3], p.v[4], p.v[5]};
48+
if (mode & 4) // Spherical
3749
{
38-
float rxy = sqrtf(mXYZ[0] * mXYZ[0] + mXYZ[2] * mXYZ[2]);
39-
float anglePhi = atan2f(mXYZ[0], mXYZ[2]);
40-
float angleTheta = atan2f(mXYZ[1], rxy);
50+
float rxy = sqrtf(xyz[0] * xyz[0] + xyz[2] * xyz[2]);
51+
float anglePhi = atan2f(xyz[0], xyz[2]);
52+
float angleTheta = atan2f(xyz[1], rxy);
4153
if (last) {
42-
mAnimationCloseAngle(anglePhi, last->v[1]);
43-
mAnimationCloseAngle(angleTheta, last->v[2]);
54+
mAnimationCloseAngle(anglePhi, last[1]);
55+
mAnimationCloseAngle(angleTheta, last[2]);
4456
}
45-
p.v[0] = 0;
46-
p.v[1] = anglePhi;
47-
p.v[2] = angleTheta;
57+
vals[0] = 0;
58+
vals[1] = anglePhi;
59+
vals[2] = angleTheta;
4860
} else {
4961
for (int32_t i = 0; i < 3; i++) {
50-
p.v[i] = mXYZ[i];
62+
vals[i] = xyz[i];
5163
}
5264
// Cartesian
5365
}
54-
p.v[3] = sqrtf(mXYZ[0] * mXYZ[0] + mXYZ[1] * mXYZ[1] + mXYZ[2] * mXYZ[2]);
55-
if (mCfgL.animationMode & 1) // Euler-angles
66+
vals[3] = sqrtf(xyz[0] * xyz[0] + xyz[1] * xyz[1] + xyz[2] * xyz[2]);
67+
if (mode & 1) // Euler-angles
5668
{
5769
for (int32_t i = 0; i < 3; i++) {
58-
float newangle = mAngle[i];
70+
float newangle = angles[i];
5971
if (last) {
60-
mAnimationCloseAngle(newangle, last->v[i + 4]);
72+
mAnimationCloseAngle(newangle, last[i + 4]);
6173
}
62-
p.v[i + 4] = newangle;
74+
vals[i + 4] = newangle;
6375
}
64-
p.v[7] = 0;
76+
vals[7] = 0;
6577
} else { // Quaternions
6678
float v[4];
67-
createQuaternionFromMatrix(v, &mViewMatrix->Elements[0][0]);
79+
const hmm_mat4 viewMatrix = cameraViewMatrix(xyz, angles);
80+
createQuaternionFromMatrix(v, &viewMatrix.Elements[0][0]);
6881
if (last) {
69-
mAnimateCloseQuaternion(v, last->v[4], last->v[5], last->v[6], last->v[7]);
82+
mAnimateCloseQuaternion(v, last[4], last[5], last[6], last[7]);
7083
}
7184
for (int32_t i = 0; i < 4; i++) {
72-
p.v[i + 4] = v[i];
85+
vals[i + 4] = v[i];
7386
}
7487
}
75-
times.emplace_back(last ? times.back() + ((int32_t)(mCfgC.animationDelay * 20)) / 20.f : 0.f);
76-
p.cfgL = mCfgL;
77-
mAnimationVectors.emplace_back(p);
78-
mCfgC.animationPoints.selected = -1;
7988
}
8089

8190
static hmm_mat4 lookAtMatrix(const float (&eye)[3], const float (&target)[3]) // HMM_LookAt with y up, z up when looking along y (the cross product with y would be 0)
@@ -87,8 +96,7 @@ static hmm_mat4 lookAtMatrix(const float (&eye)[3], const float (&target)[3]) //
8796

8897
void GPUDisplay::setCamera(const float (&xyz)[3], const float (&angles)[3])
8998
{
90-
const float toDeg = 180.f / CAMath::Pi();
91-
*mViewMatrix = HMM_Rotate(-angles[2] * toDeg, {{0, 0, 1}}) * HMM_Rotate(-angles[0] * toDeg, {{1, 0, 0}}) * HMM_Rotate(angles[1] * toDeg, {{0, 1, 0}}) * HMM_Translate({{-xyz[0], -xyz[1], -xyz[2]}}); // Inverse of calcXYZ
99+
*mViewMatrix = cameraViewMatrix(xyz, angles);
92100
calcXYZ(&mViewMatrix->Elements[0][0]);
93101
}
94102

@@ -163,15 +171,7 @@ void GPUDisplay::selectAnimationPoint(int32_t i)
163171
setCameraFromAnimationPoint(mAnimationVectors[i]);
164172
}
165173

166-
void GPUDisplay::setCameraFromAnimationPoint(const animationVector& p)
167-
{
168-
float vals[8];
169-
std::copy(p.v, p.v + 8, vals);
170-
hmm_mat4 viewMatrix = HMM_Mat4d(1.f);
171-
animationViewMatrix(vals, p.cfgL.animationMode, viewMatrix);
172-
*mViewMatrix = viewMatrix;
173-
calcXYZ(&mViewMatrix->Elements[0][0]);
174-
}
174+
void GPUDisplay::setCameraFromAnimationPoint(const animationVector& p) { setCamera({p.v[0], p.v[1], p.v[2]}, {p.v[3], p.v[4], p.v[5]}); }
175175

176176
void GPUDisplay::StopAnimation()
177177
{
@@ -237,12 +237,22 @@ void GPUDisplay::runAnimation()
237237

238238
void GPUDisplay::startAnimation()
239239
{
240-
vecpod<float> y(mAnimationVectors.size());
241-
for (int32_t i = 0; i < 8; i++) {
240+
mAnimationSplines.resize(ANIMATION_MODE_NAMES_SHORT.size());
241+
vecpod<float> y[8];
242+
for (auto& yi : y) {
243+
yi.resize(mAnimationVectors.size());
244+
}
245+
for (uint32_t mode = 0; mode < mAnimationSplines.size(); mode++) { // Splines for all modes, points may have different modes
246+
float vals[8], previous[8];
242247
for (uint32_t k = 0; k < mAnimationVectors.size(); k++) {
243-
y[k] = mAnimationVectors[k].v[i];
248+
animationPointValues(mAnimationVectors[k], mode, k ? previous : nullptr, vals); // Continuous to the previous point in time order
249+
for (int32_t i = 0; i < 8; i++) {
250+
y[i][k] = previous[i] = vals[i];
251+
}
252+
}
253+
for (int32_t i = 0; i < 8; i++) {
254+
mAnimationSplines[mode][i].create(mCfgC.animationPoints.values, y[i]);
244255
}
245-
mAnimationSplines[i].create(mCfgC.animationPoints.values, y);
246256
}
247257
mAnimationTimer.ResetStart();
248258
mAnimationFrame = 0;
@@ -276,10 +286,6 @@ int32_t GPUDisplay::animateCamera(bool suspended, float& animateTime, float& mix
276286
if (!mAnimate) {
277287
return 0;
278288
}
279-
float vals[8];
280-
for (int32_t i = 0; i < 8; i++) {
281-
vals[i] = mAnimationSplines[i].evaluate(time);
282-
}
283289
if (mCfgC.animationChangeConfig && animateTime < 0) {
284290
int32_t base = 0;
285291
const auto& times = mCfgC.animationPoints.values;
@@ -312,7 +318,41 @@ int32_t GPUDisplay::animateCamera(bool suspended, float& animateTime, float& mix
312318
}
313319
}
314320

315-
animationViewMatrix(vals, mCfgL.animationMode, nextViewMatrix);
321+
const auto& times = mCfgC.animationPoints.values;
322+
uint32_t k = 0; // Interval [times[k], times[k + 1]] of the time
323+
while (k + 2 < times.size() && time > times[k + 1]) {
324+
k++;
325+
}
326+
const int32_t modeA = mAnimationVectors[k].cfgL.animationMode, modeB = mAnimationVectors[k + 1].cfgL.animationMode;
327+
auto cameraOfMode = [&](int32_t mode, hmm_mat4& viewMatrix) {
328+
float vals[8];
329+
for (int32_t i = 0; i < 8; i++) {
330+
vals[i] = mAnimationSplines[mode][i].evaluate(time);
331+
}
332+
animationViewMatrix(vals, mode, viewMatrix);
333+
};
334+
if (modeA == modeB) {
335+
cameraOfMode(modeA, nextViewMatrix);
336+
} else { // Modes of the points differ: blend the cameras of both modes, position linearly, rotation as quaternion, with sin^2 for a smooth start and end
337+
hmm_mat4 viewA = nextViewMatrix, viewB = nextViewMatrix;
338+
cameraOfMode(modeA, viewA);
339+
cameraOfMode(modeB, viewB);
340+
const float s = sinf(std::clamp((time - times[k]) / (times[k + 1] - times[k]), 0.f, 1.f) * CAMath::Pi() / 2), w = s * s;
341+
float qA[4], qB[4], vals[8];
342+
createQuaternionFromMatrix(qA, &viewA.Elements[0][0]);
343+
createQuaternionFromMatrix(qB, &viewB.Elements[0][0]);
344+
const float sign = qA[0] * qB[0] + qA[1] * qB[1] + qA[2] * qB[2] + qA[3] * qB[3] < 0 ? -1.f : 1.f; // Shorter way
345+
for (int32_t i = 0; i < 3; i++) { // Camera position, as calcXYZ
346+
const float posA = -(viewA.Elements[i][0] * viewA.Elements[3][0] + viewA.Elements[i][1] * viewA.Elements[3][1] + viewA.Elements[i][2] * viewA.Elements[3][2]);
347+
const float posB = -(viewB.Elements[i][0] * viewB.Elements[3][0] + viewB.Elements[i][1] * viewB.Elements[3][1] + viewB.Elements[i][2] * viewB.Elements[3][2]);
348+
vals[i] = (1 - w) * posA + w * posB;
349+
}
350+
vals[3] = 0;
351+
for (int32_t i = 0; i < 4; i++) {
352+
vals[4 + i] = (1 - w) * qA[i] + w * sign * qB[i]; // Normalized by animationViewMatrix
353+
}
354+
animationViewMatrix(vals, 0, nextViewMatrix); // Mode 0: Cartesian position, quaternion
355+
}
316356
return 1;
317357
}
318358

‎GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,6 @@ void GPUDisplay::LoadGLAnimation()
135135
mCfgC.animationPoints.selected = -1;
136136
if (ok) {
137137
mCfgL = cfg;
138-
std::get<1>(mOldCfg).animationMode = cfg.animationMode; // Otherwise the mode change resets the animation which was just loaded
139138
updateConfig();
140139
} else {
141140
resetAnimation(); // Read error after the size check: no incomplete path

0 commit comments

Comments
 (0)