Files
test2/source/blender/io/usd/intern/usd_reader_xform.cc
Ashley Ruglys fcd10ee33a USD: PointInstancer import support
Added support for static point instancing import.

Added a new USDPointInstancerReader class to import UsdGeomPointInstancer
primitives as Point Clouds with geometry node modifiers that use
Instance on Points geometry nodes to instance collections containing
prototype geometry.

Added logic to the USDStageReader class to traverse the USD stage to
create readers for point instancer prototypes.

Extended USDStageReader::collect_readers() to take arguments to include
undefined prims and to exclude a set of specified prims when traversing
the stage.  Allowing traversing undefined prims is necessary because
prototype prims may be specified as "overs" in the USD stage.

Added a USDPrimReader::is_in_instancer_proto_ boolean flag which
identifies readers of prims that are in point instancer prototypes.
The flag simplifies management of collections and is used to
determine whether global transforms should be applied to root objects.

Limitation: point cloud animation is not yet supported.

This partially addresses https://projects.blender.org/blender/blender/issues/96747.

Co-authored-by: Michael Kowalski <makowalski@nvidia.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/113107
2024-02-01 15:37:43 +01:00

179 lines
4.9 KiB
C++

/* SPDX-FileCopyrightText: 2021 Tangent Animation. All rights reserved.
* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Adapted from the Blender Alembic importer implementation. */
#include "usd_reader_xform.h"
#include "BKE_constraint.h"
#include "BKE_lib_id.hh"
#include "BKE_library.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BLI_math_geom.h"
#include "BLI_math_matrix.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 && settings_->get_cache_file) {
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_;
STRNCPY(data->object_path, prim_path.c_str());
data->cache_file = settings_->get_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)
{
BLI_assert(r_mat);
BLI_assert(r_is_constant);
*r_is_constant = true;
unit_m4(r_mat);
std::optional<XformResult> xf_result = get_local_usd_xform(time);
if (!xf_result) {
return;
}
std::get<0>(*xf_result).Get(r_mat);
*r_is_constant = std::get<1>(*xf_result);
/* 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 (is_in_proto()) {
/* 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;
}
std::optional<XformResult> USDXformReader::get_local_usd_xform(const float time) const
{
pxr::UsdGeomXformable xformable = use_parent_xform_ ? pxr::UsdGeomXformable(prim_.GetParent()) :
pxr::UsdGeomXformable(prim_);
if (!xformable) {
/* This might happen if the prim is a Scope. */
return std::nullopt;
}
bool is_constant = !xformable.TransformMightBeTimeVarying();
bool reset_xform_stack;
pxr::GfMatrix4d xform;
if (!xformable.GetLocalTransformation(&xform, &reset_xform_stack, time)) {
return std::nullopt;
}
/* The USD bind transform is a matrix of doubles,
* but we cast it to GfMatrix4f because Blender expects
* a matrix of floats. */
return XformResult(pxr::GfMatrix4f(xform), is_constant);
}
} // namespace blender::io::usd