Files
test/source/blender/io/usd/hydra/world.cc
Habib Gahbiche 445eceb02a Nodes: Remove "Use Nodes" in Shader Editor for World
Part of https://projects.blender.org/blender/blender/pulls/141278

Blend files compatibility:
If a World exists and "Use Nodes" is disabled, we add new nodes to the
existing node tree (or create one if it doesn't) that emulates the
behavior of a world without a node tree. This ensures backward and
forward compatibility.

Python API compatibility:
- `world.use_nodes` was removed from Python API => **Breaking change**
- `world.color` is still being used by Workbench, so it stays there,
although it has no effect anymore when using Cycles or EEVEE.

Python API changes:
Creating a World using `bpy.data.worlds.new()` now creates a World with
 an empty (embedded) node tree. This was necessary to enable Python
scripts to add nodes without having to create a node tree (which is
currently not possible, because World node trees are embedded).

Pull Request: https://projects.blender.org/blender/blender/pulls/142342
2025-07-28 14:06:08 +02:00

166 lines
5.1 KiB
C++

/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "world.hh"
#include "usd_private.hh"
#include <pxr/base/gf/rotation.h>
#include <pxr/base/gf/vec2f.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 "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"
/* TODO: add custom `tftoken` "transparency"? */
/* NOTE: opacity and blur aren't supported by USD */
namespace blender::io::hydra {
WorldData::WorldData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &prim_id)
: LightData(scene_delegate, nullptr, prim_id)
{
prim_type_ = pxr::HdPrimTypeTokens->domeLight;
}
void WorldData::init()
{
data_.clear();
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);
/* 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;
for (auto *socket : input_sockets) {
if (STREQ(socket->name, "Surface")) {
input_socket = socket;
break;
}
}
if (!input_socket) {
return;
}
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;
}
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);
}
}
else {
ID_LOG("studiolight: %s", scene_delegate_->shading_settings.studiolight_name.c_str());
StudioLight *sl = BKE_studiolight_find(
scene_delegate_->shading_settings.studiolight_name.c_str(),
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 */
intensity = scene_delegate_->shading_settings.studiolight_intensity / 2;
}
}
data_[pxr::UsdLuxTokens->orientToStageUpAxis] = true;
data_[pxr::HdLightTokens->intensity] = intensity;
data_[pxr::HdLightTokens->color] = pxr::GfVec3f(1.0f, 1.0f, 1.0f);
data_[pxr::HdLightTokens->textureFile] = texture_file;
write_transform();
}
void WorldData::update()
{
ID_LOG("");
if (!scene_delegate_->shading_settings.use_scene_world ||
(scene_delegate_->shading_settings.use_scene_world && scene_delegate_->scene->world))
{
init();
if (data_.empty()) {
remove();
return;
}
insert();
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkSprimDirty(prim_id,
pxr::HdLight::AllDirty);
}
else {
remove();
}
}
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