Improve USD camera import/export and round-tripping #112905

Merged
Brecht Van Lommel merged 3 commits from dkvnyc/blender:feature/CameraUSDRoundtripFixes into blender-v4.0-release 2023-10-05 19:47:19 +02:00
3 changed files with 47 additions and 33 deletions

View File

@ -60,22 +60,28 @@ void USDCameraReader::read_object_data(Main *bmain, const double motionSampleTim
/*
* For USD, these camera properties are in tenths of a world unit.
* https://graphics.pixar.com/usd/release/api/class_usd_geom_camera.html#UsdGeom_CameraUnits
* tenth_of_unit = stage_meters_per_unit / 10
* val_in_meters = val.Get<float>() * tenth_of_unit
* val_in_millimeters = val_in_meters * 1000
*
* tenth_unit_to_meters = stage_meters_per_unit / 10
* tenth_unit_to_millimeters = 1000 * unit_to_tenth_unit
* = 100 * stage_meters_per_unit
*/
const double scale_to_mm = 100.0 * settings_->stage_meters_per_unit;
bcam->lens = val.Get<float>() * scale_to_mm;
bcam->sensor_x = horAp.Get<float>() * scale_to_mm;
bcam->sensor_y = verAp.Get<float>() * scale_to_mm;
bcam->shiftx = verApOffset.Get<float>() * scale_to_mm;
bcam->shifty = horApOffset.Get<float>() * scale_to_mm;
const double tenth_unit_to_millimeters = 100.0 * settings_->stage_meters_per_unit;
bcam->lens = val.Get<float>() * tenth_unit_to_millimeters;
bcam->sensor_x = horAp.Get<float>() * tenth_unit_to_millimeters;
bcam->sensor_y = verAp.Get<float>() * tenth_unit_to_millimeters;
bcam->sensor_fit = bcam->sensor_x >= bcam->sensor_y ? CAMERA_SENSOR_FIT_HOR :
CAMERA_SENSOR_FIT_VERT;
float sensor_size = bcam->sensor_x >= bcam->sensor_y ? bcam->sensor_x : bcam->sensor_y;
bcam->shiftx = (horApOffset.Get<float>() * tenth_unit_to_millimeters) / sensor_size;
bcam->shifty = (verApOffset.Get<float>() * tenth_unit_to_millimeters) / sensor_size;
bcam->type = (projectionVal.Get<pxr::TfToken>().GetString() == "perspective") ? CAM_PERSP :
CAM_ORTHO;
/* Calling UncheckedGet() to silence compiler warnings. */
bcam->clip_start = max_ff(0.1f, clippingRangeVal.UncheckedGet<pxr::GfVec2f>()[0]);
/* Call UncheckedGet() to silence compiler warnings.
* Clamp to 1e-6 matching range defined in RNA. */
bcam->clip_end = clippingRangeVal.UncheckedGet<pxr::GfVec2f>()[1];
bcam->dof.focus_distance = focalDistanceVal.Get<float>();

View File

@ -25,6 +25,7 @@ bool USDCameraWriter::is_supported(const HierarchyContext *context) const
static void camera_sensor_size_for_render(const Camera *camera,
const RenderData *rd,
float *r_sensor,
float *r_sensor_x,
float *r_sensor_y)
{
@ -33,15 +34,18 @@ static void camera_sensor_size_for_render(const Camera *camera,
float sizey = rd->ysch * rd->yasp;
int sensor_fit = BKE_camera_sensor_fit(camera->sensor_fit, sizex, sizey);
float sensor_size = BKE_camera_sensor_size(
camera->sensor_fit, camera->sensor_x, camera->sensor_y);
*r_sensor = sensor_size;
switch (sensor_fit) {
case CAMERA_SENSOR_FIT_HOR:
*r_sensor_x = camera->sensor_x;
*r_sensor_y = camera->sensor_x * sizey / sizex;
*r_sensor_x = sensor_size;
*r_sensor_y = sensor_size * sizey / sizex;
break;
case CAMERA_SENSOR_FIT_VERT:
*r_sensor_x = camera->sensor_y * sizex / sizey;
*r_sensor_y = camera->sensor_y;
*r_sensor_x = sensor_size * sizex / sizey;
*r_sensor_y = sensor_size;
break;
case CAMERA_SENSOR_FIT_AUTO:
BLI_assert_msg(0, "Camera fit should be either horizontal or vertical");
@ -60,21 +64,26 @@ void USDCameraWriter::do_write(HierarchyContext &context)
usd_camera.CreateProjectionAttr().Set(pxr::UsdGeomTokens->perspective);
/* USD stores the focal length in "millimeters or tenths of world units", because at some point
* they decided world units might be centimeters. Quite confusing, as the USD Viewer shows the
* correct FoV when we write millimeters and not "tenths of world units".
/*
* For USD, these 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
* = 100 * stage_meters_per_unit
*/
usd_camera.CreateFocalLengthAttr().Set(camera->lens, timecode);
const float tenth_unit_to_mm = 100.0f * scene->unit.scale_length;
float aperture_x, aperture_y;
camera_sensor_size_for_render(camera, &scene->r, &aperture_x, &aperture_y);
float sensor_size, aperture_x, aperture_y;
camera_sensor_size_for_render(camera, &scene->r, &sensor_size, &aperture_x, &aperture_y);
float film_aspect = aperture_x / aperture_y;
usd_camera.CreateHorizontalApertureAttr().Set(aperture_x, timecode);
usd_camera.CreateVerticalApertureAttr().Set(aperture_y, timecode);
usd_camera.CreateHorizontalApertureOffsetAttr().Set(aperture_x * camera->shiftx, timecode);
usd_camera.CreateVerticalApertureOffsetAttr().Set(aperture_y * camera->shifty * film_aspect,
timecode);
usd_camera.CreateFocalLengthAttr().Set(camera->lens / tenth_unit_to_mm, timecode);
usd_camera.CreateHorizontalApertureAttr().Set(aperture_x / tenth_unit_to_mm, timecode);
usd_camera.CreateVerticalApertureAttr().Set(aperture_y / tenth_unit_to_mm, timecode);
usd_camera.CreateHorizontalApertureOffsetAttr().Set(
sensor_size * camera->shiftx / tenth_unit_to_mm, timecode);
usd_camera.CreateVerticalApertureOffsetAttr().Set(
sensor_size * camera->shifty / tenth_unit_to_mm, timecode);
usd_camera.CreateClippingRangeAttr().Set(
pxr::VtValue(pxr::GfVec2f(camera->clip_start, camera->clip_end)), timecode);
@ -83,8 +92,7 @@ void USDCameraWriter::do_write(HierarchyContext &context)
if (camera->dof.flag & CAM_DOF_ENABLED) {
usd_camera.CreateFStopAttr().Set(camera->dof.aperture_fstop, timecode);
float focus_distance = scene->unit.scale_length *
BKE_camera_object_dof_distance(context.object);
float focus_distance = BKE_camera_object_dof_distance(context.object);
usd_camera.CreateFocusDistanceAttr().Set(focus_distance, timecode);
}
}

View File

@ -183,8 +183,8 @@ class USDImportTest(AbstractUSDTest):
self.assertAlmostEqual(43.12, test_cam.lens, 2)
self.assertAlmostEqual(24.89, test_cam.sensor_width, 2)
self.assertAlmostEqual(14.00, test_cam.sensor_height, 2)
self.assertAlmostEqual(12.34, test_cam.shift_x, 2)
self.assertAlmostEqual(56.78, test_cam.shift_y, 2)
self.assertAlmostEqual(2.281, test_cam.shift_x, 2)
self.assertAlmostEqual(0.496, test_cam.shift_y, 2)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
@ -200,8 +200,8 @@ class USDImportTest(AbstractUSDTest):
self.assertAlmostEqual(4.312, test_cam.lens, 3)
self.assertAlmostEqual(2.489, test_cam.sensor_width, 3)
self.assertAlmostEqual(1.400, test_cam.sensor_height, 3)
self.assertAlmostEqual(1.234, test_cam.shift_x, 3)
self.assertAlmostEqual(5.678, test_cam.shift_y, 3)
self.assertAlmostEqual(2.281, test_cam.shift_x, 3)
self.assertAlmostEqual(0.496, test_cam.shift_y, 3)
def test_import_shader_varname_with_connection(self):
"""Test importing USD shader where uv primvar is a connection"""