animate.cpp 19.1 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

TheNumbat's avatar
TheNumbat committed
29
    if (key.sym == SDLK_SPACE) {
TheNumbat's avatar
TheNumbat committed
30
31
32
33
34
35
36
37
        playing = !playing;
        last_frame = SDL_GetPerformanceCounter();
        return true;
    }

    return false;
}

TheNumbat's avatar
TheNumbat committed
38
void Animate::render(Scene &scene, Scene_Maybe obj_opt, Widgets &widgets, Camera &user_cam) {
TheNumbat's avatar
TheNumbat committed
39
40

    Mat4 view = user_cam.get_view();
TheNumbat's avatar
TheNumbat committed
41
    auto &R = Renderer::get();
TheNumbat's avatar
TheNumbat committed
42
43
44

    ui_camera.render(view);

TheNumbat's avatar
TheNumbat committed
45
46
47
    if (visualize_splines)
        for (auto &e : spline_cache)
            R.lines(e.second, view);
TheNumbat's avatar
TheNumbat committed
48

TheNumbat's avatar
TheNumbat committed
49
50
51
    if (!obj_opt.has_value())
        return;
    Scene_Item &item = obj_opt.value();
TheNumbat's avatar
TheNumbat committed
52

TheNumbat's avatar
TheNumbat committed
53
54
55
56
    if (item.is<Scene_Light>()) {
        Scene_Light &light = item.get<Scene_Light>();
        if (light.is_env())
            return;
TheNumbat's avatar
TheNumbat committed
57
58
    }

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

TheNumbat's avatar
TheNumbat committed
62
63
    item.render(view);

TheNumbat's avatar
TheNumbat committed
64
65
66
    if (item.is<Scene_Object>() && item.get<Scene_Object>().armature.has_bones()) {

        Scene_Object &obj = item.get<Scene_Object>();
TheNumbat's avatar
TheNumbat committed
67
        joint_id_offset = scene.used_ids();
TheNumbat's avatar
TheNumbat committed
68
        obj.armature.render(view * obj.pose.transform(), joint_select, handle_select, false, true,
TheNumbat's avatar
TheNumbat committed
69
70
                            joint_id_offset);

TheNumbat's avatar
TheNumbat committed
71
        if (!joint_select && !handle_select) {
TheNumbat's avatar
TheNumbat committed
72
73
74
            R.begin_outline();
            BBox box = obj.bbox();
            obj.render(view, false, true);
TheNumbat's avatar
TheNumbat committed
75
            obj.armature.outline(view * obj.pose.transform(), false, true, box, joint_id_offset);
TheNumbat's avatar
TheNumbat committed
76
            R.end_outline(view, box);
TheNumbat's avatar
TheNumbat committed
77
78
        } else if (handle_select) {
            widgets.active = Widget_Type::move;
TheNumbat's avatar
TheNumbat committed
79
80
81
82
83
84
85
86
        } else {
            widgets.active = Widget_Type::rotate;
        }

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

TheNumbat's avatar
TheNumbat committed
87
88
89
90
91
92
    if (handle_select) {

        Scene_Object &obj = item.get<Scene_Object>();
        widgets.render(view, pose.transform() * (handle_select->target + obj.armature.base()), scale);

    } else if (joint_select) {
TheNumbat's avatar
TheNumbat committed
93
94

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

TheNumbat's avatar
TheNumbat committed
97
98
99
    } else {
        widgets.render(view, pose.pos, scale);
    }
TheNumbat's avatar
TheNumbat committed
100
101
}

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

TheNumbat's avatar
TheNumbat committed
104
105
    if (!pose.splines.any())
        return;
TheNumbat's avatar
TheNumbat committed
106
107

    auto entry = spline_cache.find(id);
TheNumbat's avatar
TheNumbat committed
108
    if (entry == spline_cache.end()) {
TheNumbat's avatar
TheNumbat committed
109
110
111
        std::tie(entry, std::ignore) = spline_cache.insert({id, GL::Lines()});
    }

TheNumbat's avatar
TheNumbat committed
112
    GL::Lines &lines = entry->second;
TheNumbat's avatar
TheNumbat committed
113
114
115
    lines.clear();

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

TheNumbat's avatar
TheNumbat committed
118
119
120
121
122
123
124
125
126
127
128
        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
129
    if (entry == spline_cache.end()) {
TheNumbat's avatar
TheNumbat committed
130
131
132
        std::tie(entry, std::ignore) = spline_cache.insert({0, GL::Lines()});
    }

TheNumbat's avatar
TheNumbat committed
133
    GL::Lines &lines = entry->second;
TheNumbat's avatar
TheNumbat committed
134
135
136
    lines.clear();

    Vec3 prev = anim_camera.at(0.0f).pos();
TheNumbat's avatar
TheNumbat committed
137
    for (int i = 1; i < max_frame; i++) {
TheNumbat's avatar
TheNumbat committed
138
139
140
141
142
143
144
145
        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
146
147
void Animate::UIsidebar(Manager &manager, Undo &undo, Scene_Maybe obj_opt, Camera &user_cam) {

TheNumbat's avatar
TheNumbat committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
    if (handle_select) {

        Scene_ID id = obj_opt.value().get().id();

        ImGui::Text("Edit IK Handle");
        ImGui::DragFloat3("Pos", handle_select->target.data, 0.1f, 0.0f, 0.0f, "%.2f");
        if (ImGui::IsItemActivated())
            old_euler = handle_select->target;
        if (ImGui::IsItemDeactivatedAfterEdit() && old_euler != handle_select->target) {
            undo.move_handle(id, handle_select, old_euler);
        }
        ImGui::Checkbox("Enable", &handle_select->enabled);
        ImGui::Separator();

    } else if (joint_select) {

        Scene_Item& item = obj_opt.value().get();
        Scene_ID id = item.id();

TheNumbat's avatar
TheNumbat committed
167
        ImGui::Text("Edit Joint");
TheNumbat's avatar
TheNumbat committed
168
        
TheNumbat's avatar
TheNumbat committed
169
        if (ImGui::DragFloat3("Pose", joint_select->pose.data, 1.0f, 0.0f, 0.0f, "%.2f"))
TheNumbat's avatar
TheNumbat committed
170
            obj_opt.value().get().get<Scene_Object>().set_pose_dirty();
TheNumbat's avatar
TheNumbat committed
171
        if (ImGui::IsItemActivated())
TheNumbat's avatar
TheNumbat committed
172
            old_euler = joint_select->pose;
TheNumbat's avatar
TheNumbat committed
173
        if (ImGui::IsItemDeactivatedAfterEdit() && old_euler != joint_select->pose) {
TheNumbat's avatar
TheNumbat committed
174
            joint_select->pose = joint_select->pose.range(0.0f, 360.0f);
TheNumbat's avatar
TheNumbat committed
175
            undo.pose_bone(id, joint_select, old_euler);
TheNumbat's avatar
TheNumbat committed
176
177
178
179
        }
        ImGui::Separator();
    }

TheNumbat's avatar
TheNumbat committed
180
181
182
183
184
185
186
187
188
189
190
    if (obj_opt.has_value()) {
        Scene_Item& item = obj_opt.value().get();
        if (item.is<Scene_Object>()) {
            Scene_Object& obj = item.get<Scene_Object>();
            if (obj.armature.has_bones()) {
                if (obj.armature.do_ik())
                    obj.set_pose_dirty();
            }
        }
    }

TheNumbat's avatar
TheNumbat committed
191
    if (ui_camera.UI(undo, user_cam)) {
TheNumbat's avatar
TheNumbat committed
192
        camera_selected = true;
TheNumbat's avatar
TheNumbat committed
193
194
        if (obj_opt.has_value())
            prev_selected = obj_opt.value().get().id();
TheNumbat's avatar
TheNumbat committed
195
196
197
    }
}

TheNumbat's avatar
TheNumbat committed
198
void Animate::end_transform(Undo &undo, Scene_Item &obj) {
TheNumbat's avatar
TheNumbat committed
199
200
201
    if (handle_select) {
        undo.move_handle(obj.id(), handle_select, old_pose.pos - obj.get<Scene_Object>().armature.base());
    } else if (joint_select) {
TheNumbat's avatar
TheNumbat committed
202
203
204
205
206
        undo.pose_bone(obj.id(), joint_select, old_euler);
    } else {
        undo.update_pose(obj.id(), old_pose);
    }
    old_pose = {};
TheNumbat's avatar
TheNumbat committed
207
    old_euler = {};
TheNumbat's avatar
TheNumbat committed
208
209
210
    old_p_to_j = Mat4::I;
}

TheNumbat's avatar
TheNumbat committed
211
Vec3 Animate::selected_pos(Scene_Item &item) {
TheNumbat's avatar
TheNumbat committed
212
213
214
    if (handle_select) {
        return item.pose().transform() * (handle_select->target + item.get<Scene_Object>().armature.base());
    } else if (joint_select) {
TheNumbat's avatar
TheNumbat committed
215
216
        return item.pose().transform() *
               item.get<Scene_Object>().armature.posed_base_of(joint_select);
TheNumbat's avatar
TheNumbat committed
217
218
219
220
    }
    return item.pose().pos;
}

TheNumbat's avatar
TheNumbat committed
221
void Animate::apply_transform(Widgets &widgets, Scene_Item &item) {
TheNumbat's avatar
TheNumbat committed
222
223
224
    if (handle_select) {
        handle_select->target = widgets.apply_action(old_pose).pos - item.get<Scene_Object>().armature.base();
    } else if (joint_select) {
TheNumbat's avatar
TheNumbat committed
225
226
        Scene_Object &obj = item.get<Scene_Object>();
        Vec3 euler = widgets.apply_action(old_pose).euler;
TheNumbat's avatar
TheNumbat committed
227
        joint_select->pose = (old_p_to_j * Mat4::euler(euler)).to_euler();
TheNumbat's avatar
TheNumbat committed
228
229
        obj.set_pose_dirty();
    } else {
TheNumbat's avatar
TheNumbat committed
230
231
232
233
        item.pose() = widgets.apply_action(old_pose);
    }
}

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

TheNumbat's avatar
TheNumbat committed
237
    if (widgets.want_drag()) {
TheNumbat's avatar
TheNumbat committed
238

TheNumbat's avatar
TheNumbat committed
239
240
241
242
243
244
245
246
        if (handle_select) {

            Scene_Object &obj = scene.get_obj(selected);
            widgets.start_drag(handle_select->target + obj.armature.base(), cam, spos, dir);
            old_pose = {};
            old_pose.pos = handle_select->target + obj.armature.base();

        } else if (joint_select) {
TheNumbat's avatar
TheNumbat committed
247

TheNumbat's avatar
TheNumbat committed
248
249
            Scene_Object &obj = scene.get_obj(selected);
            Vec3 base = obj.pose.transform() * obj.armature.posed_base_of(joint_select);
TheNumbat's avatar
TheNumbat committed
250
            widgets.start_drag(base, cam, spos, dir);
TheNumbat's avatar
TheNumbat committed
251

TheNumbat's avatar
TheNumbat committed
252
253
254
255
256
257
258
259
            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
260
261
            Scene_Item &item = scene.get(selected).value();
            Pose &pose = item.pose();
TheNumbat's avatar
TheNumbat committed
262
263
264
265
266
            widgets.start_drag(pose.pos, cam, spos, dir);
            old_pose = pose;
        }

        return false;
TheNumbat's avatar
TheNumbat committed
267
268
    }

TheNumbat's avatar
TheNumbat committed
269
    Scene_Maybe mb = scene.get(selected);
TheNumbat's avatar
TheNumbat committed
270
    if (mb.has_value()) {
TheNumbat's avatar
TheNumbat committed
271

TheNumbat's avatar
TheNumbat committed
272
273
        Scene_Item &item = mb.value().get();
        if (item.is<Scene_Object>()) {
TheNumbat's avatar
TheNumbat committed
274

TheNumbat's avatar
TheNumbat committed
275
276
            Scene_Object &obj = item.get<Scene_Object>();
            if (id >= joint_id_offset && obj.armature.has_bones()) {
TheNumbat's avatar
TheNumbat committed
277
278
279

                Scene_ID j_id = id - joint_id_offset;
                joint_select = obj.armature.get_joint(j_id);
TheNumbat's avatar
TheNumbat committed
280
                handle_select = obj.armature.get_handle(j_id);
TheNumbat's avatar
TheNumbat committed
281
                if (joint_select) {
TheNumbat's avatar
TheNumbat committed
282
                    widgets.active = Widget_Type::rotate;
TheNumbat's avatar
TheNumbat committed
283
284
                } else if(handle_select) {
                    widgets.active = Widget_Type::move;
TheNumbat's avatar
TheNumbat committed
285
286
287
288
289
                }
                return false;
            }
        }
    }
TheNumbat's avatar
TheNumbat committed
290

TheNumbat's avatar
TheNumbat committed
291
    if (id >= n_Widget_IDs) {
TheNumbat's avatar
TheNumbat committed
292
        if (id == selected) {
TheNumbat's avatar
TheNumbat committed
293
            joint_select = nullptr;
TheNumbat's avatar
TheNumbat committed
294
            handle_select = nullptr;
TheNumbat's avatar
TheNumbat committed
295
296
297
298
299
        }
        return true;
    }

    joint_select = nullptr;
TheNumbat's avatar
TheNumbat committed
300
    handle_select = nullptr;
TheNumbat's avatar
TheNumbat committed
301
302
303
    return false;
}

TheNumbat's avatar
TheNumbat committed
304
305
void Animate::timeline(Manager &manager, Undo &undo, Scene &scene, Scene_Maybe obj,
                       Camera &user_cam) {
TheNumbat's avatar
TheNumbat committed
306
307
308

    // NOTE(max): this is pretty messy
    //      Would be good to add the ability to set per-component keyframes
TheNumbat's avatar
TheNumbat committed
309
    //      ^ I started with that but it was hard to make work with assimp and
TheNumbat's avatar
TheNumbat committed
310
311
312
313
314
315
    //        generally made everything a lot messier.

    ImVec2 size = ImGui::GetWindowSize();

    ImGui::Columns(2);
    ImGui::SetColumnWidth(0, 150.0f);
TheNumbat's avatar
TheNumbat committed
316
317
318

    if (!playing) {
        if (ImGui::Button("Play")) {
TheNumbat's avatar
TheNumbat committed
319
320
321
322
            playing = true;
            last_frame = SDL_GetPerformanceCounter();
        }
    } else {
TheNumbat's avatar
TheNumbat committed
323
        if (ImGui::Button("Stop")) {
TheNumbat's avatar
TheNumbat committed
324
325
326
327
328
            playing = false;
        }
    }

    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
329
    if (ImGui::Button("Render")) {
TheNumbat's avatar
TheNumbat committed
330
331
332
333
334
335
336
        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
337
338

    if (ImGui::Button("Add Frames")) {
TheNumbat's avatar
TheNumbat committed
339
340
        max_frame += 3 * frame_rate;
    }
TheNumbat's avatar
TheNumbat committed
341
    if (ImGui::Button("Crop End")) {
TheNumbat's avatar
TheNumbat committed
342
343
        max_frame = current_frame + 1;
        current_frame = std::min(current_frame, max_frame - 1);
TheNumbat's avatar
TheNumbat committed
344
        scene.for_items([this](Scene_Item &item) {
TheNumbat's avatar
TheNumbat committed
345
            item.animation().splines.crop((float)max_frame);
TheNumbat's avatar
TheNumbat committed
346
            if (item.is<Scene_Object>())
TheNumbat's avatar
TheNumbat committed
347
                item.get<Scene_Object>().armature.crop((float)max_frame);
TheNumbat's avatar
TheNumbat committed
348
            else if (item.is<Scene_Light>())
TheNumbat's avatar
TheNumbat committed
349
350
351
352
353
354
355
356
357
358
359
360
                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
361
362
363
    Scene_Item *select = nullptr;
    if (obj.has_value()) {
        Scene_Item &item = obj.value();
TheNumbat's avatar
TheNumbat committed
364
        select = &item;
TheNumbat's avatar
TheNumbat committed
365
        if (item.id() != prev_selected) {
TheNumbat's avatar
TheNumbat committed
366
367
368
369
370
371
372
373
374
            camera_selected = false;
            prev_selected = item.id();
        }
    }

    bool frame_changed = false;

    ImGui::Text("Keyframe:");
    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
375
376
    if (ImGui::Button("Clear")) {
        if (camera_selected) {
TheNumbat's avatar
TheNumbat committed
377
378
            anim_camera.splines.erase((float)current_frame);
            camera_spline();
TheNumbat's avatar
TheNumbat committed
379
        } else if (select) {
TheNumbat's avatar
TheNumbat committed
380
381
382
383
384
            select->animation().splines.erase((float)current_frame);
            make_spline(select->id(), select->animation());
        }
    }

TheNumbat's avatar
TheNumbat committed
385
386
    auto set_item = [&, this](Scene_Item &item) {
        if (item.is<Scene_Object>()) {
TheNumbat's avatar
TheNumbat committed
387
            undo.anim_pose_bones(item.id(), (float)current_frame);
TheNumbat's avatar
TheNumbat committed
388
389
        }
        if (item.is<Scene_Light>()) {
TheNumbat's avatar
TheNumbat committed
390
391
392
393
394
395
            undo.anim_light(item.id(), (float)current_frame);
        }
        make_spline(item.id(), item.animation());
    };

    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
396
397
    if (ImGui::Button("Set")) {
        if (camera_selected) {
TheNumbat's avatar
TheNumbat committed
398
399
            undo.anim_camera(anim_camera, (float)current_frame, ui_camera.get());
            camera_spline();
TheNumbat's avatar
TheNumbat committed
400
        } else if (select) {
TheNumbat's avatar
TheNumbat committed
401
402
403
404
            set_item(*select);
        }
    }
    ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
405
    if (ImGui::Button("Set All")) {
TheNumbat's avatar
TheNumbat committed
406
407
408
409
410
411
412
413
        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
414
    if (ImGui::SliderInt("Frame", &current_frame, 0, max_frame - 1)) {
TheNumbat's avatar
TheNumbat committed
415
416
        frame_changed = true;
    }
TheNumbat's avatar
TheNumbat committed
417
418
    ImGui::BeginChild("Timeline", {size.x - 20.0f, size.y - 80.0f}, false,
                      ImGuiWindowFlags_HorizontalScrollbar);
TheNumbat's avatar
TheNumbat committed
419
420

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

TheNumbat's avatar
TheNumbat committed
422
423
424
425
426
427
428
429
430
431
    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
432
433
434
        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
435
436
437
438
439
440
441
442
443
        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
444
        for (float f : keys) {
TheNumbat's avatar
TheNumbat committed
445
            int frame = (int)std::round(f);
TheNumbat's avatar
TheNumbat committed
446
            if (frame >= 0 && frame < max_frame)
TheNumbat's avatar
TheNumbat committed
447
448
449
                frames[frame] = true;
        }

TheNumbat's avatar
TheNumbat committed
450
451
452
        for (int i = 0; i < max_frame; i++) {
            if (i > 0)
                ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
453
454
455
456
457
            ImGui::PushID(i);

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

TheNumbat's avatar
TheNumbat committed
458
            if (i == current_frame) {
TheNumbat's avatar
TheNumbat committed
459
460
461
462
                ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
                color = true;
            }

TheNumbat's avatar
TheNumbat committed
463
            if (frames[i]) {
TheNumbat's avatar
TheNumbat committed
464
                label = "*";
TheNumbat's avatar
TheNumbat committed
465
                if (i != current_frame) {
TheNumbat's avatar
TheNumbat committed
466
                    color = true;
TheNumbat's avatar
TheNumbat committed
467
468
                    ImGui::PushStyleColor(ImGuiCol_Button,
                                          ImGui::GetColorU32(ImGuiCol_ButtonHovered));
TheNumbat's avatar
TheNumbat committed
469
470
                }
            }
TheNumbat's avatar
TheNumbat committed
471
            if (ImGui::SmallButton(label.c_str())) {
TheNumbat's avatar
TheNumbat committed
472
473
474
475
                current_frame = i;
                frame_changed = true;
                camera_selected = true;
            }
TheNumbat's avatar
TheNumbat committed
476
477
478
            if (color)
                ImGui::PopStyleColor();
            ImGui::PopID();
TheNumbat's avatar
TheNumbat committed
479
480
481
482
        }
        ImGui::PopID();
    }

TheNumbat's avatar
TheNumbat committed
483
    scene.for_items([&, this](Scene_Item &item) {
TheNumbat's avatar
TheNumbat committed
484
485
486
        frames.clear();
        frames.resize(max_frame);

TheNumbat's avatar
TheNumbat committed
487
        std::string name = const_cast<const Scene_Item &>(item).name();
TheNumbat's avatar
TheNumbat committed
488
        name.resize(name_chars);
TheNumbat's avatar
TheNumbat committed
489

TheNumbat's avatar
TheNumbat committed
490
        ImVec2 size = ImGui::CalcTextSize(name.c_str());
TheNumbat's avatar
TheNumbat committed
491
492
493
        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
494
495
496
497
498
499
500
501
502
503
        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
504
        if (item.is<Scene_Light>()) {
TheNumbat's avatar
TheNumbat committed
505
506
507
            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
508
        if (item.is<Scene_Object>()) {
TheNumbat's avatar
TheNumbat committed
509
510
511
512
            std::set<float> more_keys = item.get<Scene_Object>().armature.keys();
            keys.insert(more_keys.begin(), more_keys.end());
        }

TheNumbat's avatar
TheNumbat committed
513
        for (float f : keys) {
TheNumbat's avatar
TheNumbat committed
514
            int frame = (int)std::round(f);
TheNumbat's avatar
TheNumbat committed
515
            if (frame >= 0 && frame < max_frame)
TheNumbat's avatar
TheNumbat committed
516
517
518
                frames[frame] = true;
        }

TheNumbat's avatar
TheNumbat committed
519
520
521
        for (int i = 0; i < max_frame; i++) {
            if (i > 0)
                ImGui::SameLine();
TheNumbat's avatar
TheNumbat committed
522
523
524
525
526
            ImGui::PushID(i);

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

TheNumbat's avatar
TheNumbat committed
527
            if (i == current_frame) {
TheNumbat's avatar
TheNumbat committed
528
529
530
531
                ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonActive));
                color = true;
            }

TheNumbat's avatar
TheNumbat committed
532
            if (frames[i]) {
TheNumbat's avatar
TheNumbat committed
533
                label = "*";
TheNumbat's avatar
TheNumbat committed
534
                if (i != current_frame) {
TheNumbat's avatar
TheNumbat committed
535
                    color = true;
TheNumbat's avatar
TheNumbat committed
536
537
                    ImGui::PushStyleColor(ImGuiCol_Button,
                                          ImGui::GetColorU32(ImGuiCol_ButtonHovered));
TheNumbat's avatar
TheNumbat committed
538
539
540
                }
            }

TheNumbat's avatar
TheNumbat committed
541
            if (ImGui::SmallButton(label.c_str())) {
TheNumbat's avatar
TheNumbat committed
542
543
544
545
546
                current_frame = i;
                frame_changed = true;
                camera_selected = false;
                manager.set_select(item.id());
            }
TheNumbat's avatar
TheNumbat committed
547
548
            if (color)
                ImGui::PopStyleColor();
TheNumbat's avatar
TheNumbat committed
549
550
            ImGui::PopID();
        }
TheNumbat's avatar
TheNumbat committed
551

TheNumbat's avatar
TheNumbat committed
552
553
554
555
556
557
        live_ids.push_back(item.id());

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

TheNumbat's avatar
TheNumbat committed
559
560
561
562
    ImGui::PopStyleVar();
    ImGui::EndChild();

    std::unordered_map<Scene_ID, GL::Lines> new_cache;
TheNumbat's avatar
TheNumbat committed
563
    for (Scene_ID i : live_ids) {
TheNumbat's avatar
TheNumbat committed
564
        auto entry = spline_cache.find(i);
TheNumbat's avatar
TheNumbat committed
565
        if (entry != spline_cache.end()) {
TheNumbat's avatar
TheNumbat committed
566
567
568
569
570
            new_cache[i] = std::move(entry->second);
        }
    }
    spline_cache = std::move(new_cache);

TheNumbat's avatar
TheNumbat committed
571
572
    if (frame_changed)
        update(scene);
TheNumbat's avatar
TheNumbat committed
573
574
}

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

TheNumbat's avatar
TheNumbat committed
577
578
    current_frame = (int)time;

TheNumbat's avatar
TheNumbat committed
579
    scene.for_items([time](Scene_Item &item) { item.set_time(time); });
TheNumbat's avatar
TheNumbat committed
580
581

    Camera cam = anim_camera.at(time);
TheNumbat's avatar
TheNumbat committed
582
    if (anim_camera.splines.any()) {
TheNumbat's avatar
TheNumbat committed
583
584
585
586
587
588
589
590
591
592
593
594
595
        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
596
Anim_Camera &Animate::camera() { return anim_camera; }
TheNumbat's avatar
TheNumbat committed
597

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

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

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

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

TheNumbat's avatar
TheNumbat committed
606
void Animate::refresh(Scene &scene) { set_time(scene, (float)current_frame); }
TheNumbat's avatar
TheNumbat committed
607
608
609
610

void Animate::clear() {
    anim_camera.splines.clear();
    joint_select = nullptr;
TheNumbat's avatar
TheNumbat committed
611
612
613
614
615
616
617
618
619
    handle_select = nullptr;
}

void Animate::invalidate(Skeleton::IK_Handle* handle) {
    if (handle_select == handle) handle_select = nullptr; 
}

void Animate::invalidate(Joint* j) {
    if (joint_select == j) joint_select = nullptr; 
TheNumbat's avatar
TheNumbat committed
620
621
}

TheNumbat's avatar
TheNumbat committed
622
void Animate::update(Scene &scene) {
TheNumbat's avatar
TheNumbat committed
623
624
625

    Uint64 time = SDL_GetPerformanceCounter();

TheNumbat's avatar
TheNumbat committed
626
627
628
    if (playing) {
        if ((time - last_frame) * frame_rate / SDL_GetPerformanceFrequency()) {
            if (current_frame == max_frame - 1) {
TheNumbat's avatar
TheNumbat committed
629
630
631
632
633
634
635
636
637
                playing = false;
                current_frame = 0;
            } else {
                current_frame++;
            }
            last_frame = time;
        }
    }

TheNumbat's avatar
TheNumbat committed
638
    if (displayed_frame != current_frame) {
TheNumbat's avatar
TheNumbat committed
639
640
641
642
643
        set_time(scene, (float)current_frame);
        displayed_frame = current_frame;
    }
}

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