Skip to content

Commit

Permalink
Merge branch 'rendermesh-refactor' of github.com:syoyo/tinyusdz into …
Browse files Browse the repository at this point in the history
…tydra-skelanimation
  • Loading branch information
syoyo committed Apr 20, 2024
2 parents 18cd97e + f5009f7 commit 616302d
Show file tree
Hide file tree
Showing 3 changed files with 356 additions and 84 deletions.
156 changes: 123 additions & 33 deletions src/tydra/render-data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5127,15 +5127,6 @@ bool RenderSceneConverter::ConvertSkelAnimation(const RenderSceneConverterEnv &e
// An animation source is only valid if its translation, rotation, and scale components are all authored, storing arrays size to the same size as the authored joints array.
// """

// NOTE: fortunately USD SkelAnimation uses quaternions for rotations
// anim_out->channels.rotations

(void)anim_out;

AnimationChannel channel_txs; channel_txs.type = AnimationChannel::ChannelType::Translation;
AnimationChannel channel_rots; channel_rots.type = AnimationChannel::ChannelType::Rotation;
AnimationChannel channel_scales; channel_scales.type = AnimationChannel::ChannelType::Scale;

if (!skelAnim.joints.authored()) {
PUSH_ERROR_AND_RETURN(fmt::format("`joints` is not authored for SkelAnimation Prim : {}", abs_path));
}
Expand All @@ -5155,6 +5146,18 @@ bool RenderSceneConverter::ConvertSkelAnimation(const RenderSceneConverterEnv &e
skelAnim.scales.authored() ? "yes" : "no"));
}

//
// Reorder values[channels][timeCode][jointId] into values[jointId][channels][timeCode]
//

std::map<std::string, std::map<AnimationChannel::ChannelType, AnimationChannel>> channelMap;
StringAndIdMap jointIdMap;

for (const auto &joint : joints) {
uint64_t id = jointIdMap.size();
jointIdMap.add(joint.str(), id);
}


Animatable<std::vector<value::float3>> translations;
if (!skelAnim.translations.get_value(&translations)) {
Expand All @@ -5171,6 +5174,9 @@ bool RenderSceneConverter::ConvertSkelAnimation(const RenderSceneConverterEnv &e
PUSH_ERROR_AND_RETURN(fmt::format("Failed to get `scales` attribute of SkelAnimation. Maybe ValueBlock or connection? : {}", abs_path));
}

//
// NOTE: both timeSamples and default value are authored, timeSamples wins.
//
bool is_translations_timesamples = false;
bool is_rotations_timesamples = false;
bool is_scales_timesamples = false;
Expand All @@ -5183,16 +5189,25 @@ bool RenderSceneConverter::ConvertSkelAnimation(const RenderSceneConverterEnv &e
}

for (const auto &sample : ts_txs.get_samples()) {
AnimationSample<std::vector<value::float3>> dst;
if (!sample.blocked) {
// length check
if (sample.value.size() != joints.size()) {
PUSH_ERROR_AND_RETURN(fmt::format("Array length mismatch in SkelAnimation. timeCode {} translations.size {} must be equal to joints.size {} : {}", sample.t, sample.value.size(), joints.size(), abs_path));
}

dst.t = float(sample.t);
dst.value = sample.value;
channel_txs.translations.samples.push_back(dst);
for (size_t j = 0; j < sample.value.size(); j++) {
AnimationSample<value::float3> s;
s.t = float(sample.t);
s.value = sample.value[j];

std::string jointName = jointIdMap.at(j);
auto &it = channelMap.at(jointName).at(AnimationChannel::ChannelType::Translation);
if (it.translations.samples.empty()) {
it.type = AnimationChannel::ChannelType::Translation;
}
it.translations.samples.push_back(s);
}

}
}
is_translations_timesamples = true;
Expand All @@ -5216,6 +5231,7 @@ bool RenderSceneConverter::ConvertSkelAnimation(const RenderSceneConverterEnv &e
}
}

// value at 'default' time.
std::vector<value::float3> translation;
std::vector<value::float4> rotation;
std::vector<value::float3> scale;
Expand Down Expand Up @@ -5270,33 +5286,54 @@ bool RenderSceneConverter::ConvertSkelAnimation(const RenderSceneConverterEnv &e
is_scales_timesamples = false;
}

// Use USD TimeCode::Default for static sample.
if (is_translations_timesamples) {
} else {
AnimationSample<std::vector<value::float3>> sample;
sample.t = std::numeric_limits<float>::quiet_NaN();
sample.value = translation;
channel_txs.translations.samples.push_back(sample);
if (!is_translations_timesamples) {
// Create a channel value with single-entry
// Use USD TimeCode::Default for static sample.
for (const auto &joint : joints) {
channelMap[joint.str()][AnimationChannel::ChannelType::Translation].type = AnimationChannel::ChannelType::Translation;

AnimationSample<value::float3> s;
s.t = std::numeric_limits<float>::quiet_NaN();
uint64_t joint_id = jointIdMap.at(joint.str());
s.value = translation[joint_id];
channelMap[joint.str()][AnimationChannel::ChannelType::Translation].translations.samples.clear();
channelMap[joint.str()][AnimationChannel::ChannelType::Translation].translations.samples.push_back(s);
}
}

if (is_rotations_timesamples) {
} else {
AnimationSample<std::vector<value::float4>> sample;
sample.t = std::numeric_limits<float>::quiet_NaN();
sample.value = rotation;
channel_rots.rotations.samples.push_back(sample);
if (!is_rotations_timesamples) {
for (const auto &joint : joints) {
channelMap[joint.str()][AnimationChannel::ChannelType::Rotation].type = AnimationChannel::ChannelType::Rotation;

AnimationSample<value::float4> s;
s.t = std::numeric_limits<float>::quiet_NaN();
uint64_t joint_id = jointIdMap.at(joint.str());
s.value = rotation[joint_id];
channelMap[joint.str()][AnimationChannel::ChannelType::Rotation].rotations.samples.clear();
channelMap[joint.str()][AnimationChannel::ChannelType::Rotation].rotations.samples.push_back(s);
}
}

if (is_scales_timesamples) {
} else {
AnimationSample<std::vector<value::float3>> sample;
sample.t = std::numeric_limits<float>::quiet_NaN();
sample.value = scale;
channel_scales.scales.samples.push_back(sample);
if (!is_scales_timesamples) {
for (const auto &joint : joints) {
channelMap[joint.str()][AnimationChannel::ChannelType::Scale].type = AnimationChannel::ChannelType::Scale;

AnimationSample<value::float3> s;
s.t = std::numeric_limits<float>::quiet_NaN();
uint64_t joint_id = jointIdMap.at(joint.str());
s.value = scale[joint_id];
channelMap[joint.str()][AnimationChannel::ChannelType::Scale].scales.samples.clear();
channelMap[joint.str()][AnimationChannel::ChannelType::Scale].scales.samples.push_back(s);
}
}

PUSH_ERROR_AND_RETURN("TODO");
anim_out->abs_path = abs_path.full_path_name();
anim_out->prim_name = skelAnim.name;
anim_out->display_name = skelAnim.metas().displayName.value_or("");

anim_out->channels_map = std::move(channelMap);

return true;
}

bool RenderSceneConverter::BuildNodeHierarchyImpl(
Expand Down Expand Up @@ -5489,6 +5526,59 @@ bool RenderSceneConverter::ConvertToRenderScene(
return true;
}

bool RenderSceneConverter::ConvertSkeletonImpl(const RenderSceneConverterEnv &env, const std::string &abs_path, const tinyusdz::GeomMesh &mesh,
SkelHierarchy *&out_skel) {

if (!out_skel) {
return false;
}

if (!mesh.skeleton.has_value()) {
return false;
}

Path skelPath;

if (mesh.skeleton.value().is_path()) {
skelPath = mesh.skeleton.value().targetPath;
} else if (mesh.skeleton.value().is_pathvector()) {
// Use the first one
if (mesh.skeleton.value().targetPathVector.size()) {
skelPath = mesh.skeleton.value().targetPathVector[0];
} else {
PUSH_WARN("`skel:skeleton` has invalid definition.");
}
} else {
PUSH_WARN("`skel:skeleton` has invalid definition.");
}

if (skelPath.is_valid()) {
const Prim *skelPrim{nullptr};
if (!env.stage.find_prim_at_path(skelPath, skelPrim, &_err)) {
return false;
}

SkelHierarchy dst;
if (const auto pskel = skelPrim->as<Skeleton>()) {
SkelNode root;
if (!BuildSkelHierarchy((*pskel), root, &_err)) {
return false;
}
dst.abs_path = abs_path;
dst.prim_name = skelPrim->element_name();
dst.display_name = pskel->metas().displayName.value_or("");
dst.root_node = root;
} else {
PUSH_ERROR_AND_RETURN("Prim is not Skeleton.");
}

(*out_skel) = dst;
return true;
}

PUSH_ERROR_AND_RETURN("`skel:skeleton` path is invalid.");
}

bool DefaultTextureImageLoaderFunction(
const value::AssetPath &assetPath, const AssetInfo &assetInfo,
const AssetResolutionResolver &assetResolver, TextureImage *texImageOut,
Expand Down
23 changes: 14 additions & 9 deletions src/tydra/render-data.hh
Original file line number Diff line number Diff line change
Expand Up @@ -665,19 +665,24 @@ struct AnimationSampler {
struct AnimationChannel {
enum class ChannelType { Transform, Translation, Rotation, Scale, Weight };

AnimationChannel() = default;

AnimationChannel(ChannelType ty) : type(ty) {
}

ChannelType type;
// The following AnimationSampler is filled depending on ChannelType.
// Example: Rotation => Only `rotations` are filled.

// Matrix precision is reduced to float-precision
// NOTE: transform is not supported in glTF(you need to decompose transform
// matrix into TRS)
AnimationSampler<std::vector<mat4>> transforms;
AnimationSampler<mat4> transforms;

AnimationSampler<std::vector<vec3>> translations;
AnimationSampler<std::vector<quat>> rotations; // Rotation is represented as quaternions
AnimationSampler<std::vector<vec3>> scales; // half-types are upcasted to float precision
AnimationSampler<std::vector<float>> weights;
AnimationSampler<vec3> translations;
AnimationSampler<quat> rotations; // Rotation is represented as quaternions
AnimationSampler<vec3> scales; // half-types are upcasted to float precision
AnimationSampler<float> weights;

//std::string joint_name; // joint name(UsdSkel::joints)
//int64_t joint_id{-1}; // joint index in SkelHierarchy
Expand All @@ -689,7 +694,7 @@ struct Animation {
std::string abs_path; // Target USD Prim path
std::string display_name; // `displayName` prim meta

// key = joint, value = (key: channel_type, value= channel value))
// key = joint, value = (key: channel_type, value: channel_value)
std::map<std::string, std::map<AnimationChannel::ChannelType, AnimationChannel>> channels_map;
};

Expand Down Expand Up @@ -1769,10 +1774,10 @@ class RenderSceneConverter {
bool BuildVertexIndicesImpl(RenderMesh &mesh);

//
// Get Skeleton assigned to this GeomMesh Prim
// Get Skeleton assigned to the GeomMesh Prim and convert it to SkelHierarchy.
//
bool GetSkeletonImpl(const tinyusdz::Prim &prim,
const tinyusdz::Skeleton *&out_skeleton);
bool ConvertSkeletonImpl(const RenderSceneConverterEnv &env, const std::string &mesh_abs_path, const tinyusdz::GeomMesh &mesh,
SkelHierarchy *&out_skel);

bool BuildNodeHierarchyImpl(
const RenderSceneConverterEnv &env,
Expand Down
Loading

0 comments on commit 616302d

Please sign in to comment.