diff --git a/source/blender/io/usd/intern/usd_armature_utils.cc b/source/blender/io/usd/intern/usd_armature_utils.cc index 721791c1329..477d465779f 100644 --- a/source/blender/io/usd/intern/usd_armature_utils.cc +++ b/source/blender/io/usd/intern/usd_armature_utils.cc @@ -5,7 +5,11 @@ #include "usd_armature_utils.hh" #include "usd_utils.hh" +#include "ANIM_action.hh" +#include "ANIM_fcurve.hh" + #include "BKE_armature.hh" +#include "BKE_fcurve.hh" #include "BKE_modifier.hh" #include "BLI_listbase.h" @@ -19,6 +23,29 @@ namespace blender::io::usd { +/* Utility: create new fcurve and add it as a channel to a group. */ +FCurve *create_fcurve(blender::animrig::Channelbag &channelbag, + const blender::animrig::FCurveDescriptor &fcurve_descriptor, + const int sample_count) +{ + FCurve *fcurve = channelbag.fcurve_create_unique(nullptr, fcurve_descriptor); + BLI_assert_msg(fcurve, "The same F-Curve is being created twice, this is unexpected."); + BKE_fcurve_bezt_resize(fcurve, sample_count); + return fcurve; +} + +/* Utility: fill in a single fcurve sample at the provided index. */ +void set_fcurve_sample(FCurve *fcu, uint sample_index, const float frame, const float value) +{ + BLI_assert(sample_index >= 0 && sample_index < fcu->totvert); + BezTriple &bez = fcu->bezt[sample_index]; + bez.vec[1][0] = frame; + bez.vec[1][1] = value; + bez.ipo = BEZT_IPO_LIN; + bez.f1 = bez.f2 = bez.f3 = SELECT; + bez.h1 = bez.h2 = HD_AUTO; +} + /* Recursively invoke the 'visitor' function on the given bone and its children. */ static void visit_bones(const Bone *bone, FunctionRef visitor) { diff --git a/source/blender/io/usd/intern/usd_armature_utils.hh b/source/blender/io/usd/intern/usd_armature_utils.hh index a571d54ee04..e42ad371974 100644 --- a/source/blender/io/usd/intern/usd_armature_utils.hh +++ b/source/blender/io/usd/intern/usd_armature_utils.hh @@ -16,14 +16,28 @@ struct Bone; struct Depsgraph; +struct FCurve; struct ModifierData; struct Object; +namespace blender::animrig { +class Channelbag; +struct FCurveDescriptor; +} // namespace blender::animrig + namespace blender::io::usd { /* Custom Blender Primvar name used for storing armature bone lengths. */ inline const pxr::TfToken BlenderBoneLengths("blender:bone_lengths", pxr::TfToken::Immortal); +/* Utility: create new fcurve and add it as a channel to a group. */ +FCurve *create_fcurve(blender::animrig::Channelbag &channelbag, + const blender::animrig::FCurveDescriptor &fcurve_descriptor, + const int sample_count); + +/* Utility: fill in a single fcurve sample at the provided index. */ +void set_fcurve_sample(FCurve *fcu, uint sample_index, const float frame, const float value); + /** * Recursively invoke the given function on the given armature object's bones. * This function is a no-op if the object isn't an armature. diff --git a/source/blender/io/usd/intern/usd_reader_camera.cc b/source/blender/io/usd/intern/usd_reader_camera.cc index f7ab6503f09..b666a64fcab 100644 --- a/source/blender/io/usd/intern/usd_reader_camera.cc +++ b/source/blender/io/usd/intern/usd_reader_camera.cc @@ -6,84 +6,335 @@ * Adapted from the Blender Alembic importer implementation. */ #include "usd_reader_camera.hh" +#include "usd_armature_utils.hh" -#include "DNA_camera_types.h" -#include "DNA_object_types.h" +#include "ANIM_action.hh" +#include "ANIM_animdata.hh" #include "BLI_math_base.h" #include "BKE_camera.h" +#include "BKE_fcurve.hh" #include "BKE_object.hh" +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + #include +#include +#include + namespace blender::io::usd { +namespace { + +template struct SampleData { + float frame; + T value; +}; + +template struct AttributeData { + std::optional initial_value = std::nullopt; + Vector> samples; + + void reset() + { + initial_value = std::nullopt; + samples.clear(); + } +}; + +template +bool read_attribute_values(const pxr::UsdAttribute &attr, + const pxr::UsdTimeCode initial_time, + AttributeData &data) +{ + data.reset(); /* Clear any prior data. */ + + T value{}; + if (attr.Get(&value, initial_time)) { + data.initial_value = value; + } + else { + data.initial_value = std::nullopt; + } + + if (attr.ValueMightBeTimeVarying()) { + std::vector times; + attr.GetTimeSamples(×); + + data.samples.resize(times.size()); + for (int64_t i = 0; i < times.size(); i++) { + data.samples[i].frame = float(times[i]); + attr.Get(&data.samples[i].value, times[i]); + } + } + + return data.initial_value.has_value() || !data.samples.is_empty(); +} + +void read_aperture_data(Camera *camera, + const pxr::UsdAttribute &usd_horiz_aperture, + const pxr::UsdAttribute &usd_vert_aperture, + const pxr::UsdAttribute &usd_horiz_offset, + const pxr::UsdAttribute &usd_vert_offset, + const pxr::UsdTimeCode initial_time, + const double tenth_unit_to_millimeters, + animrig::Channelbag &channelbag) +{ + /* If the Aperture values are changing, that effects the sensor_fit and shift_x|y values as + * well. We need to put animation data on all of them. */ + if (usd_horiz_aperture.ValueMightBeTimeVarying() || usd_vert_aperture.ValueMightBeTimeVarying()) + { + std::vector times; + pxr::UsdAttribute::GetUnionedTimeSamples( + {usd_horiz_aperture, usd_vert_aperture, usd_horiz_offset, usd_vert_offset}, ×); + + std::array curves = { + create_fcurve(channelbag, {"sensor_width", 0}, times.size()), + create_fcurve(channelbag, {"sensor_height", 0}, times.size()), + create_fcurve(channelbag, {"sensor_fit", 0}, times.size()), + create_fcurve(channelbag, {"shift_x", 0}, times.size()), + create_fcurve(channelbag, {"shift_y", 0}, times.size())}; + + for (int64_t i = 0; i < times.size(); i++) { + const double time = times[i]; + + float horiz_aperture, vert_aperture; + float shift_x, shift_y; + usd_horiz_aperture.Get(&horiz_aperture, time); + usd_vert_aperture.Get(&vert_aperture, time); + usd_horiz_offset.Get(&shift_x, time); + usd_vert_offset.Get(&shift_y, time); + + const float sensor_x = horiz_aperture * tenth_unit_to_millimeters; + const float sensor_y = vert_aperture * tenth_unit_to_millimeters; + const char sensor_fit = horiz_aperture >= vert_aperture ? CAMERA_SENSOR_FIT_HOR : + CAMERA_SENSOR_FIT_VERT; + + const float sensor_size = sensor_x >= sensor_y ? sensor_x : sensor_y; + shift_x = (shift_x * tenth_unit_to_millimeters) / sensor_size; + shift_y = (shift_y * tenth_unit_to_millimeters) / sensor_size; + + set_fcurve_sample(curves[0], i, float(time), sensor_x); + set_fcurve_sample(curves[1], i, float(time), sensor_y); + set_fcurve_sample(curves[2], i, float(time), sensor_fit); + set_fcurve_sample(curves[3], i, float(time), shift_x); + set_fcurve_sample(curves[4], i, float(time), shift_y); + } + } + else if (usd_horiz_offset.ValueMightBeTimeVarying() || usd_vert_offset.ValueMightBeTimeVarying()) + { + /* Only the shift_x|y values are changing. Load in the initial values for aperture and + * sensor_fit and use those when setting the shift_x|y curves. */ + float horiz_aperture, vert_aperture; + usd_horiz_aperture.Get(&horiz_aperture, initial_time); + usd_vert_aperture.Get(&vert_aperture, initial_time); + + camera->sensor_x = horiz_aperture * tenth_unit_to_millimeters; + camera->sensor_y = vert_aperture * tenth_unit_to_millimeters; + camera->sensor_fit = camera->sensor_x >= camera->sensor_y ? CAMERA_SENSOR_FIT_HOR : + CAMERA_SENSOR_FIT_VERT; + const float sensor_size = camera->sensor_x >= camera->sensor_y ? camera->sensor_x : + camera->sensor_y; + + std::vector times; + if (usd_horiz_offset.GetTimeSamples(×)) { + FCurve *fcu = create_fcurve(channelbag, {"shift_x", 0}, times.size()); + for (int64_t i = 0; i < times.size(); i++) { + const double time = times[i]; + float shift; + usd_horiz_offset.Get(&shift, time); + + shift = (shift * tenth_unit_to_millimeters) / sensor_size; + set_fcurve_sample(fcu, i, float(time), shift); + } + } + + if (usd_vert_offset.GetTimeSamples(×)) { + FCurve *fcu = create_fcurve(channelbag, {"shift_y", 0}, times.size()); + for (int64_t i = 0; i < times.size(); i++) { + const double time = times[i]; + float shift; + usd_vert_offset.Get(&shift, time); + + shift = (shift * tenth_unit_to_millimeters) / sensor_size; + set_fcurve_sample(fcu, i, float(time), shift); + } + } + } + else { + /* No animation data. */ + float horiz_aperture, vert_aperture; + float shift_x, shift_y; + usd_horiz_aperture.Get(&horiz_aperture, initial_time); + usd_vert_aperture.Get(&vert_aperture, initial_time); + usd_horiz_offset.Get(&shift_x, initial_time); + usd_vert_offset.Get(&shift_y, initial_time); + + camera->sensor_x = horiz_aperture * tenth_unit_to_millimeters; + camera->sensor_y = vert_aperture * tenth_unit_to_millimeters; + camera->sensor_fit = camera->sensor_x >= camera->sensor_y ? CAMERA_SENSOR_FIT_HOR : + CAMERA_SENSOR_FIT_VERT; + const float sensor_size = camera->sensor_x >= camera->sensor_y ? camera->sensor_x : + camera->sensor_y; + camera->shiftx = (shift_x * tenth_unit_to_millimeters) / sensor_size; + camera->shifty = (shift_y * tenth_unit_to_millimeters) / sensor_size; + } +} + +} // namespace + void USDCameraReader::create_object(Main *bmain) { - Camera *bcam = BKE_camera_add(bmain, name_.c_str()); + Camera *camera = BKE_camera_add(bmain, name_.c_str()); object_ = BKE_object_add_only_object(bmain, OB_CAMERA, name_.c_str()); - object_->data = bcam; + object_->data = camera; } void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTime) { - Camera *bcam = (Camera *)object_->data; + pxr::UsdAttribute usd_focal_length = cam_prim_.GetFocalLengthAttr(); + pxr::UsdAttribute usd_focus_dist = cam_prim_.GetFocusDistanceAttr(); + pxr::UsdAttribute usd_fstop = cam_prim_.GetFStopAttr(); + pxr::UsdAttribute usd_clipping_range = cam_prim_.GetClippingRangeAttr(); + pxr::UsdAttribute usd_horiz_aperture = cam_prim_.GetHorizontalApertureAttr(); + pxr::UsdAttribute usd_vert_aperture = cam_prim_.GetVerticalApertureAttr(); + pxr::UsdAttribute usd_horiz_offset = cam_prim_.GetHorizontalApertureOffsetAttr(); + pxr::UsdAttribute usd_vert_offset = cam_prim_.GetVerticalApertureOffsetAttr(); - pxr::VtValue val; - cam_prim_.GetFocalLengthAttr().Get(&val, motionSampleTime); - pxr::VtValue verApOffset; - cam_prim_.GetVerticalApertureOffsetAttr().Get(&verApOffset, motionSampleTime); - pxr::VtValue horApOffset; - cam_prim_.GetHorizontalApertureOffsetAttr().Get(&horApOffset, motionSampleTime); - pxr::VtValue clippingRangeVal; - cam_prim_.GetClippingRangeAttr().Get(&clippingRangeVal, motionSampleTime); - pxr::VtValue focalDistanceVal; - cam_prim_.GetFocusDistanceAttr().Get(&focalDistanceVal, motionSampleTime); - pxr::VtValue fstopVal; - cam_prim_.GetFStopAttr().Get(&fstopVal, motionSampleTime); - pxr::VtValue projectionVal; - cam_prim_.GetProjectionAttr().Get(&projectionVal, motionSampleTime); - pxr::VtValue verAp; - cam_prim_.GetVerticalApertureAttr().Get(&verAp, motionSampleTime); - pxr::VtValue horAp; - cam_prim_.GetHorizontalApertureAttr().Get(&horAp, motionSampleTime); + /* If any of the camera attributes are time varying, then prepare the animation data. */ + const bool is_time_varying = usd_focal_length.ValueMightBeTimeVarying() || + usd_focus_dist.ValueMightBeTimeVarying() || + usd_fstop.ValueMightBeTimeVarying() || + usd_clipping_range.ValueMightBeTimeVarying() || + usd_horiz_aperture.ValueMightBeTimeVarying() || + usd_vert_aperture.ValueMightBeTimeVarying() || + usd_horiz_offset.ValueMightBeTimeVarying() || + usd_vert_offset.ValueMightBeTimeVarying(); + + Camera *camera = (Camera *)object_->data; + + bAction *action = nullptr; + if (is_time_varying) { + action = blender::animrig::id_action_ensure(bmain, &camera->id); + } + + animrig::Channelbag empty{}; + animrig::Channelbag &channelbag = is_time_varying ? + animrig::action_channelbag_ensure(*action, camera->id) : + empty; /* - * For USD, these camera properties are in tenths of a world unit. + * In USD, some camera properties are in tenths of a world unit. * https://graphics.pixar.com/usd/release/api/class_usd_geom_camera.html#UsdGeom_CameraUnits * * tenth_unit_to_meters = stage_meters_per_unit / 10 - * tenth_unit_to_millimeters = 1000 * unit_to_tenth_unit + * tenth_unit_to_millimeters = 1000 * tenth_unit_to_meters * = 100 * stage_meters_per_unit */ const double tenth_unit_to_millimeters = 100.0 * settings_->stage_meters_per_unit; - bcam->lens = val.Get() * tenth_unit_to_millimeters; - bcam->sensor_x = horAp.Get() * tenth_unit_to_millimeters; - bcam->sensor_y = verAp.Get() * tenth_unit_to_millimeters; + auto scale_default = [](std::optional input, double scale, float default_value) { + return input.has_value() ? input.value() * scale : default_value; + }; - bcam->sensor_fit = bcam->sensor_x >= bcam->sensor_y ? CAMERA_SENSOR_FIT_HOR : - CAMERA_SENSOR_FIT_VERT; + AttributeData data; + if (read_attribute_values(usd_focal_length, motionSampleTime, data)) { + camera->lens = scale_default(data.initial_value, tenth_unit_to_millimeters, camera->lens); - float sensor_size = bcam->sensor_x >= bcam->sensor_y ? bcam->sensor_x : bcam->sensor_y; - bcam->shiftx = (horApOffset.Get() * tenth_unit_to_millimeters) / sensor_size; - bcam->shifty = (verApOffset.Get() * tenth_unit_to_millimeters) / sensor_size; + if (!data.samples.is_empty()) { + FCurve *fcu = create_fcurve(channelbag, {"lens", 0}, data.samples.size()); + for (int64_t i = 0; i < data.samples.size(); i++) { + const SampleData &sample = data.samples[i]; + set_fcurve_sample(fcu, i, sample.frame, sample.value * tenth_unit_to_millimeters); + } + } + } - bcam->type = (projectionVal.Get().GetString() == "perspective") ? CAM_PERSP : - CAM_ORTHO; + if (read_attribute_values(usd_focus_dist, motionSampleTime, data)) { + camera->dof.focus_distance = scale_default( + data.initial_value, this->settings_->scene_scale, camera->dof.focus_distance); - /* Call UncheckedGet() to silence compiler warnings. - * Clamp to 1e-6 matching range defined in RNA. */ - bcam->clip_start = max_ff( - 1e-6f, clippingRangeVal.UncheckedGet()[0] * settings_->scene_scale); - bcam->clip_end = clippingRangeVal.UncheckedGet()[1] * settings_->scene_scale; + if (!data.samples.is_empty()) { + FCurve *fcu = create_fcurve(channelbag, {"dof.focus_distance", 0}, data.samples.size()); + for (int64_t i = 0; i < data.samples.size(); i++) { + const SampleData &sample = data.samples[i]; + set_fcurve_sample(fcu, i, sample.frame, sample.value * this->settings_->scene_scale); + } + } + } - bcam->dof.focus_distance = focalDistanceVal.Get() * settings_->scene_scale; - bcam->dof.aperture_fstop = float(fstopVal.Get()); + if (read_attribute_values(usd_fstop, motionSampleTime, data)) { + camera->dof.aperture_fstop = scale_default(data.initial_value, 1, camera->dof.aperture_fstop); - if (bcam->type == CAM_ORTHO) { - bcam->ortho_scale = max_ff(verAp.Get(), horAp.Get()); + if (!data.samples.is_empty()) { + FCurve *fcu = create_fcurve(channelbag, {"dof.aperture_fstop", 0}, data.samples.size()); + for (int64_t i = 0; i < data.samples.size(); i++) { + const SampleData &sample = data.samples[i]; + set_fcurve_sample(fcu, i, sample.frame, sample.value); + } + } + } + + AttributeData clip_data; + if (read_attribute_values(usd_clipping_range, motionSampleTime, clip_data)) { + auto clamp_clip = [this](pxr::GfVec2f value) { + /* Clamp the value for clip-start, matching the range defined in RNA. */ + return pxr::GfVec2f(max_ff(1e-6f, value[0] * settings_->scene_scale), + value[1] * settings_->scene_scale); + }; + + pxr::GfVec2f clip_range = clip_data.initial_value.has_value() ? + clamp_clip(clip_data.initial_value.value()) : + pxr::GfVec2f(camera->clip_start, camera->clip_end); + camera->clip_start = clip_range[0]; + camera->clip_end = clip_range[1]; + + if (!clip_data.samples.is_empty()) { + std::array curves = { + create_fcurve(channelbag, {"clip_start", 0}, clip_data.samples.size()), + create_fcurve(channelbag, {"clip_end", 0}, clip_data.samples.size())}; + + for (int64_t i = 0; i < clip_data.samples.size(); i++) { + const SampleData &sample = clip_data.samples[i]; + clip_range = clamp_clip(sample.value); + set_fcurve_sample(curves[0], i, sample.frame, clip_range[0]); + set_fcurve_sample(curves[1], i, sample.frame, clip_range[1]); + } + } + } + + /* Aperture data impacts sensor size, sensor fit, and shift values simultaneously. */ + read_aperture_data(camera, + usd_horiz_aperture, + usd_vert_aperture, + usd_horiz_offset, + usd_vert_offset, + motionSampleTime, + tenth_unit_to_millimeters, + channelbag); + + /* USD Orthographic cameras have very limited support. Support a basic, non-animated, translation + * between USD and Blender. */ + pxr::TfToken projection; + cam_prim_.GetProjectionAttr().Get(&projection, motionSampleTime); + camera->type = (projection.GetString() == "perspective") ? CAM_PERSP : CAM_ORTHO; + if (camera->type == CAM_ORTHO) { + float horiz_aperture, vert_aperture; + usd_horiz_aperture.Get(&horiz_aperture, motionSampleTime); + usd_vert_aperture.Get(&vert_aperture, motionSampleTime); + camera->ortho_scale = max_ff(vert_aperture, horiz_aperture); + } + + /* Enable depth of field when needed. */ + const bool requires_dof = usd_focus_dist.IsAuthored() || usd_fstop.IsAuthored(); + camera->dof.flag |= requires_dof ? CAM_DOF_ENABLED : 0; + + /* Recalculate any animation curve handles. */ + for (FCurve *fcu : channelbag.fcurves()) { + BKE_fcurve_handles_recalc(fcu); } USDXformReader::read_object_data(bmain, motionSampleTime); diff --git a/source/blender/io/usd/intern/usd_skel_convert.cc b/source/blender/io/usd/intern/usd_skel_convert.cc index d78dd56732c..99704411c29 100644 --- a/source/blender/io/usd/intern/usd_skel_convert.cc +++ b/source/blender/io/usd/intern/usd_skel_convert.cc @@ -71,32 +71,6 @@ void resize_fcurve(FCurve *fcu, uint bezt_count) BKE_fcurve_bezt_resize(fcu, bezt_count); } -/* Utility: create curve at the given array index and add it as a channel to a group. */ -FCurve *create_fcurve(blender::animrig::Channelbag &channelbag, - const blender::animrig::FCurveDescriptor &fcurve_descriptor, - const int totvert) -{ - FCurve *fcurve = channelbag.fcurve_create_unique(nullptr, fcurve_descriptor); - BLI_assert_msg(fcurve, "The same F-Curve is being created twice, this is unexpected."); - BKE_fcurve_bezt_resize(fcurve, totvert); - return fcurve; -} - -/* Utility: add curve sample. */ -void add_bezt(FCurve *fcu, - uint bezt_index, - const float frame, - const float value, - const eBezTriple_Interpolation ipo = BEZT_IPO_LIN) -{ - BezTriple &bez = fcu->bezt[bezt_index]; - 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; -} - /** * Import a USD skeleton animation as an action on the given armature object. * This assumes bones have already been created on the armature. @@ -114,6 +88,8 @@ void import_skeleton_curves(Main *bmain, const blender::Map &joint_to_bone_map, ReportList *reports) { + using namespace blender::io::usd; + if (!(bmain && arm_obj && skel_query)) { return; } @@ -285,7 +261,7 @@ void import_skeleton_curves(Main *bmain, break; } if (FCurve *fcu = fcurves[k]) { - add_bezt(fcu, bezt_index, frame, t[j]); + set_fcurve_sample(fcu, bezt_index, frame, t[j]); } } @@ -297,10 +273,10 @@ void import_skeleton_curves(Main *bmain, } if (FCurve *fcu = fcurves[k]) { if (j == 0) { - add_bezt(fcu, bezt_index, frame, re); + set_fcurve_sample(fcu, bezt_index, frame, re); } else { - add_bezt(fcu, bezt_index, frame, im[j - 1]); + set_fcurve_sample(fcu, bezt_index, frame, im[j - 1]); } } } @@ -312,7 +288,7 @@ void import_skeleton_curves(Main *bmain, break; } if (FCurve *fcu = fcurves[k]) { - add_bezt(fcu, bezt_index, frame, s[j]); + set_fcurve_sample(fcu, bezt_index, frame, s[j]); } } } @@ -657,7 +633,7 @@ void import_blendshapes(Main *bmain, Span weights = Span(usd_weights.cdata(), usd_weights.size()); for (int wi = 0; wi < weights.size(); ++wi) { if (curves[wi] != nullptr) { - add_bezt(curves[wi], bezt_index, frame, weights[wi]); + set_fcurve_sample(curves[wi], bezt_index, frame, weights[wi]); } } diff --git a/tests/data b/tests/data index 9323581121d..588d5ad63b8 160000 --- a/tests/data +++ b/tests/data @@ -1 +1 @@ -Subproject commit 9323581121dae4867563c9529a0375f390bfbffc +Subproject commit 588d5ad63b80a842e2a2b6d5ff8c7fd135d68b3f diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 31805f7267a..15abef20ad4 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -1136,6 +1136,7 @@ if(WITH_USD AND TEST_SRC_DIR_EXISTS) --python ${CMAKE_CURRENT_LIST_DIR}/bl_usd_import_test.py -- --testdir "${TEST_SRC_DIR}/usd" + --outdir "${TEST_OUT_DIR}/io_usd" ) endif() diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py index f380d2963b8..9a10eb220e0 100644 --- a/tests/python/bl_usd_import_test.py +++ b/tests/python/bl_usd_import_test.py @@ -1847,6 +1847,34 @@ class USDImportTest(AbstractUSDTest): bpy.utils.unregister_class(ImportMtlxTextureUSDHook) +class USDImportComparisonTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + cls.output_dir = args.outdir + + def test_import_usd(self): + comparisondir = self.testdir.joinpath("compare") + input_files = sorted(pathlib.Path(comparisondir).glob("*.usd*")) + self.passed_tests = [] + self.failed_tests = [] + self.updated_tests = [] + + from modules import io_report + report = io_report.Report("USD Import", self.output_dir, comparisondir, comparisondir.joinpath("reference")) + + for input_file in input_files: + with self.subTest(pathlib.Path(input_file).stem): + bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) + ok = report.import_and_check( + input_file, lambda filepath, params: bpy.ops.wm.usd_import( + filepath=str(input_file), import_subdiv=True, **params)) + if not ok: + self.fail(f"{input_file.stem} import result does not match expectations") + + report.finish("io_usd_import") + + class GetPrimMapUsdImportHook(bpy.types.USDHook): bl_idname = "get_prim_map_usd_import_hook" bl_label = "Get Prim Map Usd Import Hook" @@ -1962,6 +1990,7 @@ def main(): parser = argparse.ArgumentParser() parser.add_argument('--testdir', required=True, type=pathlib.Path) + parser.add_argument('--outdir', required=True, type=pathlib.Path) args, remaining = parser.parse_known_args(argv) unittest.main(argv=remaining, verbosity=0)