USD Skinned Mesh (#5812)

USD: Skinned Mesh support
This commit is contained in:
Gene Walters
2024-10-31 13:48:28 -07:00
committed by GitHub
parent ec563823b6
commit 7634cf84ca
7 changed files with 267 additions and 69 deletions

View File

@@ -212,15 +212,14 @@ void USDImporterImplTinyusdz::InternReadFile(
return;
}
// sanityCheckNodesRecursive(pScene->mRootNode);
// sanityCheckNodesRecursive(pScene->mRootNode);
meshes(render_scene, pScene, nameWExt);
materials(render_scene, pScene, nameWExt);
textures(render_scene, pScene, nameWExt);
textureImages(render_scene, pScene, nameWExt);
buffers(render_scene, pScene, nameWExt);
std::map<size_t, tinyusdz::tydra::Node> meshNodes;
setupNodes(render_scene, pScene, meshNodes, nameWExt);
setupNodes(render_scene, pScene, nameWExt);
setupBlendShapes(render_scene, pScene, nameWExt);
}
@@ -269,8 +268,68 @@ void USDImporterImplTinyusdz::verticesForMesh(
size_t meshIdx,
const std::string &nameWExt) {
UNUSED(nameWExt);
pScene->mMeshes[meshIdx]->mNumVertices = static_cast<unsigned int>(render_scene.meshes[meshIdx].points.size());
const auto numVertices = static_cast<unsigned int>(render_scene.meshes[meshIdx].points.size());
pScene->mMeshes[meshIdx]->mNumVertices = numVertices;
pScene->mMeshes[meshIdx]->mVertices = new aiVector3D[pScene->mMeshes[meshIdx]->mNumVertices];
// Check if this is a skinned mesh
if (int skeleton_id = render_scene.meshes[meshIdx].skel_id; skeleton_id > -1) {
// Recursively iterate to collect all the joints in the hierarchy into a flattened array
std::vector<const tinyusdz::tydra::SkelNode *> skeletonNodes;
skeletonNodes.push_back(&render_scene.skeletons[skeleton_id].root_node);
for (int i = 0; i < skeletonNodes.size(); ++i) {
const tinyusdz::tydra::SkelNode *skeletonNode = skeletonNodes[i];
for (const auto &child : skeletonNodes[i]->children) {
skeletonNodes.push_back(&child);
}
}
// Convert USD skeleton joints to Assimp bones
const unsigned int numBones = skeletonNodes.size();
pScene->mMeshes[meshIdx]->mNumBones = numBones;
pScene->mMeshes[meshIdx]->mBones = new aiBone *[numBones];
for (unsigned int i = 0; i < numBones; ++i) {
const tinyusdz::tydra::SkelNode *skeletonNode = skeletonNodes[i];
const int boneIndex = skeletonNode->joint_id;
// Sorted so that Assimp bone ids align with USD joint id
auto outputBone = new aiBone();
outputBone->mName = aiString(skeletonNode->joint_name);
outputBone->mOffsetMatrix = tinyUsdzMat4ToAiMat4(skeletonNode->bind_transform.m).Inverse();
pScene->mMeshes[meshIdx]->mBones[boneIndex] = outputBone;
}
// Vertex weights
std::vector<std::vector<aiVertexWeight>> aiBonesVertexWeights;
aiBonesVertexWeights.resize(numBones);
const std::vector<int> &jointIndices = render_scene.meshes[meshIdx].joint_and_weights.jointIndices;
const std::vector<float> &jointWeightIndices = render_scene.meshes[meshIdx].joint_and_weights.jointWeights;
const int numWeightsPerVertex = render_scene.meshes[meshIdx].joint_and_weights.elementSize;
for (unsigned int vertexIndex = 0; vertexIndex < numVertices; ++vertexIndex) {
for (int weightIndex = 0; weightIndex < numWeightsPerVertex; ++weightIndex) {
const unsigned int index = vertexIndex * numWeightsPerVertex + weightIndex;
const float jointWeight = jointWeightIndices[index];
if (jointWeight > 0) {
const int jointIndex = jointIndices[index];
aiBonesVertexWeights[jointIndex].emplace_back(vertexIndex, jointWeight);
}
}
}
for (int boneIndex = 0; boneIndex < numBones; ++boneIndex) {
const unsigned int numWeightsForBone = aiBonesVertexWeights[boneIndex].size();
pScene->mMeshes[meshIdx]->mBones[boneIndex]->mWeights = new aiVertexWeight[numWeightsForBone];
pScene->mMeshes[meshIdx]->mBones[boneIndex]->mNumWeights = numWeightsForBone;
std::swap_ranges(aiBonesVertexWeights[boneIndex].begin(), aiBonesVertexWeights[boneIndex].end(), pScene->mMeshes[meshIdx]->mBones[boneIndex]->mWeights);
}
} // Skinned mesh end
for (size_t j = 0; j < pScene->mMeshes[meshIdx]->mNumVertices; ++j) {
pScene->mMeshes[meshIdx]->mVertices[j].x = render_scene.meshes[meshIdx].points[j][0];
pScene->mMeshes[meshIdx]->mVertices[j].y = render_scene.meshes[meshIdx].points[j][1];
@@ -620,29 +679,29 @@ void USDImporterImplTinyusdz::buffers(
void USDImporterImplTinyusdz::setupNodes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt) {
stringstream ss;
pScene->mRootNode = nodes(render_scene, meshNodes, nameWExt);
pScene->mRootNode = nodes(render_scene, nameWExt);
if (pScene->mRootNode == nullptr) {
return;
}
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
pScene->mRootNode->mMeshes = new unsigned int[pScene->mRootNode->mNumMeshes];
ss.str("");
ss << "setupNodes(): pScene->mNumMeshes: " << pScene->mNumMeshes;
if (pScene->mRootNode != nullptr) {
ss << ", mRootNode->mNumMeshes: " << pScene->mRootNode->mNumMeshes;
}
ss << ", mRootNode->mNumMeshes: " << pScene->mRootNode->mNumMeshes;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
for (unsigned int meshIdx = 0; meshIdx < pScene->mNumMeshes; meshIdx++) {
pScene->mRootNode->mMeshes[meshIdx] = meshIdx;
}
}
aiNode *USDImporterImplTinyusdz::nodes(
const tinyusdz::tydra::RenderScene &render_scene,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt) {
const size_t numNodes{render_scene.nodes.size()};
(void) numNodes; // Ignore unused variable when -Werror enabled
@@ -650,7 +709,9 @@ aiNode *USDImporterImplTinyusdz::nodes(
ss.str("");
ss << "nodes(): model" << nameWExt << ", numNodes: " << numNodes;
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
return nodesRecursive(nullptr, render_scene.nodes[0], meshNodes);
aiNode *rootNode = nodesRecursive(nullptr, render_scene.nodes[0], render_scene.skeletons);
return rootNode;
}
using Assimp::tinyusdzNodeTypeFor;
@@ -659,7 +720,7 @@ using tinyusdz::tydra::NodeType;
aiNode *USDImporterImplTinyusdz::nodesRecursive(
aiNode *pNodeParent,
const tinyusdz::tydra::Node &node,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes) {
const std::vector<tinyusdz::tydra::SkelHierarchy> &skeletons) {
stringstream ss;
aiNode *cNode = new aiNode();
cNode->mParent = pNodeParent;
@@ -673,21 +734,69 @@ aiNode *USDImporterImplTinyusdz::nodesRecursive(
ss << " (parent " << cNode->mParent->mName.C_Str() << ")";
}
ss << " has " << node.children.size() << " children";
if (node.id > -1) {
if (node.id != -1) {
ss << "\n node mesh id: " << node.id << " (node type: " << tinyusdzNodeTypeFor(node.nodeType) << ")";
meshNodes[node.id] = node;
}
TINYUSDZLOGD(TAG, "%s", ss.str().c_str());
if (!node.children.empty()) {
cNode->mNumChildren = static_cast<unsigned int>(node.children.size());
cNode->mChildren = new aiNode *[cNode->mNumChildren];
unsigned int numChildren = node.children.size();
// Find any tinyusdz skeletons which might begin at this node
// Add the skeleton bones as child nodes
const tinyusdz::tydra::SkelNode *skelNode = nullptr;
for (const auto &skeleton : skeletons) {
if (skeleton.abs_path == node.abs_path) {
// Add this skeleton's bones as child nodes
++numChildren;
skelNode = &skeleton.root_node;
break;
}
}
size_t i{0};
for (const auto &childNode: node.children) {
cNode->mChildren[i] = nodesRecursive(cNode, childNode, meshNodes);
cNode->mNumChildren = numChildren;
// Done. No more children.
if (numChildren == 0) {
return cNode;
}
cNode->mChildren = new aiNode *[cNode->mNumChildren];
size_t i{ 0 };
for (const auto &childNode : node.children) {
cNode->mChildren[i] = nodesRecursive(cNode, childNode, skeletons);
++i;
}
if (skelNode != nullptr) {
// Convert USD skeleton into an Assimp node and make it the last child
cNode->mChildren[cNode->mNumChildren-1] = skeletonNodesRecursive(cNode, *skelNode);
}
return cNode;
}
aiNode *USDImporterImplTinyusdz::skeletonNodesRecursive(
aiNode* pNodeParent,
const tinyusdz::tydra::SkelNode& joint) {
auto *cNode = new aiNode(joint.joint_path);
cNode->mParent = pNodeParent;
cNode->mNumMeshes = 0; // not a mesh node
cNode->mTransformation = tinyUsdzMat4ToAiMat4(joint.rest_transform.m);
// Done. No more children.
if (joint.children.empty()) {
return cNode;
}
cNode->mNumChildren = static_cast<unsigned int>(joint.children.size());
cNode->mChildren = new aiNode *[cNode->mNumChildren];
for (int i = 0; i < cNode->mNumChildren; ++i) {
const tinyusdz::tydra::SkelNode &childJoint = joint.children[i];
cNode->mChildren[i] = skeletonNodesRecursive(cNode, childJoint);
}
return cNode;
}

View File

@@ -123,19 +123,20 @@ public:
void setupNodes(
const tinyusdz::tydra::RenderScene &render_scene,
aiScene *pScene,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt
);
const std::string &nameWExt);
aiNode *nodes(
const tinyusdz::tydra::RenderScene &render_scene,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes,
const std::string &nameWExt);
aiNode *nodesRecursive(
aiNode *pNodeParent,
const tinyusdz::tydra::Node &node,
std::map<size_t, tinyusdz::tydra::Node> &meshNodes);
const std::vector<tinyusdz::tydra::SkelHierarchy> &skeletons);
aiNode *skeletonNodesRecursive(
aiNode *pNodeParent,
const tinyusdz::tydra::SkelNode &joint);
void sanityCheckNodesRecursive(
aiNode *pNode);

View File

@@ -103,37 +103,21 @@ std::string Assimp::tinyusdzNodeTypeFor(NodeType type) {
aiMatrix4x4 Assimp::tinyUsdzMat4ToAiMat4(const double matIn[4][4]) {
aiMatrix4x4 matOut;
matOut.a1 = matIn[0][0];
matOut.a2 = matIn[0][1];
matOut.a3 = matIn[0][2];
matOut.a4 = matIn[0][3];
matOut.b1 = matIn[1][0];
matOut.a2 = matIn[1][0];
matOut.a3 = matIn[2][0];
matOut.a4 = matIn[3][0];
matOut.b1 = matIn[0][1];
matOut.b2 = matIn[1][1];
matOut.b3 = matIn[1][2];
matOut.b4 = matIn[1][3];
matOut.c1 = matIn[2][0];
matOut.c2 = matIn[2][1];
matOut.b3 = matIn[2][1];
matOut.b4 = matIn[3][1];
matOut.c1 = matIn[0][2];
matOut.c2 = matIn[1][2];
matOut.c3 = matIn[2][2];
matOut.c4 = matIn[2][3];
matOut.d1 = matIn[3][0];
matOut.d2 = matIn[3][1];
matOut.d3 = matIn[3][2];
matOut.c4 = matIn[3][2];
matOut.d1 = matIn[0][3];
matOut.d2 = matIn[1][3];
matOut.d3 = matIn[2][3];
matOut.d4 = matIn[3][3];
// matOut.a1 = matIn[0][0];
// matOut.a2 = matIn[1][0];
// matOut.a3 = matIn[2][0];
// matOut.a4 = matIn[3][0];
// matOut.b1 = matIn[0][1];
// matOut.b2 = matIn[1][1];
// matOut.b3 = matIn[2][1];
// matOut.b4 = matIn[3][1];
// matOut.c1 = matIn[0][2];
// matOut.c2 = matIn[1][2];
// matOut.c3 = matIn[2][2];
// matOut.c4 = matIn[3][2];
// matOut.d1 = matIn[0][3];
// matOut.d2 = matIn[1][3];
// matOut.d3 = matIn[2][3];
// matOut.d4 = matIn[3][3];
return matOut;
}

View File

@@ -149,7 +149,6 @@ SET( IMPORTERS
#unit/utM3DImportExport.cpp
unit/utMDCImportExport.cpp
unit/utAssbinImportExport.cpp
unit/utUSDImport.cpp
unit/ImportExport/utAssjsonImportExport.cpp
unit/ImportExport/utCOBImportExport.cpp
unit/ImportExport/utOgreImportExport.cpp
@@ -170,6 +169,12 @@ SET( IMPORTERS
unit/ImportExport/Pbrt/utPbrtImportExport.cpp
)
if(ASSIMP_BUILD_USD_IMPORTER)
list( APPEND IMPORTERS
unit/utUSDImport.cpp
)
endif()
SET( MATERIAL
unit/utMaterialSystem.cpp
)

View File

@@ -1,3 +1,4 @@
[blendshape.usda](blendshape.usda) copied from tinyusdz/models (No attribution/license cited in that project)
[texturedcube.usda](texturedcube.usda) copied from tinyusdz/models (No attribution/license cited in that project)
[translated-cube.usda](translated-cube.usda) copied from tinyusdz/models (No attribution/license cited in that project)
[simple-skin-test.usda](simple-skin-test.usda) copied from tinyusdz/models (No attribution/license cited in that project)

View File

@@ -0,0 +1,85 @@
#usda 1.0
(
defaultPrim = "root"
doc = "Blender v4.1.0"
metersPerUnit = 1
upAxis = "Z"
)
def Xform "root" (
customData = {
dictionary Blender = {
bool generated = 1
}
}
)
{
def SkelRoot "Armature"
{
matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, -4.371138828673793e-8, -1, 0), (0, 1, -4.371138828673793e-8, 0), (0, -1.7017418146133423, 0, 1) )
uniform token[] xformOpOrder = ["xformOp:transform"]
def Skeleton "Armature" (
prepend apiSchemas = ["SkelBindingAPI"]
)
{
uniform matrix4d[] bindTransforms = [( (1, 0, 0, 0), (0, 0, 1, 0), (0, -1, 0, 0), (0, 0, 0, 1) ), ( (1, 0, 0, 0), (0, 0, 1, 0), (0, -1, 0, 0), (0, 0, 1, 1) )]
uniform token[] joints = ["Bone", "Bone/Bone_001"]
uniform matrix4d[] restTransforms = [( (1, 0, 0, 0), (0, 0, 1, 0), (0, -1, 0, 0), (0, 0, 0, 1) ), ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 1, 0, 1) )]
}
def Xform "Grid"
{
matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, -4.371138828673793e-8, 1, 0), (0, -1, -4.371138828673793e-8, 0), (0, -7.438549687321938e-8, 1.7017418146133423, 1) )
uniform token[] xformOpOrder = ["xformOp:transform"]
def Mesh "Grid" (
prepend apiSchemas = ["SkelBindingAPI"]
)
{
float3[] extent = [(-1, -1, 0), (1, 1, 0)]
int[] faceVertexCounts = [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
int[] faceVertexIndices = [0, 1, 6, 5, 1, 2, 7, 6, 2, 3, 8, 7, 3, 4, 9, 8, 5, 6, 11, 10, 6, 7, 12, 11, 7, 8, 13, 12, 8, 9, 14, 13, 10, 11, 16, 15, 11, 12, 17, 16, 12, 13, 18, 17, 13, 14, 19, 18, 15, 16, 21, 20, 16, 17, 22, 21, 17, 18, 23, 22, 18, 19, 24, 23]
normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] (
interpolation = "faceVarying"
)
point3f[] points = [(-1, -1, 0), (-0.5, -1, 0), (0, -1, 0), (0.5, -1, 0), (1, -1, 0), (-1, -0.5, 0), (-0.5, -0.5, 0), (0, -0.5, 0), (0.5, -0.5, 0), (1, -0.5, 0), (-1, 0, 0), (-0.5, 0, 0), (0, 0, 0), (0.5, 0, 0), (1, 0, 0), (-1, 0.5, 0), (-0.5, 0.5, 0), (0, 0.5, 0), (0.5, 0.5, 0), (1, 0.5, 0), (-1, 1, 0), (-0.5, 1, 0), (0, 1, 0), (0.5, 1, 0), (1, 1, 0)]
bool[] primvars:sharp_face = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] (
interpolation = "uniform"
)
matrix4d primvars:skel:geomBindTransform = ( (1, 0, 0, 0), (0, -4.371138828673793e-8, 1, 0), (0, -1, -4.371138828673793e-8, 0), (0, -7.438549687321938e-8, 1.7017418146133423, 1) )
int[] primvars:skel:jointIndices = [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1] (
elementSize = 2
interpolation = "vertex"
)
float[] primvars:skel:jointWeights = [0.43767902, 0.56232095, 0.605441, 0.39455906, 1, 0, 0.605441, 0.39455906, 0.43767902, 0.562321, 0.23470172, 0.7652983, 0.18096954, 0.81903046, 1, 0, 0.18096954, 0.81903046, 0.23470171, 0.7652983, 0.114853, 0.88514704, 0.06506716, 0.9349329, 1, 0, 0.06506715, 0.9349329, 0.11485299, 0.88514704, 0.059175473, 0.94082457, 0.009479873, 0.9905201, 1, 0, 0.009479865, 0.9905201, 0.059175465, 0.94082457, 0.031181445, 0.96881855, 1, 0, 1, 0, 1, 0, 0.031181442, 0.9688186] (
elementSize = 2
interpolation = "vertex"
)
texCoord2f[] primvars:UVMap = [(0, 0), (0.25, 0), (0.25, 0.25), (0, 0.25), (0.25, 0), (0.5, 0), (0.5, 0.25), (0.25, 0.25), (0.5, 0), (0.75, 0), (0.75, 0.25), (0.5, 0.25), (0.75, 0), (1, 0), (1, 0.25), (0.75, 0.25), (0, 0.25), (0.25, 0.25), (0.25, 0.5), (0, 0.5), (0.25, 0.25), (0.5, 0.25), (0.5, 0.5), (0.25, 0.5), (0.5, 0.25), (0.75, 0.25), (0.75, 0.5), (0.5, 0.5), (0.75, 0.25), (1, 0.25), (1, 0.5), (0.75, 0.5), (0, 0.5), (0.25, 0.5), (0.25, 0.75), (0, 0.75), (0.25, 0.5), (0.5, 0.5), (0.5, 0.75), (0.25, 0.75), (0.5, 0.5), (0.75, 0.5), (0.75, 0.75), (0.5, 0.75), (0.75, 0.5), (1, 0.5), (1, 0.75), (0.75, 0.75), (0, 0.75), (0.25, 0.75), (0.25, 1), (0, 1), (0.25, 0.75), (0.5, 0.75), (0.5, 1), (0.25, 1), (0.5, 0.75), (0.75, 0.75), (0.75, 1), (0.5, 1), (0.75, 0.75), (1, 0.75), (1, 1), (0.75, 1)] (
interpolation = "faceVarying"
)
rel skel:skeleton = </root/Armature/Armature>
uniform token subdivisionScheme = "none"
}
}
}
def Xform "Camera"
{
matrix4d xformOp:transform = ( (0.6859206557273865, 0.7276763319969177, 0, 0), (-0.32401347160339355, 0.305420845746994, 0.8953956365585327, 0), (0.6515582203865051, -0.6141703724861145, 0.44527140259742737, 0), (7.358891487121582, -6.925790786743164, 4.958309173583984, 1) )
uniform token[] xformOpOrder = ["xformOp:transform"]
def Camera "Camera"
{
float2 clippingRange = (0.1, 100)
float focalLength = 0.5
float horizontalAperture = 0.36
float horizontalApertureOffset = 0
token projection = "perspective"
float verticalAperture = 0.2025
float verticalApertureOffset = 0
}
}
}

View File

@@ -49,18 +49,31 @@ Copyright (c) 2006-2024, assimp team
using namespace ::Assimp;
class utUSDImport : public AbstractImportExportBase {
public:
virtual bool importerTest() {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/USD/suzanne.usdc", aiProcess_ValidateDataStructure);
EXPECT_EQ(1u, scene->mNumMeshes);
EXPECT_NE(nullptr, scene->mMeshes[0]);
if (nullptr == scene->mMeshes[0]) {
return false;
}
EXPECT_EQ(507u, scene->mMeshes[0]->mNumVertices);
EXPECT_EQ(968u, scene->mMeshes[0]->mNumFaces);
return (nullptr != scene);
}
};
TEST_F(utUSDImport, meshTest) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/USD/usdc/suzanne.usdc", aiProcess_ValidateDataStructure);
EXPECT_NE(nullptr, scene);
EXPECT_EQ(1u, scene->mNumMeshes);
EXPECT_NE(nullptr, scene->mMeshes[0]);
EXPECT_EQ(1968u, scene->mMeshes[0]->mNumVertices); // Note: suzanne is authored with only 507 vertices, but TinyUSDZ rebuilds the vertex array. see https://github.com/lighttransport/tinyusdz/blob/36f2aabb256b360365989c01a52f839a57dfe2a6/src/tydra/render-data.cc#L2673-L2690
EXPECT_EQ(968u, scene->mMeshes[0]->mNumFaces);
}
TEST_F(utUSDImport, skinnedMeshTest) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/../models-nonbsd/USD/usda/simple-skin-test.usda", aiProcess_ValidateDataStructure);
EXPECT_NE(nullptr, scene);
EXPECT_TRUE(scene->HasMeshes());
const aiMesh *mesh = scene->mMeshes[0];
EXPECT_EQ(2, mesh->mNumBones);
// Check bone names and make sure scene has nodes of the same name
EXPECT_EQ(mesh->mBones[0]->mName, aiString("Bone"));
EXPECT_EQ(mesh->mBones[1]->mName, aiString("Bone/Bone_001"));
EXPECT_NE(nullptr, scene->mRootNode->FindNode("Bone"));
EXPECT_NE(nullptr, scene->mRootNode->FindNode("Bone/Bone_001"));
}