diff --git a/.gitmodules b/.gitmodules index 7f47fa72a3..a59fd9f63b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "projects/cgmesh/libigl"] path = projects/cgmesh/libigl url = https://github.com/zenustech/libigl.git -[submodule "projects/FEM/OpenSim"] - path = projects/FEM/OpenSim - url = https://github.com/zenustech/opensim-core.git diff --git a/docs/dev_win10.md b/docs/dev_win10.md index e97b970cb5..f481a6cb08 100644 --- a/docs/dev_win10.md +++ b/docs/dev_win10.md @@ -46,6 +46,9 @@ vcpkg install lapack:x64-windows @rem (Optional) Install Alembic for the extension Alembic: vcpkg install alembic[hdf5]:x64-windows + +@rem (Optional) Install libigl for the extension Skinning: +vcpkg install libigl:x64-windows ``` > Notice that you may need to install the `English Pack` for VS2019 for vcpkg to work. diff --git a/projects/FEM/CMakeLists.txt b/projects/FEM/CMakeLists.txt index 4b21ee2a3a..2e6a56c2b4 100644 --- a/projects/FEM/CMakeLists.txt +++ b/projects/FEM/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.16) set(CMAKE_CXX_STANDARD 17) + +if(WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") +endif(WIN32) + project(zeno_FEM) find_package(Eigen3 REQUIRED NO_MODULE) diff --git a/projects/FEM/OpenSim b/projects/FEM/OpenSim deleted file mode 160000 index 6faaed70f2..0000000000 --- a/projects/FEM/OpenSim +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6faaed70f297c89c6c24b025bf92960245c2fb06 diff --git a/projects/FEM/nodesys.cpp b/projects/FEM/nodesys.cpp index d1c754f106..ac5c1769e0 100644 --- a/projects/FEM/nodesys.cpp +++ b/projects/FEM/nodesys.cpp @@ -2159,7 +2159,7 @@ struct SolveEquaUsingNRSolver : zeno::INode { } stop_error *= epsilon; - stop_error *= sqrt(mesh->_mesh->size()); + stop_error *= sqrt((double)mesh->_mesh->size()); std::cout << "STOP_ERROR : " << stop_error << std::endl; } @@ -2701,7 +2701,7 @@ struct GetEffectiveStress : zeno::INode { dynamic_cast(force_model->_forceModel.get())->ComputePrincipalStress(attrbs,s,ps); - FEM_Scaler vm = pow(ps[0] - ps[1],2) + pow(ps[1] - ps[2],2) + pow(ps[0] - ps[2],2); + FEM_Scaler vm = pow(ps[0] - ps[1], 2.0) + pow(ps[1] - ps[2], 2.0) + pow(ps[0] - ps[2], 2.0); vm = vm / 2; vm = sqrt(vm); diff --git a/projects/FEM/src/bspline/cubicBspline.cpp b/projects/FEM/src/bspline/cubicBspline.cpp index bd90776241..c98118fe65 100644 --- a/projects/FEM/src/bspline/cubicBspline.cpp +++ b/projects/FEM/src/bspline/cubicBspline.cpp @@ -67,7 +67,7 @@ void UniformCubicBasisSpline::Interpolate(const VecXd& interps,const Vec2d& _int } void UniformCubicBasisSpline::SetCtrlPoints(const VecXd& _ctrlps) { - assert(_ctrlsps.size() == ctrlps.size()); + assert(_ctrlps.size() == ctrlps.size()); ctrlps = _ctrlps; poly_bezier_ctrlps = cp2pp_map * ctrlps; update_integration_offset(); diff --git a/projects/FEM/src/force_model/base_elastic_model.h b/projects/FEM/src/force_model/base_elastic_model.h index 8cb161e7f4..36ed976af6 100644 --- a/projects/FEM/src/force_model/base_elastic_model.h +++ b/projects/FEM/src/force_model/base_elastic_model.h @@ -7,6 +7,7 @@ #include #include +#include /** * @class diff --git a/projects/Skinning/CMakeLists.txt b/projects/Skinning/CMakeLists.txt index 52d05f7f46..3d79a635f4 100644 --- a/projects/Skinning/CMakeLists.txt +++ b/projects/Skinning/CMakeLists.txt @@ -4,20 +4,28 @@ project(zeno_SKINNING) find_package(Eigen3 REQUIRED NO_MODULE) +if(WIN32) + find_package(libigl REQUIRED) +endif(WIN32) + find_package(OpenMP) if(NOT OpenMP_CXX_FOUND) endif(NOT OpenMP_CXX_FOUND) set(SKINNING_SOURCE_FILES - skinning_nodesys.cpp + bone_animation.cpp + bone_system.cpp + igl_file_reader.cpp + skinning_bbw.cpp + skinning.cpp ) add_library(zeno_SKINNING SHARED ${SKINNING_SOURCE_FILES}) target_link_libraries(zeno_SKINNING PRIVATE Eigen3::Eigen) target_link_libraries(zeno_SKINNING PUBLIC OpenMP::OpenMP_CXX) -target_link_libraries(zeno_SKINNING PRIVATE igl::core igl::cgal) +target_link_libraries(zeno_SKINNING PRIVATE igl::core) target_link_libraries(zeno_SKINNING PUBLIC zeno) target_link_libraries(zeno_SKINNING PRIVATE zeno_nodep) diff --git a/projects/Skinning/bone_animation.cpp b/projects/Skinning/bone_animation.cpp new file mode 100644 index 0000000000..ee3f9b20a6 --- /dev/null +++ b/projects/Skinning/bone_animation.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include "skinning_iobject.h" + +namespace{ +using namespace zeno; + +struct ReadPoseFrame : zeno::INode { + virtual void apply() override { + auto res = std::make_shared(); + auto bones = get_input("bones"); + auto dmat_path = get_input("dmat_path")->get(); + + Eigen::MatrixXd Q; + igl::readDMAT(dmat_path,Q); + + if(bones->lines.size() != Q.rows()/4 || Q.rows() % 4 != 0){ + std::cout << "THE DIMENSION OF BONES DOES NOT MATCH POSES " << bones->lines.size() << "\t" << Q.rows() << std::endl; + throw std::runtime_error("THE DIMENSION OF BONES DOES NOT MATCH POSES"); + } + + igl::column_to_quats(Q,res->posesFrame); + set_output("posesFrame",std::move(res)); + } +}; + +ZENDEFNODE(ReadPoseFrame, { + {{"readpath","dmat_path"},"bones"}, + {"posesFrame"}, + {}, + {"Skinning"}, +}); + +} \ No newline at end of file diff --git a/projects/Skinning/bone_system.cpp b/projects/Skinning/bone_system.cpp new file mode 100644 index 0000000000..901b22f009 --- /dev/null +++ b/projects/Skinning/bone_system.cpp @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "skinning_iobject.h" + +namespace{ +using namespace zeno; + +struct MakeEmptyBones : zeno::INode { + virtual void apply() override { + auto root = get_input("root")->get(); + auto res = std::make_shared(); + + res->resize(1); + res->verts[0] = root; + + set_output("bones",std::move(res)); + } +}; + +ZENDEFNODE(MakeEmptyBones, { + {"root"}, + {"bones"}, + {}, + {"Skinning"}, +}); + +struct AddBone : zeno::INode { + virtual void apply() override { + auto bones = get_input("bones"); + auto node = get_input("node")->get(); + auto connect_to = get_input("conn_to")->get(); + + int idx = bones->size(); + + bones->verts.emplace_back(node); + bones->lines.emplace_back(connect_to,idx); + + set_output("bones_out",bones); + } +}; + +ZENDEFNODE(AddBone, { + {"bones","node","conn_to"}, + {"bones_out"}, + {}, + {"Skinning"}, +}); + +}; \ No newline at end of file diff --git a/projects/Skinning/igl_file_reader.cpp b/projects/Skinning/igl_file_reader.cpp new file mode 100644 index 0000000000..8f2aaf0364 --- /dev/null +++ b/projects/Skinning/igl_file_reader.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "skinning_iobject.h" + +namespace{ +using namespace zeno; + +struct LoadTFGPrimitiveFromFile : zeno::INode { + virtual void apply() override { + auto res = std::make_shared(); + auto tfg_path = get_input("tfg")->get(); + + Eigen::MatrixXd C; + Eigen::MatrixXi BE; + igl::readTGF(tfg_path,C,BE); + + assert(C.cols() == 3); + assert(BE.cols() == 2); + + + // auto& parents = res->add_attr("parents"); + res->resize(C.rows()); + auto& pos = res->attr("pos"); + auto& segs = res->lines; + segs.resize(BE.rows()); + + for(size_t i = 0;i < C.rows();++i) + pos[i] = zeno::vec3f(C.row(i)[0],C.row(i)[1],C.row(i)[2]); + for(size_t i = 0;i < BE.rows();++i) + segs[i] = zeno::vec2i(BE.row(i)[0],BE.row(i)[1]); + + // Eigen::VectorXi P; + // igl::directed_edge_parents(BE,P); + + // std::cout << "BE : " << std::endl << BE << std::endl; + // std::cout << "P : " << std::endl << P.transpose() << std::endl; + + // std::cout << "P : " << P.rows() << "\t" << P.cols() << std::endl; + // std::cout << "parents : " << parents.size() << std::endl; + + // for(size_t i = 0;i < parents.size();++i) + // parents[i] = P[i]; + + set_output("res",std::move(res)); + } +}; + +ZENDEFNODE(LoadTFGPrimitiveFromFile, { + {{"readpath","tfg"}}, + {"res"}, + {}, + {"Skinning"}, +}); + +struct ReadMesh : zeno::INode { + virtual void apply() override { + auto res = std::make_shared(); + auto mesh_path = get_input("mesh")->get(); + + Eigen::MatrixXd V; + Eigen::MatrixXi T,F; + igl::readMESH(mesh_path,V,T,F); + + // we only support 3d simplex volumetric meshing + assert(V.cols() == 3 && T.cols() == 4 && F.cols() == 3); + + res->resize(V.rows()); + for(size_t i = 0;i < V.rows();++i) + res->verts[i] = zeno::vec3f(V.row(i)[0],V.row(i)[1],V.row(i)[2]); + for(size_t i = 0;i < T.rows();++i) + res->quads.emplace_back(T.row(i)[0],T.row(i)[1],T.row(i)[2],T.row(i)[3]); + + for(size_t i = 0;i < res->quads.size();++i){ + auto tet = res->quads[i]; + res->tris.emplace_back(tet[0],tet[1],tet[2]); + res->tris.emplace_back(tet[1],tet[3],tet[2]); + res->tris.emplace_back(tet[0],tet[2],tet[3]); + res->tris.emplace_back(tet[0],tet[3],tet[1]); + } + + set_output("res",std::move(res)); + } +}; + +ZENDEFNODE(ReadMesh, { + {{"readpath","mesh"}}, + {"res"}, + {}, + {"Skinning"}, +}); + +}; \ No newline at end of file diff --git a/projects/Skinning/skinning.cpp b/projects/Skinning/skinning.cpp new file mode 100644 index 0000000000..2daca0fec3 --- /dev/null +++ b/projects/Skinning/skinning.cpp @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +#include + +#include "skinning_iobject.h" + +namespace{ +using namespace zeno; + +struct PosesAnimationFrame; + +struct MakeRestPoses : zeno::INode { + virtual void apply() override { + auto res = std::make_shared(); + auto bones = get_input("bones"); + + res->posesFrame.resize(bones->lines.size(),Eigen::Quaterniond::Identity()); + + set_output("posesFrame",std::move(res)); + } +}; + +ZENDEFNODE(MakeRestPoses, { + {"bones"}, + {"posesFrame"}, + {}, + {"Skinning"}, +}); + +struct BlendPoses : zeno::INode { + virtual void apply() override { + auto poses1 = get_input("p1"); + auto poses2 = get_input("p2"); + + // if(poses1->posesFrame.cols() != 4 || poses1->posesFrame.cols() != 4) + // throw std::runtime_error("INVALIED POSES DIMENSION"); + + if(poses1->posesFrame.size() != poses2->posesFrame.size()){ + std::cout << "THE DIMENSION OF TWO MERGED POSES DOES NOT MATCH " << poses1->posesFrame.size() << "\t" << poses2->posesFrame.size() << std::endl; + throw std::runtime_error("THE DIMENSION OF TWO MERGED POSES DOES NOT MATCH"); + } + + auto w = get_input("w")->get(); + + auto res = std::make_shared(); + res->posesFrame.resize(poses1->posesFrame.size()); + + for(size_t i = 0;i < res->posesFrame.size();++i){ + res->posesFrame[i] = poses1->posesFrame[i].slerp(1-w,poses2->posesFrame[i]); + } + + // std::cout << "OUT_POSES : " << std::endl; + // for(size_t i = 0;i < res->posesFrame.size();++i) + // std::cout << "P<" << i << "> : " << res->posesFrame[i] << std::endl; + + set_output("bp",std::move(res)); + } +}; + +ZENDEFNODE(BlendPoses, { + {"p1","p2","w"}, + {"bp"}, + {}, + {"Skinning"}, +}); + + +struct DoSkinning : zeno::INode { + virtual void apply() override { + std::cout << "DO LINEAR BLEND SKINNING" << std::endl; + + auto shape = get_input("shape"); + auto pose = get_input("pose"); + auto bones = get_input("bones"); + auto W = get_input("W"); + + auto algorithm = std::get(get_param("algorithm")); + + RotationList vQ; + std::vector vT; + + Eigen::MatrixXd C; + Eigen::MatrixXi BE; + + C.resize(bones->size(),3); + BE.resize(bones->lines.size(),2); + + for(size_t i = 0;i < bones->size();++i){ + C.row(i) << bones->verts[i][0],bones->verts[i][1],bones->verts[i][2]; + } + + for(size_t i = 0;i < bones->lines.size();++i) + BE.row(i) << bones->lines[i][0],bones->lines[i][1]; + + + Eigen::VectorXi P; + igl::directed_edge_parents(BE,P); + + // std::cout << "DO FORWARD KINEMATICS" << std::endl; + igl::forward_kinematics(C,BE,P,pose->posesFrame,vQ,vT); + + const int dim = C.cols(); + Eigen::MatrixXd T(BE.rows()*(dim+1),dim); + for(int e = 0;esize(),3); + for(size_t i = 0;i < V.rows();++i) + V.row(i) << shape->verts[i][0],shape->verts[i][1],shape->verts[i][2]; + + if(algorithm == "DQS"){ + igl::dqs(V,W->weight,vQ,vT,U); + }else if(algorithm == "LBS"){ + Eigen::MatrixXd M; + igl::lbs_matrix(V,W->weight,M); + U = M*T; + } + // std::cout << "U : " << U.rows() << "\t" << U.cols() << std::endl; + // std::cout << "BLSW : " << blsw->weight.rows() << "\t" << blsw->weight.cols() << std::endl; + // std::cout << "T : " << T.rows() << "\t" << T.cols() << std::endl; + + auto deformed_shape = std::make_shared(); + deformed_shape->resize(shape->size()); + deformed_shape->tris.resize(shape->tris.size()); + deformed_shape->quads.resize(shape->quads.size()); + + for(size_t i = 0;i < deformed_shape->size();++i) + deformed_shape->verts[i] = zeno::vec3f(U.row(i)[0],U.row(i)[1],U.row(i)[2]); + + for(size_t i = 0;i < shape->tris.size();++i) + deformed_shape->tris[i] = shape->tris[i]; + + for(size_t i = 0;i < shape->quads.size();++i) + deformed_shape->quads[i] = shape->quads[i]; + + // Also deform skeleton edges + Eigen::MatrixXd CT; + Eigen::MatrixXi BET; + igl::deform_skeleton(C,BE,T,CT,BET); + + auto deformed_bones = std::make_shared(); + deformed_bones->resize(CT.rows()); + deformed_bones->lines.resize(BET.rows()); + + for(size_t i = 0;i < CT.rows();++i) + deformed_bones->verts[i] = zeno::vec3f(CT.row(i)[0],CT.row(i)[1],CT.row(i)[2]); + for(size_t i = 0;i < BET.rows();++i) + deformed_bones->lines[i] = zeno::vec2i(BET.row(i)[0],BET.row(i)[1]); + + set_output("dshape",std::move(deformed_shape)); + set_output("dbones",std::move(deformed_bones)); + + std::cout << "FINISH OUTPUT DEFORMED SHAPE AND BONES" << std::endl; + } +}; + +ZENDEFNODE(DoSkinning, { + {"shape","pose","bones","W"}, + {"dshape","dbones"}, + {{"enum LBS DQS","algorithm","LBS"}}, + {"Skinning"}, +}); + +}; \ No newline at end of file diff --git a/projects/Skinning/skinning_bbw.cpp b/projects/Skinning/skinning_bbw.cpp new file mode 100644 index 0000000000..4f46279d23 --- /dev/null +++ b/projects/Skinning/skinning_bbw.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "skinning_iobject.h" + +namespace{ +using namespace zeno; + + + + +struct GenerateSkinningWeight : zeno::INode { + virtual void apply() override { + auto mesh = get_input("skinMesh"); + auto tfg = get_input("skinBone"); + + Eigen::MatrixXd V; + Eigen::MatrixXi T; + Eigen::MatrixXd C; + Eigen::MatrixXi BE; + + // first we need to transfer the data from zeno's data structure to igl's + // we only support 3d space only and tetradedron volume mesh + V.resize(mesh->size(),3); + T.resize(mesh->quads.size(),4); + + for(size_t i = 0;i < mesh->size();++i) + V.row(i) << mesh->verts[i][0],mesh->verts[i][1],mesh->verts[i][2]; + for(size_t i = 0;i < mesh->quads.size();++i) + T.row(i) << mesh->quads[i][0],mesh->quads[i][1],mesh->quads[i][2],mesh->quads[i][3]; + + C.resize(tfg->size(),3); + BE.resize(tfg->lines.size(),3); + + // do some vertices alignment here + for(size_t i = 0;i < tfg->size();++i){ + Eigen::Vector3d tfg_vert;tfg_vert << tfg->verts[i][0],tfg->verts[i][1],tfg->verts[i][2]; + Eigen::Vector3d align_vert = tfg_vert; + + double min_dist = 1e18; + for(size_t j = 0;j < mesh->size();++j){ + Eigen::Vector3d mvert;mvert << mesh->verts[j][0],mesh->verts[j][1],mesh->verts[j][2]; + double dist = (mvert - tfg_vert).norm(); + if(dist < min_dist){ + min_dist = dist; + align_vert = mvert; + } + } + C.row(i) = align_vert; + } + for(size_t i = 0;i < tfg->lines.size();++i) + BE.row(i) << tfg->lines[i][0],tfg->lines[i][1]; + + Eigen::VectorXi b; + // List of boundary conditions of each weight function + Eigen::MatrixXd bc; + igl::boundary_conditions(V,T,C,Eigen::VectorXi(),BE,Eigen::MatrixXi(),b,bc); + + + // std::cout << "OUTPUT B AND BC : " << std::endl; + // std::cout << "B : " << std::endl << b << std::endl; + // std::cout << "BC : " << std::endl << bc << std::endl; + + // compute BBW weights matrix + igl::BBWData bbw_data; + // only a few iterations for sake of demo + bbw_data.active_set_params.max_iter = 8; + bbw_data.verbosity = 1; + + Eigen::MatrixXd W; + if(!igl::bbw(V,T,b,bc,bbw_data,W)) + { + throw std::runtime_error("BBW GENERATION FAIL"); + } + + assert(W.rows() == V.rows() && W.cols() == C.rows()); + + auto res = std::make_shared(); + + igl::normalize_row_sums(W,W); + + res->weight = W; + + + std::cout << "OUTPUT_W" << std::endl; + + // add weight channel + for(size_t i = 0;i < W.cols();++i){ + std::string channel_name = "sw_" + std::to_string(i); + auto& c = mesh->add_attr(channel_name); + mesh->resize(V.rows()); + + for(size_t j = 0;j < W.rows();++j){ + c[j] = W(j,i); + // std::cout << W(j,0) << std::endl; + } + + // for(size_t i = 0;i < V.rows();++i) + // std::cout << "<" << i << "> : " << mesh->attr(channel_name)[i] << std::endl; + } + + set_output("W",std::move(res)); + set_output("mesh",mesh); + } +}; + +ZENDEFNODE(GenerateSkinningWeight, { + {"skinMesh","skinBone"}, + {"W","mesh"}, + {}, + {"Skinning"}, +}); + +}; \ No newline at end of file diff --git a/projects/Skinning/skinning_iobject.h b/projects/Skinning/skinning_iobject.h new file mode 100644 index 0000000000..25b8174353 --- /dev/null +++ b/projects/Skinning/skinning_iobject.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace { +using namespace zeno; + +typedef + std::vector > + RotationList; + +struct PosesAnimationFrame : zeno::IObject { + PosesAnimationFrame() = default; + RotationList posesFrame; +}; + +struct SkinningWeight : zeno::IObject { + SkinningWeight() = default; + Eigen::MatrixXd weight; +}; + +}; \ No newline at end of file diff --git a/projects/Skinning/skinning_nodesys.cpp b/projects/Skinning/skinning_nodesys.cpp deleted file mode 100644 index f1a51866c7..0000000000 --- a/projects/Skinning/skinning_nodesys.cpp +++ /dev/null @@ -1,495 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include - - -namespace{ -using namespace zeno; - - -typedef - std::vector > - RotationList; - -struct LoadTFGPrimitiveFromFile : zeno::INode { - virtual void apply() override { - auto res = std::make_shared(); - auto tfg_path = get_input("tfg")->get(); - - Eigen::MatrixXd C; - Eigen::MatrixXi BE; - igl::readTGF(tfg_path,C,BE); - - assert(C.cols() == 3); - assert(BE.cols() == 2); - - - // auto& parents = res->add_attr("parents"); - res->resize(C.rows()); - auto& pos = res->attr("pos"); - auto& segs = res->lines; - segs.resize(BE.rows()); - - for(size_t i = 0;i < C.rows();++i) - pos[i] = zeno::vec3f(C.row(i)[0],C.row(i)[1],C.row(i)[2]); - for(size_t i = 0;i < BE.rows();++i) - segs[i] = zeno::vec2i(BE.row(i)[0],BE.row(i)[1]); - - // Eigen::VectorXi P; - // igl::directed_edge_parents(BE,P); - - // std::cout << "BE : " << std::endl << BE << std::endl; - // std::cout << "P : " << std::endl << P.transpose() << std::endl; - - // std::cout << "P : " << P.rows() << "\t" << P.cols() << std::endl; - // std::cout << "parents : " << parents.size() << std::endl; - - // for(size_t i = 0;i < parents.size();++i) - // parents[i] = P[i]; - - set_output("res",std::move(res)); - } -}; - -ZENDEFNODE(LoadTFGPrimitiveFromFile, { - {{"readpath","tfg"}}, - {"res"}, - {}, - {"Skinning"}, -}); - -struct MakeEmptyBones : zeno::INode { - virtual void apply() override { - auto root = get_input("root")->get(); - auto res = std::make_shared(); - - res->resize(1); - res->verts[0] = root; - - set_output("bones",std::move(res)); - } -}; - -ZENDEFNODE(MakeEmptyBones, { - {"root"}, - {"bones"}, - {}, - {"Skinning"}, -}); - -struct AddBone : zeno::INode { - virtual void apply() override { - auto bones = get_input("bones"); - auto node = get_input("node")->get(); - auto connect_to = get_input("conn_to")->get(); - - int idx = bones->size(); - - bones->verts.emplace_back(node); - bones->lines.emplace_back(connect_to,idx); - - set_output("bones_out",bones); - } -}; - -ZENDEFNODE(AddBone, { - {"bones","node","conn_to"}, - {"bones_out"}, - {}, - {"Skinning"}, -}); - - -// struct RetrieveTFGByFromPrim : zeno::INode {} - -struct ReadMesh : zeno::INode { - virtual void apply() override { - auto res = std::make_shared(); - auto mesh_path = get_input("mesh")->get(); - - Eigen::MatrixXd V; - Eigen::MatrixXi T,F; - igl::readMESH(mesh_path,V,T,F); - - // we only support 3d simplex volumetric meshing - assert(V.cols() == 3 && T.cols() == 4 && F.cols() == 3); - - res->resize(V.rows()); - for(size_t i = 0;i < V.rows();++i) - res->verts[i] = zeno::vec3f(V.row(i)[0],V.row(i)[1],V.row(i)[2]); - for(size_t i = 0;i < T.rows();++i) - res->quads.emplace_back(T.row(i)[0],T.row(i)[1],T.row(i)[2],T.row(i)[3]); - - for(size_t i = 0;i < res->quads.size();++i){ - auto tet = res->quads[i]; - res->tris.emplace_back(tet[0],tet[1],tet[2]); - res->tris.emplace_back(tet[1],tet[3],tet[2]); - res->tris.emplace_back(tet[0],tet[2],tet[3]); - res->tris.emplace_back(tet[0],tet[3],tet[1]); - } - - set_output("res",std::move(res)); - } -}; - -ZENDEFNODE(ReadMesh, { - {{"readpath","mesh"}}, - {"res"}, - {}, - {"Skinning"}, -}); - - -struct SkinningWeight : zeno::IObject { - SkinningWeight() = default; - Eigen::MatrixXd weight; -}; - -struct GenerateSkinningWeight : zeno::INode { - virtual void apply() override { - auto mesh = get_input("skinMesh"); - auto tfg = get_input("skinBone"); - - Eigen::MatrixXd V; - Eigen::MatrixXi T; - Eigen::MatrixXd C; - Eigen::MatrixXi BE; - - // first we need to transfer the data from zeno's data structure to igl's - // we only support 3d space only and tetradedron volume mesh - V.resize(mesh->size(),3); - T.resize(mesh->quads.size(),4); - - for(size_t i = 0;i < mesh->size();++i) - V.row(i) << mesh->verts[i][0],mesh->verts[i][1],mesh->verts[i][2]; - for(size_t i = 0;i < mesh->quads.size();++i) - T.row(i) << mesh->quads[i][0],mesh->quads[i][1],mesh->quads[i][2],mesh->quads[i][3]; - - C.resize(tfg->size(),3); - BE.resize(tfg->lines.size(),3); - - // do some vertices alignment here - for(size_t i = 0;i < tfg->size();++i){ - Eigen::Vector3d tfg_vert;tfg_vert << tfg->verts[i][0],tfg->verts[i][1],tfg->verts[i][2]; - Eigen::Vector3d align_vert = tfg_vert; - - double min_dist = 1e18; - for(size_t j = 0;j < mesh->size();++j){ - Eigen::Vector3d mvert;mvert << mesh->verts[j][0],mesh->verts[j][1],mesh->verts[j][2]; - double dist = (mvert - tfg_vert).norm(); - if(dist < min_dist){ - min_dist = dist; - align_vert = mvert; - } - } - C.row(i) = align_vert; - } - for(size_t i = 0;i < tfg->lines.size();++i) - BE.row(i) << tfg->lines[i][0],tfg->lines[i][1]; - - Eigen::VectorXi b; - // List of boundary conditions of each weight function - Eigen::MatrixXd bc; - igl::boundary_conditions(V,T,C,Eigen::VectorXi(),BE,Eigen::MatrixXi(),b,bc); - - - // std::cout << "OUTPUT B AND BC : " << std::endl; - // std::cout << "B : " << std::endl << b << std::endl; - // std::cout << "BC : " << std::endl << bc << std::endl; - - // compute BBW weights matrix - igl::BBWData bbw_data; - // only a few iterations for sake of demo - bbw_data.active_set_params.max_iter = 8; - bbw_data.verbosity = 1; - - Eigen::MatrixXd W; - if(!igl::bbw(V,T,b,bc,bbw_data,W)) - { - throw std::runtime_error("BBW GENERATION FAIL"); - } - - assert(W.rows() == V.rows() && W.cols() == C.rows()); - - auto res = std::make_shared(); - - igl::normalize_row_sums(W,W); - - res->weight = W; - - - std::cout << "OUTPUT_W" << std::endl; - - // add weight channel - for(size_t i = 0;i < W.cols();++i){ - std::string channel_name = "sw_" + std::to_string(i); - auto& c = mesh->add_attr(channel_name); - mesh->resize(V.rows()); - - for(size_t j = 0;j < W.rows();++j){ - c[j] = W(j,i); - // std::cout << W(j,0) << std::endl; - } - - // for(size_t i = 0;i < V.rows();++i) - // std::cout << "<" << i << "> : " << mesh->attr(channel_name)[i] << std::endl; - } - - set_output("W",std::move(res)); - set_output("mesh",mesh); - } -}; - -ZENDEFNODE(GenerateSkinningWeight, { - {"skinMesh","skinBone"}, - {"W","mesh"}, - {}, - {"Skinning"}, -}); - -struct BBW2LBSW : zeno::INode { - virtual void apply() override { - auto shape = get_input("skinMesh"); - auto bbw = get_input("bbw"); - auto lbsw = std::make_shared(); - - Eigen::MatrixXd V; - V.resize(shape->size(),3); - for(size_t i = 0;i < V.rows();++i) - V.row(i) << shape->verts[i][0],shape->verts[i][1],shape->verts[i][2]; - // compute linear blend skinning matrix - igl::lbs_matrix(V,bbw->weight,lbsw->weight); - - set_output("lbsw",std::move(lbsw)); - } -}; - -ZENDEFNODE(BBW2LBSW, { - {"skinMesh","bbw"}, - {"lbsw"}, - {}, - {"Skinning"}, -}); - - -struct PosesAnimationFrame : zeno::IObject { - PosesAnimationFrame() = default; - RotationList posesFrame; -}; - -struct ReadPoseFrame : zeno::INode { - virtual void apply() override { - auto res = std::make_shared(); - auto bones = get_input("bones"); - auto dmat_path = get_input("dmat_path")->get(); - - Eigen::MatrixXd Q; - igl::readDMAT(dmat_path,Q); - - if(bones->lines.size() != Q.rows()/4 || Q.rows() % 4 != 0){ - std::cout << "THE DIMENSION OF BONES DOES NOT MATCH POSES " << bones->lines.size() << "\t" << Q.rows() << std::endl; - throw std::runtime_error("THE DIMENSION OF BONES DOES NOT MATCH POSES"); - } - - igl::column_to_quats(Q,res->posesFrame); - set_output("posesFrame",std::move(res)); - } -}; - -ZENDEFNODE(ReadPoseFrame, { - {{"readpath","dmat_path"},"bones"}, - {"posesFrame"}, - {}, - {"Skinning"}, -}); - -struct MakeRestPoses : zeno::INode { - virtual void apply() override { - auto res = std::make_shared(); - auto bones = get_input("bones"); - - res->posesFrame.resize(bones->lines.size(),Eigen::Quaterniond::Identity()); - - set_output("posesFrame",std::move(res)); - } -}; - -ZENDEFNODE(MakeRestPoses, { - {"bones"}, - {"posesFrame"}, - {}, - {"Skinning"}, -}); - - -struct BlendPoses : zeno::INode { - virtual void apply() override { - auto poses1 = get_input("p1"); - auto poses2 = get_input("p2"); - - // if(poses1->posesFrame.cols() != 4 || poses1->posesFrame.cols() != 4) - // throw std::runtime_error("INVALIED POSES DIMENSION"); - - if(poses1->posesFrame.size() != poses2->posesFrame.size()){ - std::cout << "THE DIMENSION OF TWO MERGED POSES DOES NOT MATCH " << poses1->posesFrame.size() << "\t" << poses2->posesFrame.size() << std::endl; - throw std::runtime_error("THE DIMENSION OF TWO MERGED POSES DOES NOT MATCH"); - } - - auto w = get_input("w")->get(); - - auto res = std::make_shared(); - res->posesFrame.resize(poses1->posesFrame.size()); - - for(size_t i = 0;i < res->posesFrame.size();++i){ - res->posesFrame[i] = poses1->posesFrame[i].slerp(1-w,poses2->posesFrame[i]); - } - - std::cout << "OUT_POSES : " << std::endl; - for(size_t i = 0;i < res->posesFrame.size();++i) - std::cout << "P<" << i << "> : " << res->posesFrame[i] << std::endl; - - set_output("bp",std::move(res)); - } -}; - -ZENDEFNODE(BlendPoses, { - {"p1","p2","w"}, - {"bp"}, - {}, - {"Skinning"}, -}); - - -struct DoSkinning : zeno::INode { - virtual void apply() override { - std::cout << "DO LINEAR BLEND SKINNING" << std::endl; - - auto shape = get_input("shape"); - auto pose = get_input("pose"); - auto bones = get_input("bones"); - auto W = get_input("W"); - - auto algorithm = std::get(get_param("algorithm")); - - RotationList vQ; - std::vector vT; - - Eigen::MatrixXd C; - Eigen::MatrixXi BE; - - C.resize(bones->size(),3); - BE.resize(bones->lines.size(),2); - - for(size_t i = 0;i < bones->size();++i){ - C.row(i) << bones->verts[i][0],bones->verts[i][1],bones->verts[i][2]; - } - - for(size_t i = 0;i < bones->lines.size();++i) - BE.row(i) << bones->lines[i][0],bones->lines[i][1]; - - - Eigen::VectorXi P; - igl::directed_edge_parents(BE,P); - - // std::cout << "DO FORWARD KINEMATICS" << std::endl; - igl::forward_kinematics(C,BE,P,pose->posesFrame,vQ,vT); - - const int dim = C.cols(); - Eigen::MatrixXd T(BE.rows()*(dim+1),dim); - for(int e = 0;esize(),3); - for(size_t i = 0;i < V.rows();++i) - V.row(i) << shape->verts[i][0],shape->verts[i][1],shape->verts[i][2]; - - if(algorithm == "DQS"){ - igl::dqs(V,W->weight,vQ,vT,U); - }else if(algorithm == "LBS"){ - Eigen::MatrixXd M; - igl::lbs_matrix(V,W->weight,M); - U = M*T; - } - // std::cout << "U : " << U.rows() << "\t" << U.cols() << std::endl; - // std::cout << "BLSW : " << blsw->weight.rows() << "\t" << blsw->weight.cols() << std::endl; - // std::cout << "T : " << T.rows() << "\t" << T.cols() << std::endl; - - auto deformed_shape = std::make_shared(); - deformed_shape->resize(shape->size()); - deformed_shape->tris.resize(shape->tris.size()); - deformed_shape->quads.resize(shape->quads.size()); - - for(size_t i = 0;i < deformed_shape->size();++i) - deformed_shape->verts[i] = zeno::vec3f(U.row(i)[0],U.row(i)[1],U.row(i)[2]); - - for(size_t i = 0;i < shape->tris.size();++i) - deformed_shape->tris[i] = shape->tris[i]; - - for(size_t i = 0;i < shape->quads.size();++i) - deformed_shape->quads[i] = shape->quads[i]; - - // Also deform skeleton edges - Eigen::MatrixXd CT; - Eigen::MatrixXi BET; - igl::deform_skeleton(C,BE,T,CT,BET); - - auto deformed_bones = std::make_shared(); - deformed_bones->resize(CT.rows()); - deformed_bones->lines.resize(BET.rows()); - - for(size_t i = 0;i < CT.rows();++i) - deformed_bones->verts[i] = zeno::vec3f(CT.row(i)[0],CT.row(i)[1],CT.row(i)[2]); - for(size_t i = 0;i < BET.rows();++i) - deformed_bones->lines[i] = zeno::vec2i(BET.row(i)[0],BET.row(i)[1]); - - set_output("dshape",std::move(deformed_shape)); - set_output("dbones",std::move(deformed_bones)); - - std::cout << "FINISH OUTPUT DEFORMED SHAPE AND BONES" << std::endl; - } -}; - -ZENDEFNODE(DoSkinning, { - {"shape","pose","bones","W"}, - {"dshape","dbones"}, - {{"enum LBS DQS","algorithm","LBS"}}, - {"Skinning"}, -}); - - - -};