Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve fbx #1986

Merged
merged 2 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions projects/FBX/DualQuaternion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include "DualQuaternion.h"
#include <cmath>
#include <zeno/utils/bit_operations.h>

DualQuaternion operator+(const DualQuaternion& l, const DualQuaternion& r) {
return DualQuaternion(l.real + r.real, l.dual + r.dual);
}

DualQuaternion operator*(const DualQuaternion& dq, float f) {
return DualQuaternion(dq.real * f, dq.dual * f);
}

bool operator==(const DualQuaternion& l, const DualQuaternion& r) {
return l.real == r.real && l.dual == r.dual;
}

bool operator!=(const DualQuaternion& l, const DualQuaternion& r) {
return l.real != r.real || l.dual != r.dual;
}

// Remember, multiplication order is left to right.
// This is the opposite of matrix and quaternion multiplication order
DualQuaternion operator*(const DualQuaternion& l, const DualQuaternion& r) {
DualQuaternion lhs = normalized(l);
DualQuaternion rhs = normalized(r);
// DualQuaternion lhs = l;
// DualQuaternion rhs = r;

return DualQuaternion(lhs.real * rhs.real, lhs.real * rhs.dual + lhs.dual * rhs.real);
}

float dot(const DualQuaternion& l, const DualQuaternion& r) {
return dot(l.real, r.real);
}

DualQuaternion conjugate(const DualQuaternion& dq) {
return DualQuaternion(conjugate(dq.real), conjugate(dq.dual));
}

DualQuaternion normalized(const DualQuaternion& dq) {
float magSq = dot(dq.real, dq.real);
if (magSq < 0.000001f) {
return DualQuaternion();
}
float invMag = 1.0f / sqrtf(magSq);

return DualQuaternion(dq.real * invMag, dq.dual * invMag);
}

void normalize(DualQuaternion& dq) {
float magSq = dot(dq.real, dq.real);
if (magSq < 0.000001f) {
return;
}
float invMag = 1.0f / sqrtf(magSq);

dq.real = dq.real * invMag;
dq.dual = dq.dual * invMag;
}

static void decomposeMtx(const glm::mat4& m, glm::vec3& pos, glm::quat& rot, glm::vec3& scale)
{
pos = m[3];
for(int i = 0; i < 3; i++)
scale[i] = glm::length(glm::vec3(m[i]));
const glm::mat3 rotMtx(
glm::vec3(m[0]) / scale[0],
glm::vec3(m[1]) / scale[1],
glm::vec3(m[2]) / scale[2]);
rot = glm::quat_cast(rotMtx);
}

constexpr glm::quat dual_quat(const glm::quat& q,const glm::vec3& t) {

auto qx = q.x;
auto qy = q.y;
auto qz = q.z;
auto qw = q.w;
auto tx = t[0];
auto ty = t[1];
auto tz = t[2];

glm::quat qd;
qd.w = -0.5*( tx*qx + ty*qy + tz*qz); // qd.w
qd.x = 0.5*( tx*qw + ty*qz - tz*qy); // qd.x
qd.y = 0.5*(-tx*qz + ty*qw + tz*qx); // qd.y
qd.z = 0.5*( tx*qy - ty*qx + tz*qw); // qd.z

return qd;
}
DualQuaternion mat4ToDualQuat2(const glm::mat4& transformation) {
glm::vec3 scale;
glm::quat rotation;
glm::vec3 translation;
decomposeMtx(transformation, translation, rotation, scale);
glm::quat qr = rotation;
glm::quat qd = dual_quat(qr, translation);
return DualQuaternion(qr, qd);
}

glm::mat4 dualQuatToMat4(const DualQuaternion& dq) {
glm::mat4 rotation = glm::toMat4(dq.real);

glm::quat d = conjugate(dq.real) * (dq.dual * 2.0f);
glm::mat4 position = glm::translate(glm::vec3(d.x, d.y, d.z));

glm::mat4 result = position * rotation;
return result;
}

glm::vec3 transformVector(const DualQuaternion& dq, const glm::vec3& v) {
return dq.real * v;
}

glm::vec3 transformPoint2(const DualQuaternion& dq, const glm::vec3& v){
auto d0 = glm::vec3(dq.real.x, dq.real.y, dq.real.z);
auto de = glm::vec3(dq.dual.x, dq.dual.y, dq.dual.z);
auto a0 = dq.real.w;
auto ae = dq.dual.w;

return v + 2.0f * cross(d0,cross(d0,v) + a0*v) + 2.0f *(a0*de - ae*d0 + cross(d0,de));
}


zeno::vec3f transformVector(const DualQuaternion& dq, const zeno::vec3f& v) {
return zeno::bit_cast<zeno::vec3f>(transformVector(dq, zeno::bit_cast<glm::vec3>(v)));
}

zeno::vec3f transformPoint2(const DualQuaternion& dq, const zeno::vec3f& v) {
return zeno::bit_cast<zeno::vec3f>(transformPoint2(dq, zeno::bit_cast<glm::vec3>(v)));
}
40 changes: 40 additions & 0 deletions projects/FBX/DualQuaternion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef _H_DUALQUATERNION_
#define _H_DUALQUATERNION_

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/transform.hpp>
#include <zeno/utils/vec.h>

struct DualQuaternion {
glm::quat real = {1, 0, 0, 0};
glm::quat dual = {0, 0, 0, 0};
inline DualQuaternion() { }
inline DualQuaternion(const glm::quat& r, const glm::quat& d) :
real(r), dual(d) { }
};

DualQuaternion operator+(const DualQuaternion& l, const DualQuaternion& r);
DualQuaternion operator*(const DualQuaternion& dq, float f);
// Multiplication order is left to right
// Left to right is the OPPOSITE of matrices and quaternions
DualQuaternion operator*(const DualQuaternion& l, const DualQuaternion& r);
bool operator==(const DualQuaternion& l, const DualQuaternion& r);
bool operator!=(const DualQuaternion& l, const DualQuaternion& r);

float dot(const DualQuaternion& l, const DualQuaternion& r);
DualQuaternion conjugate(const DualQuaternion& dq);
DualQuaternion normalized(const DualQuaternion& dq);
void normalize(DualQuaternion& dq);

DualQuaternion mat4ToDualQuat2(const glm::mat4& t);
glm::mat4 dualQuatToMat4(const DualQuaternion& dq);

glm::vec3 transformVector(const DualQuaternion& dq, const glm::vec3& v);
zeno::vec3f transformVector(const DualQuaternion& dq, const zeno::vec3f& v);
zeno::vec3f transformPoint2(const DualQuaternion& dq, const zeno::vec3f& v);

#endif
88 changes: 71 additions & 17 deletions projects/FBX/FBXSDK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/transform.hpp>
#include "DualQuaternion.h"

#ifdef ZENO_FBXSDK
#include <fbxsdk.h>
Expand Down Expand Up @@ -1009,6 +1010,7 @@ struct NewFBXImportAnimation : INode {
ud.set2("boneName_count", int(bone_names.size()));
for (auto i = 0; i < bone_names.size(); i++) {
ud.set2(zeno::format("boneName_{}", i), bone_names[i]);
zeno::log_info("boneName: {}", bone_names[i]);
}
}

Expand Down Expand Up @@ -1387,6 +1389,7 @@ struct NewFBXBoneDeform : INode {
return mapping;
}
virtual void apply() override {
auto usingDualQuaternion = get_input2<std::string>("SkinningMethod") == "DualQuaternion";
auto geometryToDeform = get_input2<PrimitiveObject>("GeometryToDeform");
auto geometryToDeformBoneNames = getBoneNames(geometryToDeform.get());
auto restPointTransformsPrim = get_input2<PrimitiveObject>("RestPointTransforms");
Expand All @@ -1400,6 +1403,8 @@ struct NewFBXBoneDeform : INode {

std::vector<glm::mat4> matrixs;
matrixs.reserve(geometryToDeformBoneNames.size());
std::vector<DualQuaternion> dqs;
dqs.reserve(geometryToDeformBoneNames.size());
for (auto i = 0; i < geometryToDeformBoneNames.size(); i++) {
glm::mat4 res_inv_matrix = glm::mat4(1);
glm::mat4 deform_matrix = glm::mat4(1);
Expand All @@ -1409,6 +1414,7 @@ struct NewFBXBoneDeform : INode {
}
auto matrix = deform_matrix * res_inv_matrix;
matrixs.push_back(matrix);
dqs.push_back(mat4ToDualQuat2(matrix));
}

auto prim = std::dynamic_pointer_cast<PrimitiveObject>(geometryToDeform->clone());
Expand All @@ -1425,16 +1431,31 @@ struct NewFBXBoneDeform : INode {
for (auto i = 0; i < vert_count; i++) {
auto opos = prim->verts[i];
vec3f pos = {};
DualQuaternion dq_acc({0, 0, 0, 0}, {0, 0, 0, 0});
float w = 0;
for (auto j = 0; j < maxnum_boneWeight; j++) {
if (bi[j]->operator[](i) < 0) {
auto index = bi[j]->operator[](i);
if (index < 0) {
continue;
}
auto matrix = matrixs[bi[j]->operator[](i)];
pos += transform_pos(matrix, opos) * bw[j]->operator[](i);
w += bw[j]->operator[](i);
auto weight = bw[j]->operator[](i);
if (usingDualQuaternion) {
dq_acc = dq_acc + dqs[index] * weight;
}
else {
pos += transform_pos(matrixs[index], opos) * weight;
}
w += weight;
}
if (w > 0) {
if (usingDualQuaternion) {
dq_acc = normalized(dq_acc);
prim->verts[i] = transformPoint2(dq_acc, opos);
}
else {
prim->verts[i] = pos / w;
}
}
prim->verts[i] = pos / w;
}
auto vectors_str = get_input2<std::string>("vectors");
std::vector<std::string> vectors = zeno::split_str(vectors_str, ',');
Expand All @@ -1446,17 +1467,33 @@ struct NewFBXBoneDeform : INode {
#pragma omp parallel for
for (auto i = 0; i < vert_count; i++) {
glm::mat4 matrix(0);
DualQuaternion dq_acc({0, 0, 0, 0}, {0, 0, 0, 0});
float w = 0;
for (auto j = 0; j < maxnum_boneWeight; j++) {
if (bi[j]->operator[](i) < 0) {
auto index = bi[j]->operator[](i);
if (index < 0) {
continue;
}
matrix += matrixs[bi[j]->operator[](i)] * bw[j]->operator[](i);
w += bw[j]->operator[](i);
auto weight = bw[j]->operator[](i);
if (usingDualQuaternion) {
dq_acc = dq_acc + dqs[index] * weight;
}
else {
matrix += matrixs[index] * weight;
}
w += weight;
}
if (w > 0) {
if (usingDualQuaternion) {
dq_acc = normalized(dq_acc);
nrms[i] = transformVector(dq_acc, nrms[i]);
}
else {
matrix = matrix / w;
nrms[i] = transform_nrm(matrix, nrms[i]);
}
nrms[i] = zeno::normalize(nrms[i]);
}
matrix = matrix / w;
auto nrm = transform_nrm(matrix, nrms[i]);
nrms[i] = zeno::normalize(nrm);
}
}
if (prim->loops.attr_is<vec3f>(vector)) {
Expand All @@ -1465,17 +1502,33 @@ struct NewFBXBoneDeform : INode {
for (auto i = 0; i < prim->loops.size(); i++) {
auto vi = prim->loops[i];
glm::mat4 matrix(0);
DualQuaternion dq_acc({0, 0, 0, 0}, {0, 0, 0, 0});
float w = 0;
for (auto j = 0; j < maxnum_boneWeight; j++) {
if (bi[j]->operator[](vi) < 0) {
auto index = bi[j]->operator[](vi);
if (index < 0) {
continue;
}
matrix += matrixs[bi[j]->operator[](vi)] * bw[j]->operator[](vi);
w += bw[j]->operator[](vi);
auto weight = bw[j]->operator[](vi);
if (usingDualQuaternion) {
dq_acc = dq_acc + dqs[index] * weight;
}
else {
matrix += matrixs[index] * weight;
}
w += weight;
}
if (w > 0) {
if (usingDualQuaternion) {
dq_acc = normalized(dq_acc);
nrms[i] = transformVector(dq_acc, nrms[i]);
}
else {
matrix = matrix / w;
nrms[i] = transform_nrm(matrix, nrms[i]);
}
nrms[i] = zeno::normalize(nrms[i]);
}
matrix = matrix / w;
auto nrm = transform_nrm(matrix, nrms[i]);
nrms[i] = zeno::normalize(nrm);
}
}
}
Expand All @@ -1490,6 +1543,7 @@ ZENDEFNODE(NewFBXBoneDeform, {
"GeometryToDeform",
"RestPointTransforms",
"DeformPointTransforms",
{"enum Linear DualQuaternion", "SkinningMethod", "Linear"},
{"string", "vectors", "nrm,"},
},
{
Expand Down
Loading