Hydra: Handle dome light rotation
This refactors the code for world to dome light to be shared between USD and Hydra, and makes rotations work for Hydra the same way they do in USD. One small behavior change is that missing image files now render black, matching Cycles and EEVEE more closely. Co-authored-by: Brecht Van Lommel <brecht@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/143035
This commit is contained in:
committed by
Brecht Van Lommel
parent
83472b19fe
commit
9afa991316
@@ -7,27 +7,23 @@
|
||||
|
||||
#include <pxr/base/gf/rotation.h>
|
||||
#include <pxr/base/gf/vec2f.h>
|
||||
#include <pxr/base/gf/vec3f.h>
|
||||
#include <pxr/base/vt/array.h>
|
||||
#include <pxr/imaging/hd/light.h>
|
||||
#include <pxr/imaging/hd/renderDelegate.h>
|
||||
#include <pxr/imaging/hd/tokens.h>
|
||||
#include <pxr/usd/usdLux/tokens.h>
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_constants.h"
|
||||
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_legacy_types.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_studiolight.h"
|
||||
|
||||
#include "NOD_shader.h"
|
||||
|
||||
#include "hydra_scene_delegate.hh"
|
||||
#include "image.hh"
|
||||
#include "usd_light_convert.hh"
|
||||
|
||||
/* TODO: add custom `tftoken` "transparency"? */
|
||||
|
||||
@@ -45,71 +41,39 @@ void WorldData::init()
|
||||
{
|
||||
data_.clear();
|
||||
|
||||
pxr::GfVec3f color(1.0f, 1.0f, 1.0f);
|
||||
float intensity = 1.0f;
|
||||
pxr::SdfAssetPath texture_file;
|
||||
|
||||
if (scene_delegate_->shading_settings.use_scene_world) {
|
||||
const World *world = scene_delegate_->scene->world;
|
||||
pxr::GfVec3f color(1.0f, 1.0f, 1.0f);
|
||||
ID_LOG("%s", world->id.name);
|
||||
|
||||
world->nodetree->ensure_topology_cache();
|
||||
usd::WorldToDomeLight res;
|
||||
usd::world_material_to_dome_light(scene_delegate_->scene, res);
|
||||
|
||||
/* TODO: Create nodes parsing system */
|
||||
bNode *output_node = ntreeShaderOutputNode(world->nodetree, SHD_OUTPUT_ALL);
|
||||
if (!output_node) {
|
||||
return;
|
||||
}
|
||||
const Span<bNodeSocket *> input_sockets = output_node->input_sockets();
|
||||
bNodeSocket *input_socket = nullptr;
|
||||
if (res.image) {
|
||||
const std::string file_path = cache_or_get_image_file(
|
||||
scene_delegate_->bmain, scene_delegate_->scene, res.image, res.iuser);
|
||||
if (!file_path.empty()) {
|
||||
texture_file = pxr::SdfAssetPath(file_path, file_path);
|
||||
}
|
||||
|
||||
for (auto *socket : input_sockets) {
|
||||
if (STREQ(socket->name, "Surface")) {
|
||||
input_socket = socket;
|
||||
break;
|
||||
if (res.mult_found) {
|
||||
color = pxr::GfVec3f(res.color_mult);
|
||||
}
|
||||
}
|
||||
if (!input_socket) {
|
||||
return;
|
||||
else if (res.color_found) {
|
||||
const std::string File_path = blender::io::usd::cache_image_color(res.color);
|
||||
texture_file = pxr::SdfAssetPath(File_path, File_path);
|
||||
intensity = res.intensity;
|
||||
}
|
||||
if (input_socket->directly_linked_links().is_empty()) {
|
||||
return;
|
||||
}
|
||||
bNodeLink const *link = input_socket->directly_linked_links()[0];
|
||||
|
||||
bNode *input_node = link->fromnode;
|
||||
if (input_node->type_legacy != SH_NODE_BACKGROUND) {
|
||||
return;
|
||||
else {
|
||||
intensity = 0.0f;
|
||||
color = pxr::GfVec3f(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
const bNodeSocket &color_input = *input_node->input_by_identifier("Color");
|
||||
const bNodeSocket &strength_input = *input_node->input_by_identifier("Strength");
|
||||
|
||||
float const *strength = strength_input.default_value_typed<float>();
|
||||
float const *input_color = color_input.default_value_typed<float>();
|
||||
intensity = strength[1];
|
||||
color = pxr::GfVec3f(input_color[0], input_color[1], input_color[2]);
|
||||
|
||||
if (!color_input.directly_linked_links().is_empty()) {
|
||||
bNode *color_input_node = color_input.directly_linked_links()[0]->fromnode;
|
||||
if (ELEM(color_input_node->type_legacy, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT)) {
|
||||
NodeTexImage *tex = static_cast<NodeTexImage *>(color_input_node->storage);
|
||||
Image *image = (Image *)color_input_node->id;
|
||||
if (image) {
|
||||
std::string image_path = cache_or_get_image_file(
|
||||
scene_delegate_->bmain, scene_delegate_->scene, image, &tex->iuser);
|
||||
if (!image_path.empty()) {
|
||||
texture_file = pxr::SdfAssetPath(image_path, image_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (texture_file.GetAssetPath().empty()) {
|
||||
float fill_color[4] = {color[0], color[1], color[2], 1.0f};
|
||||
std::string image_path = blender::io::usd::cache_image_color(fill_color);
|
||||
texture_file = pxr::SdfAssetPath(image_path, image_path);
|
||||
}
|
||||
transform = res.transform;
|
||||
}
|
||||
else {
|
||||
ID_LOG("studiolight: %s", scene_delegate_->shading_settings.studiolight_name.c_str());
|
||||
@@ -119,18 +83,20 @@ void WorldData::init()
|
||||
STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE);
|
||||
if (sl != nullptr && sl->flag & STUDIOLIGHT_TYPE_WORLD) {
|
||||
texture_file = pxr::SdfAssetPath(sl->filepath, sl->filepath);
|
||||
/* coefficient to follow Cycles result */
|
||||
/* Coefficient to follow Cycles result */
|
||||
intensity = scene_delegate_->shading_settings.studiolight_intensity / 2;
|
||||
}
|
||||
|
||||
transform = pxr::GfMatrix4d().SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, -1.0),
|
||||
RAD2DEGF(scene_delegate_->shading_settings.studiolight_rotation)));
|
||||
}
|
||||
|
||||
data_[pxr::UsdLuxTokens->orientToStageUpAxis] = true;
|
||||
data_[pxr::HdLightTokens->intensity] = intensity;
|
||||
data_[pxr::HdLightTokens->exposure] = 0.0f;
|
||||
data_[pxr::HdLightTokens->color] = pxr::GfVec3f(1.0f, 1.0f, 1.0f);
|
||||
data_[pxr::HdLightTokens->color] = color;
|
||||
data_[pxr::HdLightTokens->textureFile] = texture_file;
|
||||
|
||||
write_transform();
|
||||
}
|
||||
|
||||
void WorldData::update()
|
||||
@@ -154,15 +120,4 @@ void WorldData::update()
|
||||
}
|
||||
}
|
||||
|
||||
void WorldData::write_transform()
|
||||
{
|
||||
transform = pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), 90.0)) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), 90.0));
|
||||
if (!scene_delegate_->shading_settings.use_scene_world) {
|
||||
transform *= pxr::GfMatrix4d().SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, -1.0),
|
||||
RAD2DEGF(scene_delegate_->shading_settings.studiolight_rotation)));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::io::hydra
|
||||
|
||||
@@ -20,9 +20,6 @@ class WorldData : public LightData {
|
||||
|
||||
void init() override;
|
||||
void update() override;
|
||||
|
||||
protected:
|
||||
void write_transform() override;
|
||||
};
|
||||
|
||||
} // namespace blender::io::hydra
|
||||
|
||||
@@ -53,26 +53,11 @@ static const pxr::TfToken pole_axis_z("Z", pxr::TfToken::Immortal);
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Helper struct for retrieving shader information when traversing a world material
|
||||
* node chain, provided as user data for #bke::node_chain_iterator().
|
||||
*/
|
||||
struct WorldNtreeSearchResults {
|
||||
struct WorldNtreeSearchPayload {
|
||||
const blender::io::usd::USDExportParams ¶ms;
|
||||
pxr::UsdStageRefPtr stage;
|
||||
|
||||
std::string file_path;
|
||||
|
||||
float world_intensity = 0.0f;
|
||||
float world_color[3]{};
|
||||
float mapping_rot[3]{};
|
||||
float color_mult[3]{};
|
||||
|
||||
bool background_found = false;
|
||||
bool env_tex_found = false;
|
||||
bool mult_found = false;
|
||||
|
||||
WorldNtreeSearchResults(const blender::io::usd::USDExportParams &in_params,
|
||||
WorldNtreeSearchPayload(const blender::io::usd::USDExportParams &in_params,
|
||||
pxr::UsdStageRefPtr in_stage)
|
||||
: params(in_params), stage(in_stage)
|
||||
{
|
||||
@@ -153,76 +138,6 @@ static bNode *append_node(bNode *dst_node,
|
||||
return src_node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for iterating over a shader node chain to retrieve data
|
||||
* necessary for converting a world material to a USD dome light. It also
|
||||
* handles copying textures, if required.
|
||||
*/
|
||||
static bool node_search(bNode *fromnode,
|
||||
bNode * /*tonode*/,
|
||||
void *userdata,
|
||||
const bool /*reversed*/)
|
||||
{
|
||||
if (!(userdata && fromnode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
WorldNtreeSearchResults *res = reinterpret_cast<WorldNtreeSearchResults *>(userdata);
|
||||
|
||||
if (!res->background_found && fromnode->type_legacy == SH_NODE_BACKGROUND) {
|
||||
/* Get light color and intensity */
|
||||
const bNodeSocketValueRGBA *color_data = bke::node_find_socket(*fromnode, SOCK_IN, "Color")
|
||||
->default_value_typed<bNodeSocketValueRGBA>();
|
||||
const bNodeSocketValueFloat *strength_data =
|
||||
bke::node_find_socket(*fromnode, SOCK_IN, "Strength")
|
||||
->default_value_typed<bNodeSocketValueFloat>();
|
||||
|
||||
res->background_found = true;
|
||||
res->world_intensity = strength_data->value;
|
||||
res->world_color[0] = color_data->value[0];
|
||||
res->world_color[1] = color_data->value[1];
|
||||
res->world_color[2] = color_data->value[2];
|
||||
}
|
||||
else if (!res->env_tex_found && fromnode->type_legacy == SH_NODE_TEX_ENVIRONMENT) {
|
||||
/* Get env tex path. */
|
||||
|
||||
res->file_path = get_tex_image_asset_filepath(fromnode, res->stage, res->params);
|
||||
|
||||
if (!res->file_path.empty()) {
|
||||
res->env_tex_found = true;
|
||||
if (res->params.export_textures) {
|
||||
export_texture(fromnode, res->stage, res->params.overwrite_textures);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!res->env_tex_found && !res->mult_found && fromnode->type_legacy == SH_NODE_VECTOR_MATH)
|
||||
{
|
||||
|
||||
if (fromnode->custom1 == NODE_VECTOR_MATH_MULTIPLY) {
|
||||
res->mult_found = true;
|
||||
|
||||
bNodeSocket *vec_sock = bke::node_find_socket(*fromnode, SOCK_IN, "Vector");
|
||||
if (vec_sock) {
|
||||
vec_sock = vec_sock->next;
|
||||
}
|
||||
|
||||
if (vec_sock) {
|
||||
copy_v3_v3(res->color_mult, ((bNodeSocketValueVector *)vec_sock->default_value)->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (res->env_tex_found && fromnode->type_legacy == SH_NODE_MAPPING) {
|
||||
copy_v3_fl(res->mapping_rot, 0.0f);
|
||||
if (bNodeSocket *socket = bke::node_find_socket(*fromnode, SOCK_IN, "Rotation")) {
|
||||
const bNodeSocketValueVector *rot_value = static_cast<bNodeSocketValueVector *>(
|
||||
socket->default_value);
|
||||
copy_v3_v3(res->mapping_rot, rot_value->value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void world_material_to_dome_light(const USDExportParams ¶ms,
|
||||
const Scene *scene,
|
||||
pxr::UsdStageRefPtr stage)
|
||||
@@ -231,58 +146,57 @@ void world_material_to_dome_light(const USDExportParams ¶ms,
|
||||
return;
|
||||
}
|
||||
|
||||
WorldNtreeSearchResults res(params, stage);
|
||||
WorldToDomeLight res;
|
||||
world_material_to_dome_light(scene, res);
|
||||
|
||||
if (scene->world->nodetree) {
|
||||
/* Find the world output. */
|
||||
bNode *output = nullptr;
|
||||
const bNodeTree *ntree = scene->world->nodetree;
|
||||
ntree->ensure_topology_cache();
|
||||
const Span<const bNode *> bsdf_nodes = ntree->nodes_by_type("ShaderNodeOutputWorld");
|
||||
for (const bNode *node : bsdf_nodes) {
|
||||
if (node->flag & NODE_DO_OUTPUT) {
|
||||
output = const_cast<bNode *>(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output) {
|
||||
/* No output, no valid network to convert. */
|
||||
return;
|
||||
}
|
||||
|
||||
bke::node_chain_iterator(scene->world->nodetree, output, node_search, &res, true);
|
||||
}
|
||||
else {
|
||||
res.world_intensity = 1.0f;
|
||||
zero_v3(res.world_color);
|
||||
res.background_found = false;
|
||||
}
|
||||
|
||||
if (!(res.background_found || res.env_tex_found)) {
|
||||
if (!(res.color_found || res.image)) {
|
||||
/* No nodes to convert */
|
||||
return;
|
||||
}
|
||||
|
||||
std::string image_filepath;
|
||||
if (res.image) {
|
||||
/* Compute image filepath and export if needed. */
|
||||
image_filepath = get_tex_image_asset_filepath(res.image, stage, params);
|
||||
if (image_filepath.empty()) {
|
||||
return;
|
||||
}
|
||||
if (params.export_textures) {
|
||||
export_texture(res.image, stage, params.overwrite_textures);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create USD dome light. */
|
||||
|
||||
pxr::SdfPath env_light_path = get_unique_path(stage, params.root_prim_path + "/env_light");
|
||||
|
||||
pxr::UsdLuxDomeLight dome_light = pxr::UsdLuxDomeLight::Define(stage, env_light_path);
|
||||
|
||||
if (!res.env_tex_found) {
|
||||
/* Like the Hydra delegate, if no texture is found export a solid
|
||||
* color texture as a stand-in so that Hydra renderers don't
|
||||
* throw errors. */
|
||||
if (res.image) {
|
||||
/* Use existing image texture file. */
|
||||
dome_light.CreateTextureFileAttr().Set(pxr::SdfAssetPath(image_filepath));
|
||||
|
||||
float fill_color[4] = {res.world_color[0], res.world_color[1], res.world_color[2], 1.0f};
|
||||
/* Set optional color multiplication. */
|
||||
if (res.mult_found) {
|
||||
pxr::GfVec3f color_val(res.color_mult[0], res.color_mult[1], res.color_mult[2]);
|
||||
dome_light.CreateColorAttr().Set(color_val);
|
||||
}
|
||||
|
||||
std::string source_path = cache_image_color(fill_color);
|
||||
/* Set transform. */
|
||||
pxr::GfVec3d angles = res.transform.DecomposeRotation(
|
||||
pxr::GfVec3d::ZAxis(), pxr::GfVec3d::YAxis(), pxr::GfVec3d::XAxis());
|
||||
pxr::GfVec3f rot_vec(angles[2], angles[1], angles[0]);
|
||||
pxr::UsdGeomXformCommonAPI xform_api(dome_light);
|
||||
xform_api.SetRotate(rot_vec, pxr::UsdGeomXformCommonAPI::RotationOrderXYZ);
|
||||
}
|
||||
else if (res.color_found) {
|
||||
/* If no texture is found export a solid color texture as a stand-in so that Hydra
|
||||
* renderers don't throw errors. */
|
||||
dome_light.CreateIntensityAttr().Set(res.intensity);
|
||||
|
||||
std::string source_path = cache_image_color(res.color);
|
||||
const std::string base_path = stage->GetRootLayer()->GetRealPath();
|
||||
|
||||
/* It'll be short, coming from cache_image_color. */
|
||||
char file_path[64];
|
||||
BLI_path_split_file_part(source_path.c_str(), file_path, 64);
|
||||
char file_path[FILE_MAX];
|
||||
BLI_path_split_file_part(source_path.c_str(), file_path, FILE_MAX);
|
||||
char dest_path[FILE_MAX];
|
||||
BLI_path_split_dir_part(base_path.c_str(), dest_path, FILE_MAX);
|
||||
|
||||
@@ -295,52 +209,11 @@ void world_material_to_dome_light(const USDExportParams ¶ms,
|
||||
CLOG_WARN(&LOG, "USD Export: Couldn't write world color image to %s", dest_path);
|
||||
}
|
||||
else {
|
||||
res.env_tex_found = true;
|
||||
BLI_path_join(dest_path, FILE_MAX, ".", "textures", file_path);
|
||||
res.file_path = dest_path;
|
||||
image_filepath = dest_path;
|
||||
dome_light.CreateTextureFileAttr().Set(pxr::SdfAssetPath(image_filepath));
|
||||
}
|
||||
}
|
||||
|
||||
if (res.env_tex_found) {
|
||||
pxr::SdfAssetPath path(res.file_path);
|
||||
dome_light.CreateTextureFileAttr().Set(path);
|
||||
|
||||
if (res.mult_found) {
|
||||
pxr::GfVec3f color_val(res.color_mult[0], res.color_mult[1], res.color_mult[2]);
|
||||
dome_light.CreateColorAttr().Set(color_val);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pxr::GfVec3f color_val(res.world_color[0], res.world_color[1], res.world_color[2]);
|
||||
dome_light.CreateColorAttr().Set(color_val);
|
||||
}
|
||||
|
||||
if (res.background_found) {
|
||||
dome_light.CreateIntensityAttr().Set(res.world_intensity);
|
||||
}
|
||||
|
||||
/* We always set a default rotation on the light since res.mapping_rot defaults to zeros. */
|
||||
|
||||
/* Convert radians to degrees. */
|
||||
mul_v3_fl(res.mapping_rot, 180.0f / M_PI);
|
||||
|
||||
pxr::GfMatrix4d xf =
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), 90.0)) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), 90.0)) *
|
||||
pxr::GfMatrix4d().SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), -res.mapping_rot[2])) *
|
||||
pxr::GfMatrix4d().SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d(0.0, 1.0, 0.0), -res.mapping_rot[1])) *
|
||||
pxr::GfMatrix4d().SetRotate(
|
||||
pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), -res.mapping_rot[0]));
|
||||
|
||||
pxr::GfVec3d angles = xf.DecomposeRotation(
|
||||
pxr::GfVec3d::ZAxis(), pxr::GfVec3d::YAxis(), pxr::GfVec3d::XAxis());
|
||||
|
||||
pxr::GfVec3f rot_vec(angles[2], angles[1], angles[0]);
|
||||
|
||||
pxr::UsdGeomXformCommonAPI xform_api(dome_light);
|
||||
xform_api.SetRotate(rot_vec, pxr::UsdGeomXformCommonAPI::RotationOrderXYZ);
|
||||
}
|
||||
|
||||
/* Import the dome light as a world material. */
|
||||
@@ -531,4 +404,79 @@ void dome_light_to_world_material(const USDImportParams ¶ms,
|
||||
BKE_ntree_update_after_single_tree_change(*bmain, *ntree);
|
||||
}
|
||||
|
||||
static bool node_search(bNode *fromnode, bNode * /*tonode*/, void *userdata, bool /*reversed*/)
|
||||
{
|
||||
if (!(userdata && fromnode)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
WorldToDomeLight &res = *static_cast<WorldToDomeLight *>(userdata);
|
||||
|
||||
if (!res.color_found && fromnode->type_legacy == SH_NODE_BACKGROUND) {
|
||||
/* Get light color and intensity */
|
||||
const bNodeSocketValueRGBA *color_data = bke::node_find_socket(*fromnode, SOCK_IN, "Color")
|
||||
->default_value_typed<bNodeSocketValueRGBA>();
|
||||
const bNodeSocketValueFloat *strength_data =
|
||||
bke::node_find_socket(*fromnode, SOCK_IN, "Strength")
|
||||
->default_value_typed<bNodeSocketValueFloat>();
|
||||
|
||||
res.color_found = true;
|
||||
res.intensity = strength_data->value;
|
||||
res.color[0] = color_data->value[0];
|
||||
res.color[1] = color_data->value[1];
|
||||
res.color[2] = color_data->value[2];
|
||||
res.color[3] = 1.0f;
|
||||
}
|
||||
else if (!res.image && fromnode->type_legacy == SH_NODE_TEX_ENVIRONMENT) {
|
||||
NodeTexImage *tex = static_cast<NodeTexImage *>(fromnode->storage);
|
||||
res.image = reinterpret_cast<Image *>(fromnode->id);
|
||||
res.iuser = &tex->iuser;
|
||||
}
|
||||
else if (!res.image && !res.mult_found && fromnode->type_legacy == SH_NODE_VECTOR_MATH) {
|
||||
if (fromnode->custom1 == NODE_VECTOR_MATH_MULTIPLY) {
|
||||
res.mult_found = true;
|
||||
|
||||
bNodeSocket *vec_sock = bke::node_find_socket(*fromnode, SOCK_IN, "Vector");
|
||||
if (vec_sock) {
|
||||
vec_sock = vec_sock->next;
|
||||
}
|
||||
|
||||
if (vec_sock) {
|
||||
copy_v3_v3(res.color_mult, ((bNodeSocketValueVector *)vec_sock->default_value)->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (res.image && fromnode->type_legacy == SH_NODE_MAPPING) {
|
||||
if (bNodeSocket *socket = bke::node_find_socket(*fromnode, SOCK_IN, "Rotation")) {
|
||||
const bNodeSocketValueVector *rot_value = static_cast<bNodeSocketValueVector *>(
|
||||
socket->default_value);
|
||||
/* Convert radians to degrees. */
|
||||
pxr::GfVec3f rot(rot_value->value);
|
||||
mul_v3_fl(rot.data(), 180.0f / M_PI);
|
||||
res.transform =
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), 90.0)) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), 90.0)) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 0.0, 1.0), -rot[2])) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(0.0, 1.0, 0.0), -rot[1])) *
|
||||
pxr::GfMatrix4d().SetRotate(pxr::GfRotation(pxr::GfVec3d(1.0, 0.0, 0.0), -rot[0]));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void world_material_to_dome_light(const Scene *scene, WorldToDomeLight &res)
|
||||
{
|
||||
/* Find the world output. */
|
||||
scene->world->nodetree->ensure_topology_cache();
|
||||
const Span<const bNode *> bsdf_nodes = scene->world->nodetree->nodes_by_type(
|
||||
"ShaderNodeOutputWorld");
|
||||
|
||||
for (const bNode *node : bsdf_nodes) {
|
||||
if (node->flag & NODE_DO_OUTPUT) {
|
||||
bke::node_chain_iterator(scene->world->nodetree, node, node_search, &res, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
||||
@@ -6,6 +6,11 @@
|
||||
#include <pxr/usd/sdf/types.h>
|
||||
#include <pxr/usd/usd/common.h>
|
||||
|
||||
struct bNode;
|
||||
struct bNodeTree;
|
||||
|
||||
struct Image;
|
||||
struct ImageUser;
|
||||
struct Main;
|
||||
struct Scene;
|
||||
|
||||
@@ -41,4 +46,25 @@ void dome_light_to_world_material(const USDImportParams ¶ms,
|
||||
const pxr::UsdPrim &prim,
|
||||
const pxr::UsdTimeCode time = 0.0);
|
||||
|
||||
/**
|
||||
* Helper struct for converting world shader nodes to a dome light, used by both
|
||||
* USD and Hydra. */
|
||||
struct WorldToDomeLight {
|
||||
/* Image and its transform. */
|
||||
Image *image = nullptr;
|
||||
ImageUser *iuser = nullptr;
|
||||
pxr::GfMatrix4d transform = pxr::GfMatrix4d(1.0);
|
||||
|
||||
/* Multiply image by color. */
|
||||
bool mult_found = false;
|
||||
float color_mult[4]{};
|
||||
|
||||
/* Fixed color. */
|
||||
bool color_found = false;
|
||||
float intensity = 0.0f;
|
||||
float color[4]{};
|
||||
};
|
||||
|
||||
void world_material_to_dome_light(const Scene *scene, WorldToDomeLight &res);
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
||||
BIN
tests/files/render/light/storm_hydra_renders/multiple_lights_in_volume.png
(Stored with Git LFS)
BIN
tests/files/render/light/storm_hydra_renders/multiple_lights_in_volume.png
(Stored with Git LFS)
Binary file not shown.
Reference in New Issue
Block a user