From 1555a334663082e0341811041b4f07f952cfcecd Mon Sep 17 00:00:00 2001 From: TheNumbat Date: Thu, 19 Nov 2020 20:43:34 -0500 Subject: [PATCH] upstream changes --- src/gui/animate.cpp | 97 +++++++++++++++++++++++++++-------- src/gui/animate.h | 3 ++ src/gui/manager.cpp | 14 ++++- src/gui/manager.h | 1 + src/gui/rig.cpp | 77 +++++++++++++++++++++++----- src/gui/rig.h | 2 + src/gui/widgets.cpp | 13 ++++- src/scene/scene.cpp | 74 ++++++++++++++++++++------- src/scene/skeleton.cpp | 108 +++++++++++++++++++++++++++++++++------ src/scene/skeleton.h | 55 +++++++++++++++----- src/scene/undo.cpp | 53 +++++++++++++++++++ src/scene/undo.h | 3 ++ src/student/skeleton.cpp | 16 ++++++ src/util/hdr_image.cpp | 12 +++-- 14 files changed, 439 insertions(+), 89 deletions(-) diff --git a/src/gui/animate.cpp b/src/gui/animate.cpp index 56910e0..6bd17b5 100644 --- a/src/gui/animate.cpp +++ b/src/gui/animate.cpp @@ -26,17 +26,6 @@ void Animate::update_dim(Vec2 dim) { ui_camera.dim(dim); } bool Animate::keydown(Widgets &widgets, Undo &undo, Scene_ID sel, SDL_Keysym key) { -#ifdef __APPLE__ - if (key.sym == SDLK_BACKSPACE && key.mod & KMOD_GUI) { -#else - if (key.sym == SDLK_DELETE) { -#endif - if (joint_select) - undo.del_bone(sel, joint_select); - else if (sel) - undo.del_obj(sel); - } - if (key.sym == SDLK_SPACE) { playing = !playing; last_frame = SDL_GetPerformanceCounter(); @@ -76,16 +65,17 @@ void Animate::render(Scene &scene, Scene_Maybe obj_opt, Widgets &widgets, Camera Scene_Object &obj = item.get(); joint_id_offset = scene.used_ids(); - obj.armature.render(view * obj.pose.transform(), joint_select, false, true, + obj.armature.render(view * obj.pose.transform(), joint_select, handle_select, false, true, joint_id_offset); - if (!joint_select) { + if (!joint_select && !handle_select) { R.begin_outline(); BBox box = obj.bbox(); obj.render(view, false, true); - obj.armature.outline(view * obj.pose.transform(), joint_select, false, true, box, - joint_id_offset); + obj.armature.outline(view * obj.pose.transform(), false, true, box, joint_id_offset); R.end_outline(view, box); + } else if (handle_select) { + widgets.active = Widget_Type::move; } else { widgets.active = Widget_Type::rotate; } @@ -94,7 +84,12 @@ void Animate::render(Scene &scene, Scene_Maybe obj_opt, Widgets &widgets, Camera R.outline(view, item); } - if (joint_select) { + if (handle_select) { + + Scene_Object &obj = item.get(); + widgets.render(view, pose.transform() * (handle_select->target + obj.armature.base()), scale); + + } else if (joint_select) { Scene_Object &obj = item.get(); widgets.render(view, pose.transform() * obj.armature.posed_base_of(joint_select), scale); @@ -150,19 +145,49 @@ void Animate::camera_spline() { void Animate::UIsidebar(Manager &manager, Undo &undo, Scene_Maybe obj_opt, Camera &user_cam) { - if (joint_select) { + 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(); + ImGui::Text("Edit Joint"); + if (ImGui::DragFloat3("Pose", joint_select->pose.data, 1.0f, 0.0f, 0.0f, "%.2f")) obj_opt.value().get().get().set_pose_dirty(); if (ImGui::IsItemActivated()) old_euler = joint_select->pose; if (ImGui::IsItemDeactivatedAfterEdit() && old_euler != joint_select->pose) { joint_select->pose = joint_select->pose.range(0.0f, 360.0f); - undo.pose_bone(obj_opt.value().get().id(), joint_select, old_euler); + undo.pose_bone(id, joint_select, old_euler); } ImGui::Separator(); } + if (obj_opt.has_value()) { + Scene_Item& item = obj_opt.value().get(); + if (item.is()) { + Scene_Object& obj = item.get(); + if (obj.armature.has_bones()) { + if (obj.armature.do_ik()) + obj.set_pose_dirty(); + } + } + } + if (ui_camera.UI(undo, user_cam)) { camera_selected = true; if (obj_opt.has_value()) @@ -171,17 +196,22 @@ void Animate::UIsidebar(Manager &manager, Undo &undo, Scene_Maybe obj_opt, Camer } void Animate::end_transform(Undo &undo, Scene_Item &obj) { - if (joint_select) { + if (handle_select) { + undo.move_handle(obj.id(), handle_select, old_pose.pos - obj.get().armature.base()); + } else if (joint_select) { undo.pose_bone(obj.id(), joint_select, old_euler); } else { undo.update_pose(obj.id(), old_pose); } old_pose = {}; + old_euler = {}; old_p_to_j = Mat4::I; } Vec3 Animate::selected_pos(Scene_Item &item) { - if (joint_select) { + if (handle_select) { + return item.pose().transform() * (handle_select->target + item.get().armature.base()); + } else if (joint_select) { return item.pose().transform() * item.get().armature.posed_base_of(joint_select); } @@ -189,7 +219,9 @@ Vec3 Animate::selected_pos(Scene_Item &item) { } void Animate::apply_transform(Widgets &widgets, Scene_Item &item) { - if (joint_select) { + if (handle_select) { + handle_select->target = widgets.apply_action(old_pose).pos - item.get().armature.base(); + } else if (joint_select) { Scene_Object &obj = item.get(); Vec3 euler = widgets.apply_action(old_pose).euler; joint_select->pose = (old_p_to_j * Mat4::euler(euler)).to_euler(); @@ -204,7 +236,14 @@ bool Animate::select(Scene &scene, Widgets &widgets, Scene_ID selected, Scene_ID if (widgets.want_drag()) { - if (joint_select) { + 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) { Scene_Object &obj = scene.get_obj(selected); Vec3 base = obj.pose.transform() * obj.armature.posed_base_of(joint_select); @@ -238,8 +277,11 @@ bool Animate::select(Scene &scene, Widgets &widgets, Scene_ID selected, Scene_ID Scene_ID j_id = id - joint_id_offset; joint_select = obj.armature.get_joint(j_id); + handle_select = obj.armature.get_handle(j_id); if (joint_select) { widgets.active = Widget_Type::rotate; + } else if(handle_select) { + widgets.active = Widget_Type::move; } return false; } @@ -249,11 +291,13 @@ bool Animate::select(Scene &scene, Widgets &widgets, Scene_ID selected, Scene_ID if (id >= n_Widget_IDs) { if (id == selected) { joint_select = nullptr; + handle_select = nullptr; } return true; } joint_select = nullptr; + handle_select = nullptr; return false; } @@ -564,6 +608,15 @@ void Animate::refresh(Scene &scene) { set_time(scene, (float)current_frame); } void Animate::clear() { anim_camera.splines.clear(); joint_select = nullptr; + 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; } void Animate::update(Scene &scene) { diff --git a/src/gui/animate.h b/src/gui/animate.h index 9b6a4e5..bdfc2dd 100644 --- a/src/gui/animate.h +++ b/src/gui/animate.h @@ -52,6 +52,8 @@ public: const Anim_Camera &camera() const; Anim_Camera &camera(); void set(int n_frames, int fps); + void invalidate(Skeleton::IK_Handle* handle); + void invalidate(Joint* handle); private: Uint64 last_frame = 0; @@ -66,6 +68,7 @@ private: Anim_Camera anim_camera; Joint *joint_select = nullptr; + Skeleton::IK_Handle *handle_select = nullptr; unsigned int joint_id_offset = 0; Pose old_pose; diff --git a/src/gui/manager.cpp b/src/gui/manager.cpp index 603ebee..2945124 100644 --- a/src/gui/manager.cpp +++ b/src/gui/manager.cpp @@ -38,6 +38,8 @@ void Manager::invalidate_obj(Scene_ID id) { if (id == layout.selected()) { layout.clear_select(); model.unset_mesh(); + rig.clear(); + animate.clear(); } } @@ -129,12 +131,22 @@ void Manager::save_scene(Scene &scene) { set_error(error); } +static bool postfix(const std::string &path, const std::string &type) { + if (path.length() >= type.length()) + return path.compare(path.length() - type.length(), type.length(), type) == 0; + return false; +} + void Manager::write_scene(Scene &scene) { char *path = nullptr; NFD_SaveDialog("dae", nullptr, &path); if (path) { - std::string error = scene.write(std::string(path), render.get_cam(), animate); + std::string spath(path); + if (!postfix(path, ".dae")) { + spath += ".dae"; + } + std::string error = scene.write(spath, render.get_cam(), animate); set_error(error); free(path); } diff --git a/src/gui/manager.h b/src/gui/manager.h index cfda2b5..9100292 100644 --- a/src/gui/manager.h +++ b/src/gui/manager.h @@ -39,6 +39,7 @@ struct Color { RGBv(red, 163, 66, 81); RGBv(green, 124, 172, 40); RGBv(blue, 64, 127, 193); + RGBv(hoverg, 102, 204, 102); static Vec3 axis(Axis a); }; #undef RGBv diff --git a/src/gui/rig.cpp b/src/gui/rig.cpp index 804ece0..5a07b8c 100644 --- a/src/gui/rig.cpp +++ b/src/gui/rig.cpp @@ -13,11 +13,17 @@ bool Rig::keydown(Widgets &widgets, Undo &undo, SDL_Keysym key) { #ifdef __APPLE__ if (key.sym == SDLK_BACKSPACE && key.mod & KMOD_GUI) { #else - if (key.sym == SDLK_DELETE && selected) { + if (key.sym == SDLK_DELETE && (selected || handle)) { #endif - undo.del_bone(my_obj->id(), selected); - selected = nullptr; - return true; + if (selected) { + undo.del_bone(my_obj->id(), selected); + selected = nullptr; + return true; + } else if(handle) { + undo.del_handle(my_obj->id(), handle); + handle = nullptr; + return true; + } } return false; } @@ -38,6 +44,7 @@ void Rig::render(Scene_Maybe obj_opt, Widgets &widgets, Camera &cam) { if (my_obj != &obj) { my_obj = &obj; selected = nullptr; + handle = nullptr; } if (my_obj->rig_dirty) { mesh_bvh.build(obj.mesh()); @@ -46,15 +53,17 @@ void Rig::render(Scene_Maybe obj_opt, Widgets &widgets, Camera &cam) { Mat4 view = cam.get_view(); obj.render(view, false, false, false, false); - obj.armature.render(view, selected, root_selected, false); + obj.armature.render(view, selected, handle, root_selected, false); - if (selected || root_selected) { + if (selected || handle || root_selected) { widgets.active = Widget_Type::move; Vec3 pos; if (selected) pos = obj.armature.end_of(selected); + else if (handle) + pos = handle->target + obj.armature.base(); else pos = obj.armature.base(); @@ -70,11 +79,18 @@ void Rig::invalidate(Joint *j) { new_joint = nullptr; } +void Rig::invalidate(Skeleton::IK_Handle *j) { + if (handle == j) + handle = nullptr; +} + void Rig::end_transform(Widgets &widgets, Undo &undo, Scene_Object &obj) { if (root_selected) - undo.move_root(obj.id(), old_ext); - else + undo.move_root(obj.id(), old_pos); + else if (selected) undo.move_bone(obj.id(), selected, old_ext); + else if (handle) + undo.move_handle(obj.id(), handle, old_pos - my_obj->armature.base()); obj.set_skel_dirty(); } @@ -86,16 +102,23 @@ void Rig::apply_transform(Widgets &widgets) { Vec3 new_pos = widgets.apply_action(Pose::moved(old_pos)).pos; selected->extent = new_pos - old_base; my_obj->set_skel_dirty(); + } else if (handle) { + Vec3 new_pos = widgets.apply_action(Pose::moved(old_pos)).pos; + handle->target = new_pos - my_obj->armature.base(); + my_obj->set_skel_dirty(); } } Vec3 Rig::selected_pos() { if (root_selected) { return my_obj->armature.base(); - } else { - assert(selected); + } else if (selected) { return my_obj->armature.end_of(selected); + } else if (handle) { + return handle->target + my_obj->armature.base(); } + assert(false); + return Vec3(); } void Rig::select(Scene &scene, Widgets &widgets, Undo &undo, Scene_ID id, Vec3 cam, Vec2 spos, @@ -110,6 +133,7 @@ void Rig::select(Scene &scene, Widgets &widgets, Undo &undo, Scene_ID id, Vec3 c selected = new_joint; new_joint = nullptr; + handle = nullptr; creating_bone = false; root_selected = false; @@ -118,17 +142,19 @@ void Rig::select(Scene &scene, Widgets &widgets, Undo &undo, Scene_ID id, Vec3 c if (root_selected) { old_pos = my_obj->armature.base(); - } else { - assert(selected); + } else if (selected) { old_pos = my_obj->armature.end_of(selected); old_base = my_obj->armature.base_of(selected); old_ext = selected->extent; + } else if (handle) { + old_pos = handle->target + my_obj->armature.base(); } widgets.start_drag(old_pos, cam, spos, dir); } else if (!id || id >= n_Widget_IDs) { selected = my_obj->armature.get_joint(id); + handle = my_obj->armature.get_handle(id); root_selected = my_obj->armature.is_root_id(id); } } @@ -136,9 +162,13 @@ void Rig::select(Scene &scene, Widgets &widgets, Undo &undo, Scene_ID id, Vec3 c void Rig::clear() { my_obj = nullptr; selected = nullptr; + handle = nullptr; } -void Rig::clear_select() { selected = nullptr; } +void Rig::clear_select() { + selected = nullptr; + handle = nullptr; +} void Rig::hover(Vec3 cam, Vec2 spos, Vec3 dir) { @@ -183,6 +213,7 @@ Mode Rig::UIsidebar(Manager &manager, Undo &undo, Widgets &widgets, Scene_Maybe if (my_obj != &obj) { my_obj = &obj; selected = nullptr; + handle = nullptr; } if (my_obj->rig_dirty) { mesh_bvh.build(obj.mesh()); @@ -196,11 +227,13 @@ Mode Rig::UIsidebar(Manager &manager, Undo &undo, Widgets &widgets, Scene_Maybe creating_bone = false; my_obj->armature.erase(new_joint); new_joint = nullptr; + handle = nullptr; my_obj->set_skel_dirty(); } } else if (ImGui::Button("New Bone")) { creating_bone = true; + handle = nullptr; if (!selected || root_selected) { new_joint = my_obj->armature.add_root(Vec3{0.0f}); @@ -223,10 +256,28 @@ Mode Rig::UIsidebar(Manager &manager, Undo &undo, Widgets &widgets, Scene_Maybe ImGui::DragFloat3("Pose", selected->pose.data, 0.1f); + if (ImGui::Button("Add IK")) { + handle = my_obj->armature.add_handle(my_obj->armature.end_of(selected), selected); + undo.add_handle(my_obj->id(), handle); + selected = nullptr; + } + ImGui::SameLine(); if (ImGui::Button("Delete [del]")) { undo.del_bone(my_obj->id(), selected); selected = nullptr; } + + } else if (handle) { + ImGui::Separator(); + ImGui::Text("Edit Handle"); + + ImGui::DragFloat3("Target", handle->target.data, 0.1f); + ImGui::Checkbox("Enable", &handle->enabled); + + if (ImGui::Button("Delete [del]")) { + undo.del_handle(my_obj->id(), handle); + handle = nullptr; + } } return Mode::rig; diff --git a/src/gui/rig.h b/src/gui/rig.h index 61c2af7..cd7b046 100644 --- a/src/gui/rig.h +++ b/src/gui/rig.h @@ -25,6 +25,7 @@ public: void clear_select(); void clear(); void invalidate(Joint *j); + void invalidate(Skeleton::IK_Handle *j); void render(Scene_Maybe obj_opt, Widgets &widgets, Camera &cam); Mode UIsidebar(Manager &manager, Undo &undo, Widgets &widgets, Scene_Maybe obj); @@ -36,6 +37,7 @@ private: Scene_Object *my_obj = nullptr; Joint *selected = nullptr; + Skeleton::IK_Handle *handle = nullptr; Joint *new_joint = nullptr; PT::Tri_Mesh mesh_bvh; }; diff --git a/src/gui/widgets.cpp b/src/gui/widgets.cpp index 1e1ccf4..941c58d 100644 --- a/src/gui/widgets.cpp +++ b/src/gui/widgets.cpp @@ -709,6 +709,12 @@ void Widget_Render::animate(Scene &scene, Widget_Camera &cam, Camera &user_cam, ImGui::End(); } +static bool postfix(const std::string &path, const std::string &type) { + if (path.length() >= type.length()) + return path.compare(path.length() - type.length(), type.length(), type) == 0; + return false; +} + bool Widget_Render::UI(Scene &scene, Widget_Camera &cam, Camera &user_cam, std::string &err) { bool ret = false; @@ -752,6 +758,11 @@ bool Widget_Render::UI(Scene &scene, Widget_Camera &cam, Camera &user_cam, std:: NFD_SaveDialog("png", nullptr, &path); if (path) { + std::string spath(path); + if (!postfix(spath, ".png")) { + spath += ".png"; + } + std::vector data; if (method == 1) { @@ -762,7 +773,7 @@ bool Widget_Render::UI(Scene &scene, Widget_Camera &cam, Camera &user_cam, std:: stbi_flip_vertically_on_write(true); } - if (!stbi_write_png(path, (int)out_w, (int)out_h, 4, data.data(), (int)out_w * 4)) { + if (!stbi_write_png(spath.c_str(), (int)out_w, (int)out_h, 4, data.data(), (int)out_w * 4)) { err = "Failed to write png!"; } free(path); diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 9d81fa1..fa00f02 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -459,12 +459,19 @@ static void load_node(Scene &scobj, std::vector &errors, aiBone *bone = node_to_aibone[node]; aiVector3D t, r, s; bone->mOffsetMatrix.Decompose(s, r, t); - Joint *c = skeleton.add_child(p, aiVec(t)); - node_to_bone[node] = c; - c->pose = aiVec(r); - c->radius = bone->mWeights[0].mWeight; - for (unsigned int j = 0; j < node->mNumChildren; j++) - build_tree(c, node->mChildren[j]); + + std::string name(bone->mName.C_Str()); + if (name.find("S3D-joint-IK") != std::string::npos) { + Skeleton::IK_Handle *h = skeleton.add_handle(aiVec(t), p); + h->enabled = bone->mWeights[0].mWeight > 1.0f; + } else { + Joint *c = skeleton.add_child(p, aiVec(t)); + node_to_bone[node] = c; + c->pose = aiVec(r); + c->radius = bone->mWeights[0].mWeight; + for (unsigned int j = 0; j < node->mNumChildren; j++) + build_tree(c, node->mChildren[j]); + } }; for (unsigned int j = 0; j < arm_node->mNumChildren; j++) { aiNode *root_node = arm_node->mChildren[j]; @@ -1011,35 +1018,53 @@ std::string Scene::write(std::string file, const Camera &render_cam, ai_mat->AddProperty(new float(opt.intensity), 1, AI_MATKEY_SHININESS); if (obj.armature.has_bones()) { - ai_mesh->mNumBones = obj.armature.n_bones(); + ai_mesh->mNumBones = obj.armature.n_bones() + obj.armature.n_handles(); ai_mesh->mBones = new aiBone *[ai_mesh->mNumBones]; size_t bone_idx = 0; - std::string prefix = "S3D-joint-" + std::to_string(obj.id()) + "-"; + std::string jprefix = "S3D-joint-" + std::to_string(obj.id()) + "-"; + std::string ikprefix = "S3D-joint-IK-" + std::to_string(obj.id()) + "-"; aiNode *arm_node = new aiNode(); scene.mRootNode->mChildren[node_idx++] = arm_node; - arm_node->mName = aiString(prefix + "armature"); + arm_node->mName = aiString(jprefix + "armature"); arm_node->mTransformation = matMat(Mat4::translate(obj.armature.base_pos)); arm_node->mNumChildren = (unsigned int)obj.armature.roots.size(); arm_node->mChildren = new aiNode *[obj.armature.roots.size()]; std::unordered_map j_to_node; + std::unordered_map ik_to_node; - std::function joint_tree; - joint_tree = [&joint_tree, &j_to_node](std::string n, aiNode *node, Joint *j) { - std::string name = n + std::to_string(j->_id); - node->mName = aiString(name); + std::function joint_tree; + joint_tree = [&](aiNode *node, Joint *j) { + std::string jname = jprefix + std::to_string(j->_id); + node->mName = aiString(jname); j_to_node[j] = node; - if (j->children.size()) { - node->mNumChildren = (unsigned int)j->children.size(); - node->mChildren = new aiNode *[j->children.size()]; + size_t children = j->children.size(); + for (Skeleton::IK_Handle* h : obj.armature.handles) { + if (h->joint == j) { + children++; + } + } + if (children > 0) { + node->mNumChildren = (unsigned int)children; + node->mChildren = new aiNode *[children]; size_t i = 0; for (Joint *c : j->children) { node->mChildren[i] = new aiNode(); - joint_tree(n, node->mChildren[i], c); + joint_tree(node->mChildren[i], c); i++; } + for (Skeleton::IK_Handle* h : obj.armature.handles) { + if (h->joint == j) { + aiNode* iknode = new aiNode(); + std::string ikname = ikprefix + std::to_string(h->_id); + iknode->mName = aiString(ikname); + node->mChildren[i] = iknode; + ik_to_node[h] = iknode; + i++; + } + } } }; @@ -1048,7 +1073,7 @@ std::string Scene::write(std::string file, const Camera &render_cam, aiNode *root_node = new aiNode(); arm_node->mChildren[i] = root_node; j_to_node[j] = root_node; - joint_tree(prefix, root_node, j); + joint_tree(root_node, j); i++; } @@ -1061,12 +1086,23 @@ std::string Scene::write(std::string file, const Camera &render_cam, ai_mesh->mBones[bone_idx++] = bone; bone->mOffsetMatrix = matMat(Mat4::translate(j->extent) * Mat4::euler(j->pose)); bone->mNode = j_to_node[j]; - std::string name = prefix + std::to_string(j->_id); + std::string name = jprefix + std::to_string(j->_id); bone->mName = aiString(name); bone->mNumWeights = 1; bone->mWeights = new aiVertexWeight[1]; bone->mWeights[0].mWeight = j->radius; }); + for (Skeleton::IK_Handle* h : obj.armature.handles) { + aiBone *bone = new aiBone(); + ai_mesh->mBones[bone_idx++] = bone; + bone->mOffsetMatrix = matMat(Mat4::translate(h->target)); + bone->mNode = ik_to_node[h]; + std::string ikname = ikprefix + std::to_string(h->_id); + bone->mName = aiString(ikname); + bone->mNumWeights = 1; + bone->mWeights = new aiVertexWeight[1]; + bone->mWeights[0].mWeight = h->enabled ? 2.0f : 1.0f; + } } } else if (entry.second.is()) { diff --git a/src/scene/skeleton.cpp b/src/scene/skeleton.cpp index 1584c9e..0b8b648 100644 --- a/src/scene/skeleton.cpp +++ b/src/scene/skeleton.cpp @@ -24,8 +24,12 @@ Skeleton::Skeleton(unsigned int obj_id) { Skeleton::~Skeleton() { for (Joint *j : roots) delete j; - for (Joint *j : erased) - delete j; + for (auto& e : erased) + delete e.first; + for (IK_Handle *h : handles) + delete h; + for (IK_Handle *h : erased_handles) + delete h; } bool Skeleton::set_time(float time) { @@ -57,6 +61,15 @@ void Skeleton::crop(float t) { for_joints([t](Joint *j) { j->anim.crop(t); }); } +Skeleton::IK_Handle *Skeleton::get_handle(unsigned int id) { + IK_Handle *j = nullptr; + for(IK_Handle *h : handles) { + if (h->_id == id) + j = h; + } + return j; +} + Joint *Skeleton::get_joint(unsigned int id) { Joint *j = nullptr; for_joints([&](Joint *jt) { @@ -66,7 +79,7 @@ Joint *Skeleton::get_joint(unsigned int id) { return j; } -void Skeleton::render(const Mat4 &view, Joint *select, bool root, bool posed, unsigned int offset) { +void Skeleton::render(const Mat4 &view, Joint *jselect, IK_Handle *hselect, bool root, bool posed, unsigned int offset) { Renderer &R = Renderer::get(); @@ -81,41 +94,55 @@ void Skeleton::render(const Mat4 &view, Joint *select, bool root, bool posed, un R.capsule(opt, j->extent.norm(), j->radius); }); - if (select) { + if (jselect) { R.begin_outline(); Mat4 model = Mat4::translate(base_pos) * - (posed ? select->joint_to_posed() : select->joint_to_bind()) * - Mat4::rotate_to(select->extent); + (posed ? jselect->joint_to_posed() : jselect->joint_to_bind()) * + Mat4::rotate_to(jselect->extent); Renderer::MeshOpt opt; opt.modelview = view; - opt.id = select->_id + offset; + opt.id = jselect->_id + offset; opt.depth_only = true; BBox box; - R.capsule(opt, model, select->extent.norm(), select->radius, box); + R.capsule(opt, model, jselect->extent.norm(), jselect->radius, box); R.end_outline(view, box); } R.reset_depth(); - Renderer::MeshOpt opt; - opt.id = root_id + offset; - opt.modelview = V * Mat4::scale(Vec3{0.1f}); - opt.color = root ? Gui::Color::outline : Gui::Color::hover; - R.sphere(opt); + { + Renderer::MeshOpt opt; + opt.id = root_id + offset; + opt.modelview = V * Mat4::scale(Vec3{0.1f}); + opt.color = root ? Gui::Color::outline : Gui::Color::hover; + R.sphere(opt); + } for_joints([&](Joint *j) { Renderer::MeshOpt opt; opt.modelview = V * (posed ? j->joint_to_posed() : j->joint_to_bind()) * Mat4::translate(j->extent) * Mat4::scale(Vec3{j->radius * 0.25f}); opt.id = j->_id + offset; - opt.color = select == j ? Gui::Color::outline : Gui::Color::hover; + opt.color = jselect == j ? Gui::Color::outline : Gui::Color::hover; R.sphere(opt); }); + + GL::Lines ik_lines; + for(IK_Handle* h : handles) { + Renderer::MeshOpt opt; + opt.modelview = V * Mat4::translate(h->target) * Mat4::scale(Vec3{h->joint->radius * 0.3f}); + opt.id = h->_id + offset; + opt.color = hselect == h ? Gui::Color::outline : Gui::Color::hoverg; + Vec3 j_world = posed ? posed_end_of(h->joint) : end_of(h->joint); + ik_lines.add(h->target, j_world - base_pos, h->enabled ? Vec3(1.0f, 0.0f, 0.0f) : Vec3(0.0f)); + R.sphere(opt); + } + R.lines(ik_lines, V); } -void Skeleton::outline(const Mat4 &view, Joint *select, bool root, bool posed, BBox &box, +void Skeleton::outline(const Mat4 &view, bool root, bool posed, BBox &box, unsigned int offset) { Renderer &R = Renderer::get(); @@ -145,6 +172,10 @@ unsigned int Skeleton::n_bones() { return n; } +unsigned int Skeleton::n_handles() { + return (unsigned int)handles.size(); +} + Vec3 &Skeleton::base() { return base_pos; } Joint *Skeleton::add_child(Joint *j, Vec3 e) { @@ -162,6 +193,13 @@ void Skeleton::restore(Joint *j) { } else { roots.insert(j); } + + auto entry = erased.find(j); + assert(entry != erased.end()); + + for (IK_Handle* handle : entry->second) { + restore(handle); + } erased.erase(j); } @@ -171,7 +209,16 @@ void Skeleton::erase(Joint *j) { } else { roots.erase(j); } - erased.insert(j); + std::vector herase; + for (IK_Handle* h : handles) { + if (h->joint == j) { + herase.push_back(h); + } + } + for (IK_Handle* h : herase) { + erase(h); + } + erased.insert({j, std::move(herase)}); } Vec3 Skeleton::posed_base_of(Joint *j) { return j->is_root() ? base() : posed_end_of(parent(j)); } @@ -216,3 +263,32 @@ std::unordered_map Skeleton::at(float t) { void Skeleton::set(float t, const std::unordered_map &data) { for_joints([&data, t](Joint *j) { j->anim.set(t, Quat::euler(data.at(j->_id))); }); } + +void Skeleton::restore(IK_Handle *h) { + handles.insert(h); + erased_handles.erase(h); +} + +void Skeleton::erase(IK_Handle *h) { + handles.erase(h); + erased_handles.insert(h); +} + +Skeleton::IK_Handle *Skeleton::add_handle(Vec3 pos, Joint* j) { + IK_Handle* handle = new IK_Handle{pos - base_pos, j, false, next_id++}; + handles.insert(handle); + return handle; +} + +bool Skeleton::do_ik() { + std::vector enabled; + for (IK_Handle* h : handles) { + if (h->enabled) { + enabled.push_back(h); + } + } + if (enabled.empty()) return false; + step_ik(std::move(enabled)); + return true; +} + diff --git a/src/scene/skeleton.h b/src/scene/skeleton.h index 99fa151..0c7c17a 100644 --- a/src/scene/skeleton.h +++ b/src/scene/skeleton.h @@ -59,6 +59,13 @@ private: // Set of child joints - owned by this joint (could be shared_ptr and everything else weak_ptr) std::unordered_set children; + // Current angle gradient for IK + Vec3 angle_gradient; + + // Computes the gradient of IK energy for this joint and, recursively, + // upward in the heirarchy, storing the result in angle_gradient. + void compute_gradient(Vec3 target, Vec3 current); + void for_joints(std::function func); unsigned int _id = 0; @@ -70,6 +77,13 @@ private: class Skeleton { public: + struct IK_Handle { + Vec3 target; + Joint* joint = nullptr; + bool enabled = false; + unsigned int _id = 0; + }; + Skeleton(); Skeleton(unsigned int obj_id); ~Skeleton(); @@ -80,36 +94,51 @@ public: void operator=(const Skeleton &src) = delete; Skeleton &operator=(Skeleton &&src) = default; + //////////////////////////////////////////// + // You will implement these functions + + Vec3 end_of(Joint *j); + Vec3 posed_end_of(Joint *j); + + void step_ik(std::vector active_handles); + + Mat4 joint_to_bind(const Joint *j) const; + Mat4 joint_to_posed(const Joint *j) const; + + void find_joints(const GL::Mesh &src, + std::unordered_map> &map); + void skin(const GL::Mesh &input, GL::Mesh &output, + const std::unordered_map> &map); + + //////////////////////////////////////////// + Vec3 &base(); bool has_bones() const; unsigned int n_bones(); + unsigned int n_handles(); Joint *parent(Joint *j); Joint *get_joint(unsigned int id); void erase(Joint *j); void restore(Joint *j); - Vec3 end_of(Joint *j); Vec3 base_of(Joint *j); - Vec3 posed_end_of(Joint *j); Vec3 posed_base_of(Joint *j); void for_joints(std::function func); - Mat4 joint_to_bind(const Joint *j) const; - Mat4 joint_to_posed(const Joint *j) const; + void erase(IK_Handle* handle); + void restore(IK_Handle* handle); + IK_Handle *get_handle(unsigned int id); + IK_Handle *add_handle(Vec3 pos, Joint* j); + bool do_ik(); Joint *add_root(Vec3 extent); Joint *add_child(Joint *j, Vec3 extent); bool is_root_id(unsigned int id); bool set_time(float time); - void render(const Mat4 &view, Joint *select, bool root, bool posed, unsigned int offset = 0); - void outline(const Mat4 &view, Joint *select, bool root, bool posed, BBox &box, - unsigned int offset = 0); - void find_joints(const GL::Mesh &src, - std::unordered_map> &map); - void skin(const GL::Mesh &input, GL::Mesh &output, - const std::unordered_map> &map); + void render(const Mat4 &view, Joint *jselect, IK_Handle *hselect, bool root, bool posed, unsigned int offset = 0); + void outline(const Mat4 &view, bool root, bool posed, BBox &box, unsigned int offset = 0); void set(float t); void crop(float t); @@ -123,6 +152,8 @@ public: private: Vec3 base_pos; unsigned int root_id, next_id; - std::unordered_set roots, erased; + std::unordered_set roots; + std::unordered_map> erased; + std::unordered_set handles, erased_handles; friend class Scene; }; diff --git a/src/scene/undo.cpp b/src/scene/undo.cpp index 25d4b53..ea724e2 100644 --- a/src/scene/undo.cpp +++ b/src/scene/undo.cpp @@ -74,6 +74,28 @@ void Undo::del_bone(Scene_ID id, Joint *j) { Scene_Object &obj = scene.get_obj(id); obj.armature.erase(j); gui.get_rig().invalidate(j); + gui.get_animate().invalidate(j); + obj.set_skel_dirty(); + }, + [this, id, j]() { + Scene_Object &obj = scene.get_obj(id); + obj.armature.restore(j); + obj.set_skel_dirty(); + }); +} + +void Undo::del_handle(Scene_ID id, Skeleton::IK_Handle *j) { + + Scene_Object &obj = scene.get_obj(id); + obj.armature.erase(j); + obj.set_skel_dirty(); + + action( + [this, id, j]() { + Scene_Object &obj = scene.get_obj(id); + obj.armature.erase(j); + gui.get_animate().invalidate(j); + gui.get_rig().invalidate(j); obj.set_skel_dirty(); }, [this, id, j]() { @@ -97,6 +119,20 @@ void Undo::move_bone(Scene_ID id, Joint *j, Vec3 old) { }); } +void Undo::move_handle(Scene_ID id, Skeleton::IK_Handle *j, Vec3 old) { + action( + [this, id, j, ne = j->target]() { + Scene_Object &obj = scene.get_obj(id); + j->target = ne; + obj.set_skel_dirty(); + }, + [this, id, j, oe = old]() { + Scene_Object &obj = scene.get_obj(id); + j->target = oe; + obj.set_skel_dirty(); + }); +} + void Undo::pose_bone(Scene_ID id, Joint *j, Vec3 old) { action( [this, id, j, ne = j->pose]() { @@ -111,6 +147,22 @@ void Undo::pose_bone(Scene_ID id, Joint *j, Vec3 old) { }); } +void Undo::add_handle(Scene_ID id, Skeleton::IK_Handle *j) { + action( + [this, id, j]() { + Scene_Object &obj = scene.get_obj(id); + obj.armature.restore(j); + obj.set_skel_dirty(); + }, + [this, id, j]() { + Scene_Object &obj = scene.get_obj(id); + obj.armature.erase(j); + gui.get_rig().invalidate(j); + gui.get_animate().invalidate(j); + obj.set_skel_dirty(); + }); +} + void Undo::add_bone(Scene_ID id, Joint *j) { action( @@ -123,6 +175,7 @@ void Undo::add_bone(Scene_ID id, Joint *j) { Scene_Object &obj = scene.get_obj(id); obj.armature.erase(j); gui.get_rig().invalidate(j); + gui.get_animate().invalidate(j); obj.set_skel_dirty(); }); } diff --git a/src/scene/undo.h b/src/scene/undo.h index 75e3639..5b86350 100644 --- a/src/scene/undo.h +++ b/src/scene/undo.h @@ -59,9 +59,12 @@ public: void update_pose(Scene_ID id, Pose old); void del_bone(Scene_ID id, Joint *j); + void del_handle(Scene_ID id, Skeleton::IK_Handle *j); + void add_handle(Scene_ID id, Skeleton::IK_Handle *j); void add_bone(Scene_ID id, Joint *j); void move_bone(Scene_ID id, Joint *j, Vec3 old); void pose_bone(Scene_ID id, Joint *j, Vec3 old); + void move_handle(Scene_ID id, Skeleton::IK_Handle *j, Vec3 old); void move_root(Scene_ID id, Vec3 old); void update_light(Scene_ID id, Scene_Light::Options old); diff --git a/src/student/skeleton.cpp b/src/student/skeleton.cpp index eb93b8f..be69566 100644 --- a/src/student/skeleton.cpp +++ b/src/student/skeleton.cpp @@ -111,3 +111,19 @@ void Skeleton::skin(const GL::Mesh &input, GL::Mesh &output, std::vector idxs = input.indices(); output.recreate(std::move(verts), std::move(idxs)); } + +void Joint::compute_gradient(Vec3 target, Vec3 current) { + + // TODO(Animation): Task 2 + + // Computes the gradient of IK energy for this joint and, should be called + // recursively upward in the heirarchy. Each call should storing the result + // in the angle_gradient for this joint. +} + +void Skeleton::step_ik(std::vector active_handles) { + + // TODO(Animation): Task 2 + + // Do several iterations of Jacobian Transpose gradient descent for IK +} diff --git a/src/util/hdr_image.cpp b/src/util/hdr_image.cpp index 967bb1c..723ad54 100644 --- a/src/util/hdr_image.cpp +++ b/src/util/hdr_image.cpp @@ -90,6 +90,8 @@ std::string HDR_Image::load_from(std::string file) { size_t didx = 4 * (j * w + i); size_t pidx = (h - j - 1) * w + i; pixels[pidx] = Spectrum(data[didx], data[didx + 1], data[didx + 2]); + if (!pixels[pidx].valid()) + pixels[pidx] = {}; } } @@ -118,12 +120,12 @@ std::string HDR_Image::load_from(std::string file) { } stbi_image_free(data); - } - for (size_t i = 0; i < pixels.size(); i++) { - pixels[i].make_linear(); - if (!pixels[i].valid()) - pixels[i] = {}; + for (size_t i = 0; i < pixels.size(); i++) { + pixels[i].make_linear(); + if (!pixels[i].valid()) + pixels[i] = {}; + } } last_path = file; -- GitLab