USD import blendshapes.
Importing USD blendshapes as shapekeys and creating animation curves for animated blendshape weights.
This commit is contained in:
parent
b665ae266d
commit
833df7ebc1
|
@ -963,6 +963,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
|
|||
const bool import_lights = RNA_boolean_get(op->ptr, "import_lights");
|
||||
const bool import_materials = RNA_boolean_get(op->ptr, "import_materials");
|
||||
const bool import_meshes = RNA_boolean_get(op->ptr, "import_meshes");
|
||||
const bool import_blendshapes = RNA_boolean_get(op->ptr, "import_blendshapes");
|
||||
const bool import_volumes = RNA_boolean_get(op->ptr, "import_volumes");
|
||||
|
||||
const bool import_subdiv = RNA_boolean_get(op->ptr, "import_subdiv");
|
||||
|
@ -1028,6 +1029,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
|
|||
.import_lights = import_lights,
|
||||
.import_materials = import_materials,
|
||||
.import_meshes = import_meshes,
|
||||
.import_blendshapes = import_blendshapes,
|
||||
.import_volumes = import_volumes,
|
||||
.prim_path_mask = prim_path_mask,
|
||||
.import_subdiv = import_subdiv,
|
||||
|
@ -1068,6 +1070,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op)
|
|||
uiItemR(col, ptr, "import_lights", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "import_materials", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "import_meshes", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "import_blendshapes", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "import_volumes", 0, NULL, ICON_NONE);
|
||||
uiItemR(box, ptr, "prim_path_mask", 0, NULL, ICON_NONE);
|
||||
uiItemR(box, ptr, "scale", 0, NULL, ICON_NONE);
|
||||
|
@ -1163,6 +1166,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
|
|||
RNA_def_boolean(ot->srna, "import_lights", true, "Lights", "");
|
||||
RNA_def_boolean(ot->srna, "import_materials", true, "Materials", "");
|
||||
RNA_def_boolean(ot->srna, "import_meshes", true, "Meshes", "");
|
||||
RNA_def_boolean(ot->srna, "import_blendshapes", true, "Blend Shapes", "");
|
||||
RNA_def_boolean(ot->srna, "import_volumes", true, "Volumes", "");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
|
|
|
@ -69,6 +69,7 @@ set(SRC
|
|||
intern/usd_common.cc
|
||||
intern/usd_hierarchy_iterator.cc
|
||||
intern/usd_light_convert.cc
|
||||
intern/usd_skel_convert.cc
|
||||
|
||||
intern/usd_reader_camera.cc
|
||||
intern/usd_reader_curve.cc
|
||||
|
@ -107,6 +108,7 @@ set(SRC
|
|||
intern/usd_exporter_context.h
|
||||
intern/usd_hierarchy_iterator.h
|
||||
intern/usd_light_convert.h
|
||||
intern/usd_skel_convert.h
|
||||
|
||||
intern/usd_reader_camera.h
|
||||
intern/usd_reader_curve.h
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "usd_reader_mesh.h"
|
||||
#include "usd_reader_material.h"
|
||||
#include "usd_skel_convert.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_main.h"
|
||||
|
@ -272,6 +273,10 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
|
|||
}
|
||||
}
|
||||
|
||||
if (import_params_.import_blendshapes) {
|
||||
import_blendshapes(bmain, object_, prim_);
|
||||
}
|
||||
|
||||
USDXformReader::read_object_data(bmain, motionSampleTime);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2022 NVIDIA Corporation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
|
||||
#include "usd_skel_convert.h"
|
||||
|
||||
#include "usd.h"
|
||||
|
||||
#include <pxr/usd/usdSkel/animation.h>
|
||||
#include <pxr/usd/usdSkel/blendShape.h>
|
||||
#include <pxr/usd/usdSkel/bindingAPI.h>
|
||||
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_runtime.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "BLI_math_vector.h"
|
||||
|
||||
#include "DNA_key_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_meta_types.h"
|
||||
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_fcurve.h"
|
||||
#include "ED_keyframing.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace usdtokens {
|
||||
// Attribute names.
|
||||
//static const pxr::TfToken color("color", pxr::TfToken::Immortal);
|
||||
} // namespace usdtokens
|
||||
|
||||
namespace {
|
||||
|
||||
FCurve *create_fcurve(int array_index, const char *rna_path)
|
||||
{
|
||||
FCurve *fcu = BKE_fcurve_create();
|
||||
fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED);
|
||||
fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
|
||||
fcu->array_index = array_index;
|
||||
return fcu;
|
||||
}
|
||||
|
||||
void add_bezt(FCurve *fcu,
|
||||
float frame,
|
||||
float value,
|
||||
eBezTriple_Interpolation ipo = BEZT_IPO_LIN)
|
||||
{
|
||||
BezTriple bez;
|
||||
memset(&bez, 0, sizeof(BezTriple));
|
||||
bez.vec[1][0] = frame;
|
||||
bez.vec[1][1] = value;
|
||||
bez.ipo = ipo; /* use default interpolation mode here... */
|
||||
bez.f1 = bez.f2 = bez.f3 = SELECT;
|
||||
bez.h1 = bez.h2 = HD_AUTO;
|
||||
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
|
||||
BKE_fcurve_handles_recalc(fcu);
|
||||
}
|
||||
|
||||
} // End anonymous namespace.
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
void test_create_shapekeys(Main *bmain, Object *obj)
|
||||
{
|
||||
if (!(obj && obj->data && obj->type == OB_MESH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(obj->data);
|
||||
|
||||
/* insert key to source mesh */
|
||||
Key *key = BKE_key_add(bmain, (ID *)mesh);
|
||||
key->type = KEY_RELATIVE;
|
||||
|
||||
mesh->key = key;
|
||||
|
||||
/* insert basis key */
|
||||
KeyBlock *kb = BKE_keyblock_add(key, "Basis");
|
||||
BKE_keyblock_convert_from_mesh(mesh, key, kb);
|
||||
|
||||
kb = BKE_keyblock_add(key, "Key1");
|
||||
BKE_keyblock_convert_from_mesh(mesh, key, kb);
|
||||
|
||||
float offsets[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||
BKE_keyblock_update_from_offset(obj, kb, (float(*)[3])&offsets);
|
||||
|
||||
bAction *act = ED_id_action_ensure(bmain, (ID *)&key->id);
|
||||
|
||||
FCurve *fcu = create_fcurve(0, "key_blocks[\"Key1\"].value");
|
||||
fcu->totvert = 3;
|
||||
|
||||
add_bezt(fcu, 0.f, 0.f);
|
||||
add_bezt(fcu, 30.f, 1.f);
|
||||
add_bezt(fcu, 60.f, 0.3f);
|
||||
|
||||
BLI_addtail(&act->curves, fcu);
|
||||
}
|
||||
|
||||
void import_blendshapes(Main *bmain, Object *obj, pxr::UsdPrim prim)
|
||||
{
|
||||
if (!(obj && obj->data && obj->type == OB_MESH && prim)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdSkelBindingAPI skel_api = pxr::UsdSkelBindingAPI::Apply(prim);
|
||||
|
||||
if (!skel_api) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skel_api.GetBlendShapeTargetsRel().HasAuthoredTargets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::SdfPathVector targets;
|
||||
if (!skel_api.GetBlendShapeTargetsRel().GetTargets(&targets)) {
|
||||
std::cout << "Couldn't get blendshape targets for prim " << prim.GetPath() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (targets.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skel_api.GetBlendShapesAttr().HasAuthoredValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::VtTokenArray blendshapes;
|
||||
if (!skel_api.GetBlendShapesAttr().Get(&blendshapes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blendshapes.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (targets.size() != blendshapes.size()) {
|
||||
std::cout << "Number of blendshapes doesn't match number of blendshape targets for prim " << prim.GetPath() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh *mesh = static_cast<Mesh *>(obj->data);
|
||||
|
||||
/* insert key to source mesh */
|
||||
Key *key = BKE_key_add(bmain, (ID *)mesh);
|
||||
key->type = KEY_RELATIVE;
|
||||
|
||||
mesh->key = key;
|
||||
|
||||
/* insert basis key */
|
||||
KeyBlock *kb = BKE_keyblock_add(key, "Basis");
|
||||
BKE_keyblock_convert_from_mesh(mesh, key, kb);
|
||||
|
||||
pxr::UsdStageRefPtr stage = prim.GetStage();
|
||||
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Keep track of the shapkeys we're adding, for
|
||||
* validation when creating curves later. */
|
||||
std::set<pxr::TfToken> shapekey_names;
|
||||
|
||||
for (int i = 0; i < targets.size(); ++i) {
|
||||
|
||||
const pxr::SdfPath &path = targets[i];
|
||||
|
||||
pxr::UsdSkelBlendShape blendshape(stage->GetPrimAtPath(path));
|
||||
|
||||
if (!blendshape) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!blendshape.GetOffsetsAttr().HasAuthoredValue()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pxr::VtVec3fArray offsets;
|
||||
if (!blendshape.GetOffsetsAttr().Get(&offsets)) {
|
||||
std::cout << "Couldn't get offsets for blendshape " << path << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
shapekey_names.insert(blendshapes[i]);
|
||||
|
||||
kb = BKE_keyblock_add(key, blendshapes[i].GetString().c_str());
|
||||
BKE_keyblock_convert_from_mesh(mesh, key, kb);
|
||||
|
||||
pxr::VtArray<int> point_indices;
|
||||
if (blendshape.GetPointIndicesAttr().HasAuthoredValue()) {
|
||||
blendshape.GetPointIndicesAttr().Get(&point_indices);
|
||||
}
|
||||
|
||||
float *fp = static_cast<float *>(kb->data);
|
||||
|
||||
if (point_indices.empty()) {
|
||||
for (int a = 0; a < kb->totelem; ++a, fp += 3) {
|
||||
add_v3_v3(fp, offsets[a].data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int a = 0;
|
||||
for (int i : point_indices) {
|
||||
if (i < 0 || i > kb->totelem) {
|
||||
std::cout << "out of bounds point index " << i << std::endl;
|
||||
++a;
|
||||
continue;
|
||||
}
|
||||
add_v3_v3(&fp[3 * i], offsets[a].data());
|
||||
++a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skel_api.GetSkeletonRel().HasAuthoredTargets()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdRelationship skel_rel = skel_api.GetSkeletonRel();
|
||||
|
||||
if (!skel_api.GetSkeletonRel().GetTargets(&targets)) {
|
||||
std::cout << "Couldn't get skeleton targets for prim " << prim.GetPath() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (targets.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO(makowalski): do we need to account for multiple skeleton targets? */
|
||||
pxr::SdfPath skel_path = targets.front();
|
||||
|
||||
pxr::UsdSkelSkeleton skel_prim(stage->GetPrimAtPath(skel_path));
|
||||
|
||||
if (!skel_prim) {
|
||||
return;
|
||||
}
|
||||
|
||||
skel_api = pxr::UsdSkelBindingAPI::Apply(skel_prim.GetPrim());
|
||||
|
||||
if (!skel_api) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdPrim anim_prim;
|
||||
if (!skel_api.GetAnimationSource(&anim_prim)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdSkelAnimation skel_anim(anim_prim);
|
||||
|
||||
if (!skel_anim) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!skel_anim.GetBlendShapesAttr().HasAuthoredValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
pxr::UsdAttribute weights_attr = skel_anim.GetBlendShapeWeightsAttr();
|
||||
|
||||
if (!(weights_attr && weights_attr.HasAuthoredValue())) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<double> times;
|
||||
if (!weights_attr.GetTimeSamples(×)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (times.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
blendshapes;
|
||||
if (!skel_anim.GetBlendShapesAttr().Get(&blendshapes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blendshapes.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t num_samples = times.size();
|
||||
|
||||
/* Create the animation and curves. */
|
||||
bAction *act = ED_id_action_ensure(bmain, (ID *)&key->id);
|
||||
std::vector<FCurve *> curves;
|
||||
|
||||
for (auto blendshape_name : blendshapes) {
|
||||
|
||||
if (shapekey_names.find(blendshape_name) == shapekey_names.end()) {
|
||||
printf("Warning: blendshape %s doesn't match any shapekey name\n",
|
||||
blendshape_name.GetString().c_str());
|
||||
}
|
||||
|
||||
std::string rna_path = "key_blocks[\"" + blendshape_name.GetString() + "\"].value";
|
||||
FCurve *fcu = create_fcurve(0, rna_path.c_str());
|
||||
fcu->totvert = num_samples;
|
||||
curves.push_back(fcu);
|
||||
BLI_addtail(&act->curves, fcu);
|
||||
}
|
||||
|
||||
for (double frame : times) {
|
||||
pxr::VtFloatArray weights;
|
||||
if (!weights_attr.Get(&weights, frame)) {
|
||||
std::cout << "Couldn't get blendshape weights for time " << frame << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (weights.size() != curves.size()) {
|
||||
std::cout << "Programmer error: number of weight samples doesn't match number of shapekey curves for time " << time << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int wi = 0; wi < weights.size(); ++wi) {
|
||||
add_bezt(curves[wi], frame, weights[wi]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* The Original Code is Copyright (C) 2022 NVIDIA Corporation.
|
||||
* All rights reserved.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct USDExportParams;
|
||||
struct USDImportParams;
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
struct ImportSettings;
|
||||
|
||||
void test_create_shapekeys(Main *bmain, Object *shape_obj);
|
||||
|
||||
void import_blendshapes(Main *bmain, Object *shape_obj, pxr::UsdPrim prim);
|
||||
|
||||
} // namespace blender::io::usd
|
|
@ -136,6 +136,7 @@ struct USDImportParams {
|
|||
bool import_lights;
|
||||
bool import_materials;
|
||||
bool import_meshes;
|
||||
bool import_blendshapes;
|
||||
bool import_volumes;
|
||||
char *prim_path_mask;
|
||||
bool import_subdiv;
|
||||
|
|
Loading…
Reference in New Issue