Files
test/source/blender/io/usd/intern/usd_reader_xform.cc
Michael Kowalski ea54cbe1b4 USD: add USD importer
This is an initial implementation of a USD importer.

This work is comprised of Tangent Animation's open source USD importer,
combined with features @makowalski had implemented.

The design is very similar to the approach taken in the Alembic
importer. The core functionality resides in a collection of "reader"
classes, each of which is responsible for converting an instance of a
USD prim to the corresponding Blender Object representation.

The flow of control for the conversion can be followed in the
`import_startjob()` and `import_endjob()` functions in `usd_capi.cc`.
The `USDStageReader` class is responsible for traversing the USD stage
and instantiating the appropriate readers.

Reviewed By: sybren, HooglyBoogly

Differential Revision: https://developer.blender.org/D10700
2021-08-03 12:33:36 +02:00

185 lines
5.1 KiB
C++

/*
* 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.
*
* Adapted from the Blender Alembic importer implementation.
*
* Modifications Copyright (C) 2021 Tangent Animation.
* All rights reserved.
*/
#include "usd_reader_xform.h"
#include "BKE_constraint.h"
#include "BKE_lib_id.h"
#include "BKE_library.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BLI_math_geom.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_cachefile_types.h"
#include "DNA_constraint_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_space_types.h" /* for FILE_MAX */
#include <pxr/base/gf/math.h>
#include <pxr/base/gf/matrix4f.h>
#include <pxr/usd/usdGeom/xform.h>
namespace blender::io::usd {
void USDXformReader::create_object(Main *bmain, const double /* motionSampleTime */)
{
object_ = BKE_object_add_only_object(bmain, OB_EMPTY, name_.c_str());
object_->empty_drawsize = 0.1f;
object_->data = nullptr;
}
void USDXformReader::read_object_data(Main * /* bmain */, const double motionSampleTime)
{
bool is_constant;
float transform_from_usd[4][4];
read_matrix(transform_from_usd, motionSampleTime, import_params_.scale, &is_constant);
if (!is_constant) {
bConstraint *con = BKE_constraint_add_for_object(
object_, nullptr, CONSTRAINT_TYPE_TRANSFORM_CACHE);
bTransformCacheConstraint *data = static_cast<bTransformCacheConstraint *>(con->data);
std::string prim_path = use_parent_xform_ ? prim_.GetParent().GetPath().GetAsString() :
prim_path_;
BLI_strncpy(data->object_path, prim_path.c_str(), FILE_MAX);
data->cache_file = settings_->cache_file;
id_us_plus(&data->cache_file->id);
}
BKE_object_apply_mat4(object_, transform_from_usd, true, false);
}
void USDXformReader::read_matrix(float r_mat[4][4] /* local matrix */,
const float time,
const float scale,
bool *r_is_constant)
{
if (r_is_constant) {
*r_is_constant = true;
}
unit_m4(r_mat);
pxr::UsdGeomXformable xformable;
if (use_parent_xform_) {
xformable = pxr::UsdGeomXformable(prim_.GetParent());
}
else {
xformable = pxr::UsdGeomXformable(prim_);
}
if (!xformable) {
/* This might happen if the prim is a Scope. */
return;
}
if (r_is_constant) {
*r_is_constant = !xformable.TransformMightBeTimeVarying();
}
pxr::GfMatrix4d usd_local_xf;
bool reset_xform_stack;
xformable.GetLocalTransformation(&usd_local_xf, &reset_xform_stack, time);
/* Convert the result to a float matrix. */
pxr::GfMatrix4f mat4f = pxr::GfMatrix4f(usd_local_xf);
mat4f.Get(r_mat);
/* Apply global scaling and rotation only to root objects, parenting
* will propagate it. */
if ((scale != 1.0 || settings_->do_convert_mat) && is_root_xform_) {
if (scale != 1.0f) {
float scale_mat[4][4];
scale_m4_fl(scale_mat, scale);
mul_m4_m4m4(r_mat, scale_mat, r_mat);
}
if (settings_->do_convert_mat) {
mul_m4_m4m4(r_mat, settings_->conversion_mat, r_mat);
}
}
}
bool USDXformReader::prim_has_xform_ops() const
{
pxr::UsdGeomXformable xformable(prim_);
if (!xformable) {
/* This might happen if the prim is a Scope. */
return false;
}
bool reset_xform_stack = false;
return !xformable.GetOrderedXformOps(&reset_xform_stack).empty();
}
bool USDXformReader::is_root_xform_prim() const
{
if (!prim_.IsValid()) {
return false;
}
if (prim_.IsInMaster()) {
/* We don't consider prototypes to be root prims,
* because we never want to apply global scaling
* or rotations to the prototypes themselves. */
return false;
}
if (prim_.IsA<pxr::UsdGeomXformable>()) {
/* If this prim doesn't have an ancestor that's a
* UsdGeomXformable, then it's a root prim. Note
* that it's not sufficient to only check the immediate
* parent prim, since the immediate parent could be a
* UsdGeomScope that has an xformable ancestor. */
pxr::UsdPrim cur_parent = prim_.GetParent();
if (use_parent_xform_) {
cur_parent = cur_parent.GetParent();
}
while (cur_parent && !cur_parent.IsPseudoRoot()) {
if (cur_parent.IsA<pxr::UsdGeomXformable>()) {
return false;
}
cur_parent = cur_parent.GetParent();
}
/* We didn't find an xformable ancestor. */
return true;
}
return false;
}
} // namespace blender::io::usd