Files
test2/source/blender/io/usd/intern/usd_writer_light.cc
Jesse Yurkovich 1ceaaeeff7 USD: Write extents out for Curves and more consistently for other types
Cleanup and enhance our export of the USD `extent` attribute.

This does the following:
- The existing `author_extents` function now uses recently added common
  code to write out the extents attribute
- A new `author_extents` overload allows the use of Blender's native
  bounds for the types that support it. We now use this rather than
  asking USD to recompute it for us.
- Meshes will now have their extents correctly written during animations
- Curves will now have their extents written as they were not doing so
  prior to this PR
- Hair, Lights, Points, and Volumes make use of the `author_extents`
  functions now

Since Curves need their extents tested, this PR also moves the test from
C++ to Python. Python tests allow for faster iteration, are more
straightforward to write, and allow usage of the USD validator.

Pull Request: https://projects.blender.org/blender/blender/pulls/132531
2025-01-17 03:28:13 +01:00

172 lines
6.2 KiB
C++

/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_writer_light.hh"
#include "usd_attribute_utils.hh"
#include "usd_hierarchy_iterator.hh"
#include <pxr/usd/usdLux/diskLight.h>
#include <pxr/usd/usdLux/distantLight.h>
#include <pxr/usd/usdLux/rectLight.h>
#include <pxr/usd/usdLux/shapingAPI.h>
#include <pxr/usd/usdLux/sphereLight.h>
#include "BLI_assert.h"
#include "BLI_math_rotation.h"
#include "DNA_light_types.h"
namespace blender::io::usd {
USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) {}
bool USDLightWriter::is_supported(const HierarchyContext * /*context*/) const
{
return true;
}
void USDLightWriter::do_write(HierarchyContext &context)
{
pxr::UsdStageRefPtr stage = usd_export_context_.stage;
const pxr::SdfPath &usd_path = usd_export_context_.usd_path;
pxr::UsdTimeCode timecode = get_export_time_code();
const Light *light = static_cast<const Light *>(context.object->data);
pxr::UsdLuxLightAPI usd_light_api;
switch (light->type) {
case LA_AREA: {
switch (light->area_shape) {
case LA_AREA_RECT: {
pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path);
set_attribute(rect_light.CreateWidthAttr(pxr::VtValue(), true),
light->area_size,
timecode,
usd_value_writer_);
set_attribute(rect_light.CreateHeightAttr(pxr::VtValue(), true),
light->area_sizey,
timecode,
usd_value_writer_);
usd_light_api = rect_light.LightAPI();
break;
}
case LA_AREA_SQUARE: {
pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path);
set_attribute(rect_light.CreateWidthAttr(pxr::VtValue(), true),
light->area_size,
timecode,
usd_value_writer_);
set_attribute(rect_light.CreateHeightAttr(pxr::VtValue(), true),
light->area_size,
timecode,
usd_value_writer_);
usd_light_api = rect_light.LightAPI();
break;
}
case LA_AREA_DISK: {
pxr::UsdLuxDiskLight disk_light = pxr::UsdLuxDiskLight::Define(stage, usd_path);
set_attribute(disk_light.CreateRadiusAttr(pxr::VtValue(), true),
light->area_size / 2.0f,
timecode,
usd_value_writer_);
usd_light_api = disk_light.LightAPI();
break;
}
case LA_AREA_ELLIPSE: {
/* An ellipse light deteriorates into a disk light. */
pxr::UsdLuxDiskLight disk_light = pxr::UsdLuxDiskLight::Define(stage, usd_path);
set_attribute(disk_light.CreateRadiusAttr(pxr::VtValue(), true),
(light->area_size + light->area_sizey) / 4.0f,
timecode,
usd_value_writer_);
usd_light_api = disk_light.LightAPI();
break;
}
}
break;
}
case LA_LOCAL:
case LA_SPOT: {
pxr::UsdLuxSphereLight sphere_light = pxr::UsdLuxSphereLight::Define(stage, usd_path);
set_attribute(sphere_light.CreateRadiusAttr(pxr::VtValue(), true),
light->radius,
timecode,
usd_value_writer_);
set_attribute(sphere_light.CreateTreatAsPointAttr(pxr::VtValue(), true),
light->radius == 0.0f,
timecode,
usd_value_writer_);
if (light->type == LA_SPOT) {
pxr::UsdLuxShapingAPI shaping_api = pxr::UsdLuxShapingAPI::Apply(sphere_light.GetPrim());
if (shaping_api) {
set_attribute(shaping_api.CreateShapingConeAngleAttr(pxr::VtValue(), true),
RAD2DEGF(light->spotsize) / 2.0f,
timecode,
usd_value_writer_);
set_attribute(shaping_api.CreateShapingConeSoftnessAttr(pxr::VtValue(), true),
light->spotblend,
timecode,
usd_value_writer_);
}
}
usd_light_api = sphere_light.LightAPI();
break;
}
case LA_SUN: {
pxr::UsdLuxDistantLight distant_light = pxr::UsdLuxDistantLight::Define(stage, usd_path);
set_attribute(distant_light.CreateAngleAttr(pxr::VtValue(), true),
RAD2DEGF(light->sun_angle / 2.0f),
timecode,
usd_value_writer_);
usd_light_api = distant_light.LightAPI();
break;
}
default:
BLI_assert_unreachable();
break;
}
float intensity;
if (light->type == LA_SUN) {
/* Unclear why, but approximately matches Karma. */
intensity = light->energy / 4.0f;
}
else {
/* Convert from radiant flux to intensity. */
intensity = light->energy / M_PI;
}
set_attribute(usd_light_api.CreateIntensityAttr(pxr::VtValue(), true),
intensity,
timecode,
usd_value_writer_);
set_attribute(
usd_light_api.CreateExposureAttr(pxr::VtValue(), true), 0.0f, timecode, usd_value_writer_);
set_attribute(usd_light_api.CreateColorAttr(pxr::VtValue(), true),
pxr::GfVec3f(light->r, light->g, light->b),
timecode,
usd_value_writer_);
set_attribute(usd_light_api.CreateDiffuseAttr(pxr::VtValue(), true),
light->diff_fac,
timecode,
usd_value_writer_);
set_attribute(usd_light_api.CreateSpecularAttr(pxr::VtValue(), true),
light->spec_fac,
timecode,
usd_value_writer_);
set_attribute(
usd_light_api.CreateNormalizeAttr(pxr::VtValue(), true), true, timecode, usd_value_writer_);
pxr::UsdPrim prim = usd_light_api.GetPrim();
write_id_properties(prim, light->id, timecode);
/* Only a subset of light types are "boundable". */
if (auto boundable = pxr::UsdGeomBoundable(prim)) {
this->author_extent(boundable, timecode);
}
}
} // namespace blender::io::usd