diff --git a/source/blender/render/hydra/CMakeLists.txt b/source/blender/render/hydra/CMakeLists.txt index 1114514a934c..1b993328380d 100644 --- a/source/blender/render/hydra/CMakeLists.txt +++ b/source/blender/render/hydra/CMakeLists.txt @@ -20,6 +20,7 @@ set(INC ../../../../intern/guardedalloc ../../makesdna ../../makesrna + ../../nodes ../../blenlib ../../depsgraph ../../blenkernel @@ -65,6 +66,8 @@ set(SRC sceneDelegate/object.cc sceneDelegate/material.h sceneDelegate/material.cc + sceneDelegate/world.h + sceneDelegate/world.cc ) set(LIB diff --git a/source/blender/render/hydra/finalEngine.cc b/source/blender/render/hydra/finalEngine.cc index 9f4c7e100af5..695aba6eb125 100644 --- a/source/blender/render/hydra/finalEngine.cc +++ b/source/blender/render/hydra/finalEngine.cc @@ -23,7 +23,7 @@ void FinalEngine::sync(BL::Depsgraph &b_depsgraph, BL::Context &b_context, pxr:: { sceneDelegate = std::make_unique(renderIndex.get(), SdfPath::AbsoluteRootPath().AppendElementString("scene")); - sceneDelegate->Populate(b_depsgraph); + sceneDelegate->Populate(b_depsgraph, b_context); for (auto const& setting : renderSettings) { renderDelegate->SetRenderSetting(setting.first, setting.second); diff --git a/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.cc b/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.cc index 7fd1c829c768..d1668b8c9bac 100644 --- a/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.cc +++ b/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.cc @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -11,11 +12,14 @@ #include "blenderSceneDelegate.h" #include "object.h" +using namespace std; + namespace blender::render::hydra { BlenderSceneDelegate::BlenderSceneDelegate(HdRenderIndex* parentIndex, SdfPath const& delegateID) : HdSceneDelegate(parentIndex, delegateID), b_depsgraph(nullptr), + b_context(nullptr), view3d(nullptr), is_populated(false) { @@ -50,10 +54,38 @@ void BlenderSceneDelegate::update_material(Material *material) } } +void BlenderSceneDelegate::add_update_world(World *world) +{ + SdfPath world_light_id = world_id(); + + LOG(INFO) << "Add world: " << world_light_id; + + if (!world) { + world_data = nullptr; + GetRenderIndex().RemoveSprim(HdPrimTypeTokens->domeLight, world_light_id); + return; + } + + if (!world_data) { + world_data = make_unique(world, (bContext *)b_context->ptr.data); + GetRenderIndex().InsertSprim(HdPrimTypeTokens->domeLight, this, world_light_id); + } + else { + world_data = make_unique(world, (bContext *)b_context->ptr.data); + GetRenderIndex().GetChangeTracker().MarkSprimDirty(world_light_id, HdLight::AllDirty); + } +} + bool BlenderSceneDelegate::GetVisible(SdfPath const &id) { ObjectData *obj_data = object_data(id); - LOG(INFO) << "GetVisible: " << id.GetAsString() << " " << obj_data->is_visible(); + LOG(INFO) << "GetVisible: " << id.GetAsString(); + + HdRenderIndex &index = GetRenderIndex(); + + if (id == world_id()) { + return world_data->is_visible(); + } return obj_data->is_visible(); } @@ -207,6 +239,11 @@ SdfPath BlenderSceneDelegate::material_id(Material *material) return GetDelegateID().AppendElementString(str); } +SdfPath BlenderSceneDelegate::world_id() +{ + return GetDelegateID().AppendElementString("World"); +} + bool BlenderSceneDelegate::supported_object(Object *object) { return object->type == OB_MESH || @@ -218,17 +255,21 @@ bool BlenderSceneDelegate::supported_object(Object *object) object->type == OB_MBALL; } -void BlenderSceneDelegate::Populate(BL::Depsgraph &b_deps, View3D *v3d) +void BlenderSceneDelegate::Populate(BL::Depsgraph &b_deps, BL::Context &b_cont) { LOG(INFO) << "Populate " << is_populated; - view3d = v3d; + view3d = (View3D *)b_cont.space_data().ptr.data; b_depsgraph = &b_deps; + b_context = &b_cont; if (!is_populated) { /* Export initial objects */ update_collection(); + World *world = (World *)b_depsgraph->scene().world().ptr.data; + add_update_world(world); + is_populated = true; return; } @@ -272,11 +313,25 @@ void BlenderSceneDelegate::Populate(BL::Depsgraph &b_deps, View3D *v3d) } if (id.is_a(&RNA_Scene)) { + World *world = (World *)b_depsgraph->scene().world().ptr.data; + add_update_world(world); if (!update.is_updated_geometry() && !update.is_updated_transform() && !update.is_updated_shading()) { do_update_visibility = true; } continue; } + + if (id.is_a(&RNA_World)) { + World *world = (World *)b_depsgraph->scene().world().ptr.data; + add_update_world(world); + continue; + } + + if (id.is_a(&RNA_ShaderNodeTree)) { + World *world = (World *)b_depsgraph->scene().world().ptr.data; + add_update_world(world); + continue; + } } if (do_update_collection) { @@ -397,6 +452,12 @@ GfMatrix4d BlenderSceneDelegate::GetTransform(SdfPath const& id) { LOG(INFO) << "GetTransform: " << id.GetAsString(); + HdRenderIndex &index = GetRenderIndex(); + + if (id == world_id()) { + return world_data->transform(index.GetRenderDelegate()->GetRendererDisplayName()); + } + return objects[id].transform(); } @@ -404,6 +465,9 @@ VtValue BlenderSceneDelegate::GetLightParamValue(SdfPath const& id, TfToken cons { LOG(INFO) << "GetLightParamValue: " << id.GetAsString() << " [" << key.GetString() << "]"; VtValue ret; + + HdRenderIndex &index = GetRenderIndex(); + ObjectData *obj_data = object_data(id); if (obj_data) { if (obj_data->has_data(key)) { @@ -414,6 +478,12 @@ VtValue BlenderSceneDelegate::GetLightParamValue(SdfPath const& id, TfToken cons ret = 1.0f; } } + else if (id == world_id()) { + if (world_data->has_data(key)) { + ret = world_data->get_data(key); + } + } + return ret; } diff --git a/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.h b/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.h index 77867f2f5c06..ea244055be46 100644 --- a/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.h +++ b/source/blender/render/hydra/sceneDelegate/blenderSceneDelegate.h @@ -15,6 +15,7 @@ #include "RNA_blender_cpp.h" #include "object.h" +#include "world.h" using namespace pxr; @@ -25,7 +26,7 @@ public: BlenderSceneDelegate(HdRenderIndex* renderIndex, SdfPath const &delegateId); ~BlenderSceneDelegate() override = default; - void Populate(BL::Depsgraph &b_deps, View3D *v3d = nullptr); + void Populate(BL::Depsgraph &b_deps, BL::Context &b_context); // delegate methods HdMeshTopology GetMeshTopology(SdfPath const& id) override; @@ -42,20 +43,24 @@ private: MaterialData *material_data(SdfPath const &id); SdfPath object_id(Object *object); SdfPath material_id(Material *material); + SdfPath world_id(); bool supported_object(Object *object); void add_update_object(Object *object, bool geometry, bool transform, bool shading); void set_material(ObjectData &obj_data); void update_material(Material *material); + void add_update_world(World *world); void update_collection(); void update_visibility(); private: BL::Depsgraph *b_depsgraph; + BL::Context *b_context; View3D *view3d; bool is_populated; ObjectDataMap objects; MaterialDataMap materials; + std::unique_ptr world_data; }; } // namespace blender::render::hydra diff --git a/source/blender/render/hydra/sceneDelegate/world.cc b/source/blender/render/hydra/sceneDelegate/world.cc new file mode 100644 index 000000000000..2accd814d821 --- /dev/null +++ b/source/blender/render/hydra/sceneDelegate/world.cc @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "BKE_context.h" +#include "DNA_node_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_node.h" +#include "BKE_node_runtime.hh" +#include "BKE_image.h" +#include "NOD_shader.h" + +#include "world.h" +#include "../utils.h" + +/* TODO : add custom tftoken "transparency"? */ + +using namespace pxr; +using namespace std; + +namespace blender::render::hydra { + +WorldData::WorldData() + : b_context(nullptr), + world(nullptr) +{ +} + +WorldData::WorldData(World *world, bContext *b_context) + : b_context(b_context), + world(world) +{ + data.clear(); + + data[UsdLuxTokens->orientToStageUpAxis] = true; + + if (world->use_nodes) { + bNode *output_node = ntreeShaderOutputNode(world->nodetree, SHD_OUTPUT_ALL); + bNodeSocket input_socket = output_node->input_by_identifier("Surface"); + bNodeLink const *link = input_socket.directly_linked_links()[0]; + if (input_socket.directly_linked_links().is_empty()) { + return; + } + + bNode *input_node = link->fromnode; + + bNodeSocket color_input = input_node->input_by_identifier("Color"); + bNodeSocket strength_input = input_node->input_by_identifier("Strength"); + + float const *strength = strength_input.default_value_typed(); + float const *color = color_input.default_value_typed(); + data[HdLightTokens->intensity] = strength[1]; + data[HdLightTokens->exposure] = 1.0f; + data[HdLightTokens->color] = GfVec3f(color[0], color[1], color[2]); + + if (!color_input.directly_linked_links().is_empty()) { + bNode *color_input_node = color_input.directly_linked_links()[0]->fromnode; + if (color_input_node->type == SH_NODE_TEX_IMAGE) { + NodeTexImage *tex = static_cast(color_input_node->storage); + Image *image = (Image *)color_input_node->id; + + if (image) { + Main *bmain = CTX_data_main(b_context); + Scene *scene = CTX_data_scene(b_context); + + ReportList reports; + ImageSaveOptions opts; + opts.im_format.imtype = R_IMF_IMTYPE_PNG; + + string cached_image_path = cache_image(bmain, scene, image, &tex->iuser, &opts, &reports); + if (!cached_image_path.empty()) { + data[HdLightTokens->textureFile] = SdfAssetPath(cached_image_path, cached_image_path); + } + } + } + } + } + else { + data[HdLightTokens->intensity] = 1.0f; + data[HdLightTokens->exposure] = world->exposure; + data[HdLightTokens->color] = GfVec3f(world->horr, world->horg, world->horb); + } +} + +GfMatrix4d WorldData::transform(string const &renderer_name) +{ + GfMatrix4d transform = GfMatrix4d().SetIdentity(); + + if (has_data(UsdLuxTokens->orientToStageUpAxis)) { + transform *= GfMatrix4d(GfRotation(GfVec3d(1.0, 0.0, 0.0), -90), GfVec3d()); + } + /* TODO : do this check via RenderSettings*/ + if (renderer_name == "RPR") { + transform *= GfMatrix4d(GfRotation(GfVec3d(1.0, 0.0, 0.0), -180), GfVec3d()); + transform *= GfMatrix4d(GfRotation(GfVec3d(0.0, 0.0, 1.0), 90.0), GfVec3d()); + } + + return transform; +} + +VtValue &WorldData::get_data(TfToken const &key) +{ + return data[key]; +} + +bool WorldData::has_data(TfToken const &key) +{ + return data.find(key) != data.end(); +} + +bool WorldData::is_visible() +{ + return true; +} + +} // namespace blender::render::hydra diff --git a/source/blender/render/hydra/sceneDelegate/world.h b/source/blender/render/hydra/sceneDelegate/world.h new file mode 100644 index 000000000000..b61d5b4824a0 --- /dev/null +++ b/source/blender/render/hydra/sceneDelegate/world.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2011-2022 Blender Foundation */ + +#pragma once + +#include + +#include +#include +#include +#include +#include "pxr/base/tf/staticTokens.h" + +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" + +namespace blender::render::hydra { + +class WorldData { +public: + WorldData(); + WorldData(World *world, bContext *b_context); + + pxr::TfToken prim_type(); + pxr::GfMatrix4d transform(std::string const &renderer_name); + + pxr::VtValue &get_data(pxr::TfToken const &key); + template + const T &get_data(pxr::TfToken const &key); + bool has_data(pxr::TfToken const &key); + bool is_visible(); + + bContext *b_context; + World *world; + + private: + std::map data; +}; + +template +const T &WorldData::get_data(pxr::TfToken const &key) +{ + return get_data(key).Get(); +} + +} // namespace blender::render::hydra diff --git a/source/blender/render/hydra/utils.cc b/source/blender/render/hydra/utils.cc index 1bb6c7ec40f9..37ad5807a418 100644 --- a/source/blender/render/hydra/utils.cc +++ b/source/blender/render/hydra/utils.cc @@ -2,6 +2,14 @@ * Copyright 2011-2022 Blender Foundation */ #include +#include + +#include + +#include "BKE_appdir.h" +#include "BKE_image_save.h" +#include "BLI_string.h" +#include "BLI_path_util.h" #include "DNA_camera_types.h" @@ -25,24 +33,70 @@ string format_duration(chrono::milliseconds millisecs) { stringstream ss; bool neg = millisecs < 0ms; - if (neg) - millisecs = -millisecs; + if (neg) { + millisecs = -millisecs; + } auto m = chrono::duration_cast(millisecs); millisecs -= m; auto s = chrono::duration_cast(millisecs); millisecs -= s; - if (neg) - ss << "-"; - if (m < 10min) - ss << "0"; + if (neg) { + ss << "-"; + } + if (m < 10min) { + ss << "0"; + } ss << to_string(m / 1min) << ":"; - if (s < 10s) - ss << "0"; - ss << to_string(s/1s) << ":"; - if (millisecs < 10ms) - ss << "0"; - ss << to_string(millisecs/1ms/10); + if (s < 10s) { + ss << "0"; + } + ss << to_string(s / 1s) << ":"; + if (millisecs < 10ms) { + ss << "0"; + } + ss << to_string(millisecs / 1ms / 10); return ss.str(); } -} // namespace blender::render::hydra +string cache_image(Main *bmain, + Scene *scene, + Image *image, + ImageUser *iuser, + ImageSaveOptions *opts, + ReportList *reports) +{ + const string default_format = ".png"; + + char tempfile[FILE_MAX]; + + if (!BKE_image_save_options_init(opts, bmain, scene, image, iuser, true, false)) { + BKE_image_save_options_free(opts); + return ""; + } + + string image_name; + + if (image->source == IMA_SRC_GENERATED) { + image_name = TfMakeValidIdentifier(image_name.append(image->id.name + 2)); + } + else { + image_name = image->filepath == NULL ? image->filepath : image->id.name + 2; + image_name = std::filesystem::path(image_name).filename().replace_extension().string(); + image_name = TfMakeValidIdentifier(image_name); + } + + image_name.append(default_format); + + BLI_path_join(tempfile, sizeof(tempfile), BKE_tempdir_session(), image_name.c_str()); + STRNCPY(opts->filepath, tempfile); + + if (!BKE_image_save(reports, bmain, image, iuser, opts)) { + BKE_image_save_options_free(opts); + return ""; + }; + + BKE_image_save_options_free(opts); + return tempfile; +} + +} // namespace blender::render::hydra diff --git a/source/blender/render/hydra/utils.h b/source/blender/render/hydra/utils.h index ecb6273a652d..06a994c73f26 100644 --- a/source/blender/render/hydra/utils.h +++ b/source/blender/render/hydra/utils.h @@ -8,9 +8,18 @@ #include +#include "BKE_image.h" +#include "BKE_image_save.h" + namespace blender::render::hydra { pxr::GfMatrix4d gf_matrix_from_transform(float m[4][4]); std::string format_duration(std::chrono::milliseconds secs); +std::string cache_image(Main *bmain, + Scene *scene, + Image *image, + ImageUser *iuser, + ImageSaveOptions *opts, + ReportList *reports); } // namespace blender::render::hydra diff --git a/source/blender/render/hydra/viewportEngine.cc b/source/blender/render/hydra/viewportEngine.cc index 07ea6d92bedf..882a3ada8dd6 100644 --- a/source/blender/render/hydra/viewportEngine.cc +++ b/source/blender/render/hydra/viewportEngine.cc @@ -246,8 +246,7 @@ void ViewportEngine::sync(BL::Depsgraph &b_depsgraph, BL::Context &b_context, Hd sceneDelegate = std::make_unique(renderIndex.get(), SdfPath::AbsoluteRootPath().AppendElementString("scene")); } - View3D *view3d = (View3D *)b_context.space_data().ptr.data; - sceneDelegate->Populate(b_depsgraph, view3d); + sceneDelegate->Populate(b_depsgraph, b_context); for (auto const& setting : renderSettings) { renderDelegate->SetRenderSetting(setting.first, setting.second);