animate.cpp 17 KB
Newer Older
TheNumbat's avatar
TheNumbat committed
1
2
3

#include "animate.h"
#include "../scene/renderer.h"
TheNumbat's avatar
TheNumbat committed
4
#include "manager.h"
TheNumbat's avatar
TheNumbat committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

#include <tuple>

namespace Gui {

Camera Anim_Camera::at(float t) const {
    Camera ret(dim);
    auto [p, r, f, a] = splines.at(t);
    Vec3 dir = r.rotate(Vec3{0.0f, 0.0f, -1.0f});
    ret.look_at(p + dir, p);
    ret.set_fov(f);
    ret.set_ar(a);
    return ret;
}

TheNumbat's avatar
TheNumbat committed
20
21
void Anim_Camera::set(float t, const Camera &cam) {
    splines.set(t, cam.pos(), Quat::euler(Mat4::rotate_z_to(cam.center() - cam.pos()).to_euler()),
TheNumbat's avatar
TheNumbat committed
22
23
24
                cam.get_fov(), cam.get_ar());
}

TheNumbat's avatar
TheNumbat committed
25
26
27
void Animate::update_dim(Vec2 dim) { ui_camera.dim(dim); }

bool Animate::keydown(Widgets &widgets, Undo &undo, Scene_ID sel, SDL_Keysym key) {
TheNumbat's avatar
TheNumbat committed
28
29

#ifdef __APPLE__
TheNumbat's avatar
TheNumbat committed
30
    if (key.sym == SDLK_BACKSPACE && key.mod & KMOD_GUI) {
TheNumbat's avatar
TheNumbat committed
31
#else
TheNumbat's avatar
TheNumbat committed
32
33
34
35
36
37
    if (key.sym == SDLK_DELETE) {
#endif
        if (joint_select)
            undo.del_bone(sel, joint_select);
        else if (sel)
            undo.del_obj(sel);
TheNumbat's avatar
TheNumbat committed
38
39
    }

TheNumbat's avatar
TheNumbat committed
40
    if (key.sym == SDLK_SPACE) {
TheNumbat's avatar
TheNumbat committed
41
42
43
44
45
46
47
48
        playing = !playing;
        last_frame = SDL_GetPerformanceCounter();
        return true;
    }

    return false;
}

TheNumbat's avatar
TheNumbat committed
49
void Animate::render(Scene &scene, Scene_Maybe obj_opt, Widgets &widgets, Camera &user_cam) {
TheNumbat's avatar
TheNumbat committed
50
51

    Mat4 view = user_cam.get_view();
TheNumbat's avatar
TheNumbat committed
52
    auto &R = Renderer::get();
TheNumbat's avatar
TheNumbat committed
53
54
55

    ui_camera.render(view);

TheNumbat's avatar
TheNumbat committed
56
57
58
    if (visualize_splines)
        for (auto &e : spline_cache)
            R.lines(e.second, view);
TheNumbat's avatar
TheNumbat committed
59

TheNumbat's avatar
TheNumbat committed
60
61
62
    if (!obj_opt.has_value())
        return;
    Scene_Item &item = obj_opt.value();
TheNumbat's avatar
TheNumbat committed
63

TheNumbat's avatar
TheNumbat committed
64
65
66
67
    if (item.is<Scene_Light>()) {
        Scene_Light &light = item.get<Scene_Light>();
        if (light.is_env())
            return;
TheNumbat's avatar
TheNumbat committed
68
69
    }

TheNumbat's avatar
TheNumbat committed
70
71
72
    Pose &pose = item.pose();
    float scale = std::min((user_cam.pos() - pose.pos).norm() / 5.5f, 10.0f);

TheNumbat's avatar
TheNumbat committed
73
74
    item.render(view);

TheNumbat's avatar
TheNumbat committed
75
76
77
    if (item.is<Scene_Object>() && item.get<Scene_Object>().armature.has_bones()) {

        Scene_Object &obj = item.get<Scene_Object>();
TheNumbat's avatar
TheNumbat committed
78
        joint_id_offset = scene.used_ids();
TheNumbat's avatar
TheNumbat committed
79
80
81
82
        obj.armature.render(view * obj.pose.transform(), joint_select, false, true,
                            joint_id_offset);

        if (!joint_select) {
TheNumbat's avatar
TheNumbat committed
83
84
85
            R.begin_outline();
            BBox box = obj.bbox();
            obj.render(view, false, true);
TheNumbat's avatar
TheNumbat committed
86
87
            obj.armature.outline(view * obj.pose.transform(), joint_select, false, true, box,
                                 joint_id_offset);
TheNumbat's avatar
TheNumbat committed
88
89
90
91
92
93
94
95
96
            R.end_outline(view, box);
        } else {
            widgets.active = Widget_Type::rotate;
        }

    } else {
        R.outline(view, item);
    }

TheNumbat's avatar
TheNumbat committed
97
98
99
    if (joint_select) {

        Scene_Object &obj = item.get<Scene_Object>();
TheNumbat's avatar
TheNumbat committed
100
101
        widgets.render(view, pose.transform() * obj.armature.posed_base_of(joint_select), scale);

TheNumbat's avatar
TheNumbat committed
102
103
104
    } else {
        widgets.render(view, pose.pos, scale);
    }
TheNumbat's avatar
TheNumbat committed
105
106
}

TheNumbat's avatar
TheNumbat committed
107
void Animate::make_spline(Scene_ID id, const Anim_Pose &pose) {
TheNumbat's avatar
TheNumbat committed
108

TheNumbat's avatar
TheNumbat committed
109
110
    if (!pose.splines.any())
        return;
TheNumbat's avatar
TheNumbat committed
111
112

    auto entry = spline_cache.find(id);
TheNumbat's avatar
TheNumbat committed
113
    if (entry == spline_cache.end()) {
TheNumbat's avatar
TheNumbat committed
114
115
116
        std::tie(entry, std::ignore) = spline_cache.insert({id, GL::Lines()});
    }

TheNumbat's avatar
TheNumbat committed
117
    GL::Lines &lines = entry->second;
TheNumbat's avatar
TheNumbat committed
118
119
120
    lines.clear();

    Vec3 prev = pose.at(0.0f).pos;
TheNumbat's avatar
TheNumbat committed
121
122
    for (int i = 1; i < max_frame; i++) {

TheNumbat's avatar
TheNumbat committed
123
124
125
126
127
128
129
130
131
132
133
        float f = (float)i;
        float c = (float)(i % 20) / 19.0f;
        Vec3 cur = pose.at(f).pos;
        lines.add(prev, cur, Vec3{c, c, 1.0f});
        prev = cur;
    }
}

void Animate::camera_spline() {

    auto entry = spline_cache.find(0);
TheNumbat's avatar
TheNumbat committed
134
    if (entry == spline_cache.end()) {
TheNumbat's avatar
TheNumbat committed
135
136
137
        std::tie(entry, std::ignore) = spline_cache.insert({0, GL::Lines()});
    }

TheNumbat's avatar
TheNumbat committed
138
    GL::Lines &lines = entry->second;
TheNumbat's avatar
TheNumbat committed
139
140
141
    lines.clear();

    Vec3 prev = anim_camera.at(0.0f).pos();
TheNumbat's avatar
TheNumbat committed
142
    for (int i = 1; i < max_frame; i++) {
TheNumbat's avatar
TheNumbat committed
143
144
145
146
147
148
149
150
        float f = (float)i;
        float c = (float)(i % 20) / 19.0f;
        Vec3 cur = anim_camera.at(f).pos();
        lines.add(prev, cur, Vec3{c, c, 1.0f});
        prev = cur;
    }
}

TheNumbat's avatar
TheNumbat committed
151
152
153
void Animate::UIsidebar(Manager &manager, Undo &undo, Scene_Maybe obj_opt, Camera &user_cam) {

    if (joint_select) {
TheNumbat's avatar
TheNumbat committed
154
        ImGui::Text("Edit Joint");
TheNumbat's avatar
TheNumbat committed
155
        if (ImGui::DragFloat3("Pose", joint_select->pose.data, 1.0f, 0.0f, 0.0f, "%.2f"))
TheNumbat's avatar
TheNumbat committed
156
            obj_opt.value().get().get<Scene_Object>().set_pose_dirty();
TheNumbat's avatar
TheNumbat committed
157
        if (ImGui::IsItemActivated())
TheNumbat's avatar
TheNumbat committed
158
            old_euler = joint_select->pose;
TheNumbat's avatar
TheNumbat committed
159
        if (ImGui::IsItemDeactivatedAfterEdit() && old_euler != joint_select->pose) {
TheNumbat's avatar
TheNumbat committed
160
161
162
163
164
165
            joint_select->pose = joint_select->pose.range(0.0f, 360.0f);
            undo.pose_bone(obj_opt.value().get().id(), joint_select, old_euler);
        }
        ImGui::Separator();
    }

TheNumbat's avatar
TheNumbat committed
166
    if (ui_camera.UI(undo, user_cam)) {
TheNumbat's avatar
TheNumbat committed
167
        camera_selected = true;
TheNumbat's avatar
TheNumbat committed
168
169
        if (obj_opt.has_value())
            prev_selected = obj_opt.value().get().id();
TheNumbat's avatar
TheNumbat committed
170
171
172
    }
}

TheNumbat's avatar
TheNumbat committed
173
174
void Animate::end_transform(Undo &undo, Scene_Item &obj) {
    if (joint_select) {
TheNumbat's avatar
TheNumbat committed
175
176
177
178
179
180
181
182
        undo.pose_bone(obj.id(), joint_select, old_euler);
    } else {
        undo.update_pose(obj.id(), old_pose);
    }
    old_pose = {};
    old_p_to_j = Mat4::I;
}

TheNumbat's avatar
TheNumbat committed
183
184
185
186
Vec3 Animate::selected_pos(Scene_Item &item) {
    if (joint_select) {
        return item.pose().transform() *
               item.get<Scene_Object>().armature.posed_base_of(joint_select);
TheNumbat's avatar
TheNumbat committed
187
188
189
190
    }
    return item.pose().pos;
}

TheNumbat's avatar
TheNumbat committed
191
192
193
194
void Animate::apply_transform(Widgets &widgets, Scene_Item &item) {
    if (joint_select) {
        Scene_Object &obj = item.get<Scene_Object>();
        Vec3 euler = widgets.apply_action(old_pose).euler;
TheNumbat's avatar
TheNumbat committed
195
        joint_select->pose = (old_p_to_j * Mat4::euler(euler)).to_euler();
TheNumbat's avatar
TheNumbat committed
196
197
        obj.set_pose_dirty();
    } else {
TheNumbat's avatar
TheNumbat committed
198
199
200
201
        item.pose() = widgets.apply_action(old_pose);
    }
}

TheNumbat's avatar
TheNumbat committed
202
203
bool Animate::select(Scene &scene, Widgets &widgets, Scene_ID selected, Scene_ID id, Vec3 cam,
                     Vec2 spos, Vec3 dir) {
TheNumbat's avatar
TheNumbat committed
204

TheNumbat's avatar
TheNumbat committed
205
    if (widgets.want_drag()) {
TheNumbat's avatar
TheNumbat committed
206

TheNumbat's avatar
TheNumbat committed
207
        if (joint_select) {
TheNumbat's avatar
TheNumbat committed
208

TheNumbat's avatar
TheNumbat committed
209
210
            Scene_Object &obj = scene.get_obj(selected);
            Vec3 base = obj.pose.transform() * obj.armature.posed_base_of(joint_select);
TheNumbat's avatar
TheNumbat committed
211
            widgets.start_drag(base, cam, spos, dir);
TheNumbat's avatar
TheNumbat committed
212

TheNumbat's avatar
TheNumbat committed
213
214
215
216
217
218
219
220
            Mat4 j_to_p = obj.pose.transform() * obj.armature.joint_to_posed(joint_select);
            old_euler = joint_select->pose;
            old_pose.euler = j_to_p.to_euler();
            j_to_p = j_to_p * Mat4::euler(old_euler).inverse();
            old_p_to_j = j_to_p.inverse();

        } else {

TheNumbat's avatar
TheNumbat committed
221
222
            Scene_Item &item = scene.get(selected).value();
            Pose &pose = item.pose();
TheNumbat's avatar
TheNumbat committed
223
224
225
226
227
            widgets.start_drag(pose.pos, cam, spos, dir);
            old_pose = pose;
        }

        return false;
TheNumbat's avatar
TheNumbat committed
228
229
    }

TheNumbat's avatar
TheNumbat committed
230
    Scene_Maybe mb = scene.get(selected);
TheNumbat's avatar
TheNumbat committed
231
    if (mb.has_value()) {
TheNumbat's avatar
TheNumbat committed
232

TheNumbat's avatar
TheNumbat committed
233
234
        Scene_Item &item = mb.value().get();
        if (item.is<Scene_Object>()) {
TheNumbat's avatar
TheNumbat committed
235

TheNumbat's avatar
TheNumbat committed
236
237
            Scene_Object &obj = item.get<Scene_Object>();
            if (id >= joint_id_offset && obj.armature.has_bones()) {
TheNumbat's avatar
TheNumbat committed
238
239
240

                Scene_ID j_id = id - joint_id_offset;
                joint_select = obj.armature.get_joint(j_id);
TheNumbat's avatar
TheNumbat committed
241
                if (joint_select) {
TheNumbat's avatar
TheNumbat committed
242
243
244
245
246
247
                    widgets.active = Widget_Type::rotate;
                }
                return false;
            }
        }
    }
TheNumbat's avatar
TheNumbat committed
248

TheNumbat's avatar
TheNumbat committed
249
    if (id >= n_Widget_IDs) {
TheNumbat's avatar
TheNumbat committed
250
        if (id == selected) {
TheNumbat's avatar
TheNumbat committed
251
252
253
254
255
256
257
258
259
            joint_select = nullptr;
        }
        return true;
    }

    joint_select = nullptr;
    return false;
}

TheNumbat's avatar
TheNumbat committed
260
261
void Animate::timeline(Manager &manager, Undo &undo, Scene &scene, Scene_Maybe obj,
                       Camera &user_cam) {
TheNumbat's avatar
TheNumbat committed
262
263
264

    // NOTE(max): this is pretty messy
    //      Would be good to add the ability to set per-component keyframes
TheNumbat's avatar
TheNumbat committed
265
    //      ^ I started with that but it was hard to make work with assimp and
TheNumbat's avatar
TheNumbat committed
266
267
268
269
270
271
    //        generally made everything a lot messier.

    ImVec2 size = ImGui::GetWindowSize();

    ImGui::Columns(2);
    ImGui::SetColumnWidth(0, 150.0f);
TheNumbat's avatar
TheNumbat committed
272
273
274

    if (!playing) {
        if (ImGui::Button("Play")) {
TheNumbat's avatar
TheNumbat committed
275
276
277
278
            playing = true;
            last_frame = SDL_GetPerformanceCounter();
        }
    } else {
TheNumbat's avatar
TheNumbat committed
279
        if (ImGui::Button("Stop")) {
TheNumbat's avatar
TheNumbat committed
280
281
282
283
284
            playing = false;
        }
    }

    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
285
    if (ImGui::Button("Render")) {
TheNumbat's avatar
TheNumbat committed
286
287
288
289
290
291
292
        ui_render.open();
    }
    ui_render.animate(scene, ui_camera, user_cam, max_frame);

    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {0.0f, 0.0f});
    ImGui::Dummy({1.0f, 4.0f});
    ImGui::PopStyleVar();
TheNumbat's avatar
TheNumbat committed
293
294

    if (ImGui::Button("Add Frames")) {
TheNumbat's avatar
TheNumbat committed
295
296
        max_frame += 3 * frame_rate;
    }
TheNumbat's avatar
TheNumbat committed
297
    if (ImGui::Button("Crop End")) {
TheNumbat's avatar
TheNumbat committed
298
299
        max_frame = current_frame + 1;
        current_frame = std::min(current_frame, max_frame - 1);
TheNumbat's avatar
TheNumbat committed
300
        scene.for_items([this](Scene_Item &item) {
TheNumbat's avatar
TheNumbat committed
301
            item.animation().splines.crop((float)max_frame);
TheNumbat's avatar
TheNumbat committed
302
            if (item.is<Scene_Object>())
TheNumbat's avatar
TheNumbat committed
303
                item.get<Scene_Object>().armature.crop((float)max_frame);
TheNumbat's avatar
TheNumbat committed
304
            else if (item.is<Scene_Light>())
TheNumbat's avatar
TheNumbat committed
305
306
307
308
309
310
311
312
313
314
315
316
                item.get<Scene_Light>().lanim.splines.crop((float)max_frame);
            make_spline(item.id(), item.animation());
        });
        set_time(scene, (float)current_frame);
    }
    ImGui::SliderInt("Rate", &frame_rate, 1, 240);
    frame_rate = clamp(frame_rate, 1, 240);

    ImGui::Checkbox("Draw Splines", &visualize_splines);

    ImGui::NextColumn();

TheNumbat's avatar
TheNumbat committed
317
318
319
    Scene_Item *select = nullptr;
    if (obj.has_value()) {
        Scene_Item &item = obj.value();
TheNumbat's avatar
TheNumbat committed
320
        select = &item;
TheNumbat's avatar
TheNumbat committed
321
        if (item.id() != prev_selected) {
TheNumbat's avatar
TheNumbat committed
322
323
324
325
326
327
328
329
330
            camera_selected = false;
            prev_selected = item.id();
        }
    }

    bool frame_changed = false;

    ImGui::Text("Keyframe:");
    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
331
332
    if (ImGui::Button("Clear")) {
        if (camera_selected) {
TheNumbat's avatar
TheNumbat committed
333
334
            anim_camera.splines.erase((float)current_frame);
            camera_spline();
TheNumbat's avatar
TheNumbat committed
335
        } else if (select) {
TheNumbat's avatar
TheNumbat committed
336
337
338
339
340
            select->animation().splines.erase((float)current_frame);
            make_spline(select->id(), select->animation());
        }
    }

TheNumbat's avatar
TheNumbat committed
341
342
    auto set_item = [&, this](Scene_Item &item) {
        if (item.is<Scene_Object>()) {
TheNumbat's avatar
TheNumbat committed
343
            undo.anim_pose_bones(item.id(), (float)current_frame);
TheNumbat's avatar
TheNumbat committed
344
345
        }
        if (item.is<Scene_Light>()) {
TheNumbat's avatar
TheNumbat committed
346
347
348
349
350
351
            undo.anim_light(item.id(), (float)current_frame);
        }
        make_spline(item.id(), item.animation());
    };

    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
352
353
    if (ImGui::Button("Set")) {
        if (camera_selected) {
TheNumbat's avatar
TheNumbat committed
354
355
            undo.anim_camera(anim_camera, (float)current_frame, ui_camera.get());
            camera_spline();
TheNumbat's avatar
TheNumbat committed
356
        } else if (select) {
TheNumbat's avatar
TheNumbat committed
357
358
359
360
            set_item(*select);
        }
    }
    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
361
    if (ImGui::Button("Set All")) {
TheNumbat's avatar
TheNumbat committed
362
363
364
365
366
367
368
369
        undo.anim_camera(anim_camera, (float)current_frame, ui_camera.get());
        camera_spline();
        scene.for_items(set_item);
    }

    ImGui::Separator();
    ImGui::Dummy({74.0f, 1.0f});
    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
370
    if (ImGui::SliderInt("Frame", &current_frame, 0, max_frame - 1)) {
TheNumbat's avatar
TheNumbat committed
371
372
        frame_changed = true;
    }
TheNumbat's avatar
TheNumbat committed
373
374
    ImGui::BeginChild("Timeline", {size.x - 20.0f, size.y - 80.0f}, false,
                      ImGuiWindowFlags_HorizontalScrollbar);
TheNumbat's avatar
TheNumbat committed
375
376

    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {0.0f, 0.0f});
TheNumbat's avatar
TheNumbat committed
377

TheNumbat's avatar
TheNumbat committed
378
379
380
381
382
383
384
385
386
387
    const int name_chars = 6;
    std::vector<bool> frames;
    std::vector<Scene_ID> live_ids;

    {
        frames.clear();
        frames.resize(max_frame);

        std::string name = "Camera";
        ImVec2 sz = ImGui::CalcTextSize(name.c_str());
TheNumbat's avatar
TheNumbat committed
388
389
390
        if (camera_selected)
            ImGui::TextColored({Color::outline.x, Color::outline.y, Color::outline.z, 1.0f}, "%s",
                               name.c_str());
TheNumbat's avatar
TheNumbat committed
391
392
393
394
395
396
397
398
399
        else
            ImGui::Text("%s", name.c_str());
        ImGui::SameLine();
        ImGui::Dummy({80.0f - sz.x, 1.0f});
        ImGui::SameLine();
        ImGui::PushID("##CAMERA_");

        std::set<float> keys = anim_camera.splines.keys();

TheNumbat's avatar
TheNumbat committed
400
        for (float f : keys) {
TheNumbat's avatar
TheNumbat committed
401
            int frame = (int)std::round(f);
TheNumbat's avatar
TheNumbat committed
402
            if (frame >= 0 && frame < max_frame)
TheNumbat's avatar
TheNumbat committed
403
404
405
                frames[frame] = true;
        }

TheNumbat's avatar
TheNumbat committed
406
407
408
        for (int i = 0; i < max_frame; i++) {
            if (i > 0)
                ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
409
410
411
412
413
            ImGui::PushID(i);

            bool color = false;
            std::string label = "_";

TheNumbat's avatar
TheNumbat committed
414
            if (i == current_frame) {
TheNumbat's avatar
TheNumbat committed
415
416
417
418
                ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
                color = true;
            }

TheNumbat's avatar
TheNumbat committed
419
            if (frames[i]) {
TheNumbat's avatar
TheNumbat committed
420
                label = "*";
TheNumbat's avatar
TheNumbat committed
421
                if (i != current_frame) {
TheNumbat's avatar
TheNumbat committed
422
                    color = true;
TheNumbat's avatar
TheNumbat committed
423
424
                    ImGui::PushStyleColor(ImGuiCol_Button,
                                          ImGui::GetColorU32(ImGuiCol_ButtonHovered));
TheNumbat's avatar
TheNumbat committed
425
426
                }
            }
TheNumbat's avatar
TheNumbat committed
427
            if (ImGui::SmallButton(label.c_str())) {
TheNumbat's avatar
TheNumbat committed
428
429
430
431
                current_frame = i;
                frame_changed = true;
                camera_selected = true;
            }
TheNumbat's avatar
TheNumbat committed
432
433
434
            if (color)
                ImGui::PopStyleColor();
            ImGui::PopID();
TheNumbat's avatar
TheNumbat committed
435
436
437
438
        }
        ImGui::PopID();
    }

TheNumbat's avatar
TheNumbat committed
439
    scene.for_items([&, this](Scene_Item &item) {
TheNumbat's avatar
TheNumbat committed
440
441
442
        frames.clear();
        frames.resize(max_frame);

TheNumbat's avatar
TheNumbat committed
443
        std::string name = const_cast<const Scene_Item &>(item).name();
TheNumbat's avatar
TheNumbat committed
444
        name.resize(name_chars);
TheNumbat's avatar
TheNumbat committed
445

TheNumbat's avatar
TheNumbat committed
446
        ImVec2 size = ImGui::CalcTextSize(name.c_str());
TheNumbat's avatar
TheNumbat committed
447
448
449
        if (!camera_selected && select && item.id() == select->id())
            ImGui::TextColored({Color::outline.x, Color::outline.y, Color::outline.z, 1.0f}, "%s",
                               name.c_str());
TheNumbat's avatar
TheNumbat committed
450
451
452
453
454
455
456
457
458
459
        else
            ImGui::Text("%s", name.c_str());
        ImGui::SameLine();
        ImGui::Dummy({80.0f - size.x, 1.0f});
        ImGui::SameLine();

        ImGui::PushID(item.id());

        Anim_Pose animation = item.animation();
        std::set<float> keys = animation.splines.keys();
TheNumbat's avatar
TheNumbat committed
460
        if (item.is<Scene_Light>()) {
TheNumbat's avatar
TheNumbat committed
461
462
463
            std::set<float> more_keys = item.get<Scene_Light>().lanim.splines.keys();
            keys.insert(more_keys.begin(), more_keys.end());
        }
TheNumbat's avatar
TheNumbat committed
464
        if (item.is<Scene_Object>()) {
TheNumbat's avatar
TheNumbat committed
465
466
467
468
            std::set<float> more_keys = item.get<Scene_Object>().armature.keys();
            keys.insert(more_keys.begin(), more_keys.end());
        }

TheNumbat's avatar
TheNumbat committed
469
        for (float f : keys) {
TheNumbat's avatar
TheNumbat committed
470
            int frame = (int)std::round(f);
TheNumbat's avatar
TheNumbat committed
471
            if (frame >= 0 && frame < max_frame)
TheNumbat's avatar
TheNumbat committed
472
473
474
                frames[frame] = true;
        }

TheNumbat's avatar
TheNumbat committed
475
476
477
        for (int i = 0; i < max_frame; i++) {
            if (i > 0)
                ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
478
479
480
481
482
            ImGui::PushID(i);

            bool color = false;
            std::string label = "_";

TheNumbat's avatar
TheNumbat committed
483
            if (i == current_frame) {
TheNumbat's avatar
TheNumbat committed
484
485
486
487
                ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
                color = true;
            }

TheNumbat's avatar
TheNumbat committed
488
            if (frames[i]) {
TheNumbat's avatar
TheNumbat committed
489
                label = "*";
TheNumbat's avatar
TheNumbat committed
490
                if (i != current_frame) {
TheNumbat's avatar
TheNumbat committed
491
                    color = true;
TheNumbat's avatar
TheNumbat committed
492
493
                    ImGui::PushStyleColor(ImGuiCol_Button,
                                          ImGui::GetColorU32(ImGuiCol_ButtonHovered));
TheNumbat's avatar
TheNumbat committed
494
495
496
                }
            }

TheNumbat's avatar
TheNumbat committed
497
            if (ImGui::SmallButton(label.c_str())) {
TheNumbat's avatar
TheNumbat committed
498
499
500
501
502
                current_frame = i;
                frame_changed = true;
                camera_selected = false;
                manager.set_select(item.id());
            }
TheNumbat's avatar
TheNumbat committed
503
504
            if (color)
                ImGui::PopStyleColor();
TheNumbat's avatar
TheNumbat committed
505
506
            ImGui::PopID();
        }
TheNumbat's avatar
TheNumbat committed
507

TheNumbat's avatar
TheNumbat committed
508
509
510
511
512
513
        live_ids.push_back(item.id());

        ImGui::SameLine();
        ImGui::Dummy({142.0f, 1.0f});
        ImGui::PopID();
    });
TheNumbat's avatar
TheNumbat committed
514

TheNumbat's avatar
TheNumbat committed
515
516
517
518
    ImGui::PopStyleVar();
    ImGui::EndChild();

    std::unordered_map<Scene_ID, GL::Lines> new_cache;
TheNumbat's avatar
TheNumbat committed
519
    for (Scene_ID i : live_ids) {
TheNumbat's avatar
TheNumbat committed
520
        auto entry = spline_cache.find(i);
TheNumbat's avatar
TheNumbat committed
521
        if (entry != spline_cache.end()) {
TheNumbat's avatar
TheNumbat committed
522
523
524
525
526
            new_cache[i] = std::move(entry->second);
        }
    }
    spline_cache = std::move(new_cache);

TheNumbat's avatar
TheNumbat committed
527
528
    if (frame_changed)
        update(scene);
TheNumbat's avatar
TheNumbat committed
529
530
}

TheNumbat's avatar
TheNumbat committed
531
532
Camera Animate::set_time(Scene &scene, float time) {

TheNumbat's avatar
TheNumbat committed
533
534
    current_frame = (int)time;

TheNumbat's avatar
TheNumbat committed
535
    scene.for_items([time](Scene_Item &item) { item.set_time(time); });
TheNumbat's avatar
TheNumbat committed
536
537

    Camera cam = anim_camera.at(time);
TheNumbat's avatar
TheNumbat committed
538
    if (anim_camera.splines.any()) {
TheNumbat's avatar
TheNumbat committed
539
540
541
542
543
544
545
546
547
548
549
550
551
        ui_camera.load(cam.center(), cam.pos(), cam.get_ar(), cam.get_fov());
    } else {
        cam = ui_camera.get();
    }
    return cam;
}

void Animate::set(int n_frames, int fps) {
    max_frame = n_frames;
    frame_rate = fps;
    current_frame = std::min(current_frame, max_frame - 1);
}

TheNumbat's avatar
TheNumbat committed
552
Anim_Camera &Animate::camera() { return anim_camera; }
TheNumbat's avatar
TheNumbat committed
553

TheNumbat's avatar
TheNumbat committed
554
const Anim_Camera &Animate::camera() const { return anim_camera; }
TheNumbat's avatar
TheNumbat committed
555

TheNumbat's avatar
TheNumbat committed
556
float Animate::fps() const { return (float)frame_rate; }
TheNumbat's avatar
TheNumbat committed
557

TheNumbat's avatar
TheNumbat committed
558
int Animate::n_frames() const { return max_frame; }
TheNumbat's avatar
TheNumbat committed
559

TheNumbat's avatar
TheNumbat committed
560
std::string Animate::pump_output(Scene &scene) { return ui_render.step(*this, scene); }
TheNumbat's avatar
TheNumbat committed
561

TheNumbat's avatar
TheNumbat committed
562
void Animate::refresh(Scene &scene) { set_time(scene, (float)current_frame); }
TheNumbat's avatar
TheNumbat committed
563
564
565
566
567
568

void Animate::clear() {
    anim_camera.splines.clear();
    joint_select = nullptr;
}

TheNumbat's avatar
TheNumbat committed
569
void Animate::update(Scene &scene) {
TheNumbat's avatar
TheNumbat committed
570
571
572

    Uint64 time = SDL_GetPerformanceCounter();

TheNumbat's avatar
TheNumbat committed
573
574
575
    if (playing) {
        if ((time - last_frame) * frame_rate / SDL_GetPerformanceFrequency()) {
            if (current_frame == max_frame - 1) {
TheNumbat's avatar
TheNumbat committed
576
577
578
579
580
581
582
583
584
                playing = false;
                current_frame = 0;
            } else {
                current_frame++;
            }
            last_frame = time;
        }
    }

TheNumbat's avatar
TheNumbat committed
585
    if (displayed_frame != current_frame) {
TheNumbat's avatar
TheNumbat committed
586
587
588
589
590
        set_time(scene, (float)current_frame);
        displayed_frame = current_frame;
    }
}

TheNumbat's avatar
TheNumbat committed
591
} // namespace Gui