From ade8576bf70fc627e00c761a727691dfc8b1ef57 Mon Sep 17 00:00:00 2001 From: Cartesian Caramel Date: Fri, 28 Mar 2025 22:54:13 +0100 Subject: [PATCH] Geometry Nodes: new Camera Info Node This adds a new Camera Info node to Geometry Nodes. It provides information about the passed in camera like its projection matrix and focus distance. This can be used for camera culling which was must more complex before. It also allows building other view-dependent effects. Pull Request: https://projects.blender.org/blender/blender/pulls/135311 --- .../startup/bl_ui/node_add_menu_geometry.py | 1 + .../blenlib/BLI_struct_equality_utils.hh | 8 ++ .../blender/makesrna/intern/rna_nodetree.cc | 1 + source/blender/modifiers/intern/MOD_nodes.cc | 9 +- .../nodes/NOD_geometry_nodes_dependencies.hh | 9 +- source/blender/nodes/geometry/CMakeLists.txt | 1 + .../geometry/nodes/node_geo_camera_info.cc | 85 +++++++++++++++++++ .../intern/geometry_nodes_dependencies.cc | 17 ++++ 8 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 source/blender/nodes/geometry/nodes/node_geo_camera_info.cc diff --git a/scripts/startup/bl_ui/node_add_menu_geometry.py b/scripts/startup/bl_ui/node_add_menu_geometry.py index a4f3fa5e6a3..e21bcceff53 100644 --- a/scripts/startup/bl_ui/node_add_menu_geometry.py +++ b/scripts/startup/bl_ui/node_add_menu_geometry.py @@ -328,6 +328,7 @@ class NODE_MT_geometry_node_GEO_INPUT_SCENE(Menu): if context.space_data.geometry_nodes_type == 'TOOL': node_add_menu.add_node_type(layout, "GeometryNodeTool3DCursor") node_add_menu.add_node_type(layout, "GeometryNodeInputActiveCamera") + node_add_menu.add_node_type(layout, "GeometryNodeCameraInfo") node_add_menu.add_node_type(layout, "GeometryNodeCollectionInfo") node_add_menu.add_node_type(layout, "GeometryNodeImageInfo") node_add_menu.add_node_type(layout, "GeometryNodeIsViewport") diff --git a/source/blender/blenlib/BLI_struct_equality_utils.hh b/source/blender/blenlib/BLI_struct_equality_utils.hh index 6cec721ea9f..88f90d09bb2 100644 --- a/source/blender/blenlib/BLI_struct_equality_utils.hh +++ b/source/blender/blenlib/BLI_struct_equality_utils.hh @@ -53,3 +53,11 @@ return a.m1 == b.m1 && a.m2 == b.m2 && a.m3 == b.m3 && a.m4 == b.m4 && a.m5 == b.m5; \ } \ BLI_STRUCT_DERIVED_UNEQUAL_OPERATOR(Type) + +#define BLI_STRUCT_EQUALITY_OPERATORS_6(Type, m1, m2, m3, m4, m5, m6) \ + friend bool operator==(const Type &a, const Type &b) \ + { \ + return a.m1 == b.m1 && a.m2 == b.m2 && a.m3 == b.m3 && a.m4 == b.m4 && a.m5 == b.m5 && \ + a.m6 == b.m6; \ + } \ + BLI_STRUCT_DERIVED_UNEQUAL_OPERATOR(Type) diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index fd72e91ca9c..1cab68c8e03 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -12451,6 +12451,7 @@ static void rna_def_nodes(BlenderRNA *brna) define("GeometryNode", "GeometryNodeBake", rna_def_geo_bake); define("GeometryNode", "GeometryNodeBlurAttribute"); define("GeometryNode", "GeometryNodeBoundBox"); + define("GeometryNode", "GeometryNodeCameraInfo"); define("GeometryNode", "GeometryNodeCaptureAttribute", rna_def_geo_capture_attribute); define("GeometryNode", "GeometryNodeCollectionInfo"); define("GeometryNode", "GeometryNodeConvexHull"); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index db14b8e6eb6..bbbfaf9fb3b 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -152,6 +152,11 @@ static void add_object_relation( DEG_add_customdata_mask(ctx->node, &object, &dependency_data_mask); } } + if (object.type == OB_CAMERA) { + if (info.camera_parameters) { + DEG_add_object_relation(ctx->node, &object, DEG_OB_COMP_PARAMETERS, "Nodes Modifier"); + } + } } static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) @@ -218,7 +223,9 @@ static void update_depsgraph(ModifierData *md, const ModifierUpdateDepsgraphCont } if (eval_deps.needs_active_camera) { DEG_add_scene_camera_relation(ctx->node, ctx->scene, DEG_OB_COMP_TRANSFORM, "Nodes Modifier"); - /* Active camera is a scene parameter that can change, so we need a relation for that, too. */ + } + /* Active camera is a scene parameter that can change, so we need a relation for that, too. */ + if (eval_deps.needs_active_camera || eval_deps.needs_scene_render_params) { DEG_add_scene_relation(ctx->node, ctx->scene, DEG_SCENE_COMP_PARAMETERS, "Nodes Modifier"); } } diff --git a/source/blender/nodes/NOD_geometry_nodes_dependencies.hh b/source/blender/nodes/NOD_geometry_nodes_dependencies.hh index f5511497acd..4d6c90d2095 100644 --- a/source/blender/nodes/NOD_geometry_nodes_dependencies.hh +++ b/source/blender/nodes/NOD_geometry_nodes_dependencies.hh @@ -24,10 +24,11 @@ struct GeometryNodesEvalDependencies { struct ObjectDependencyInfo { bool transform = false; bool geometry = false; + bool camera_parameters = false; - BLI_STRUCT_EQUALITY_OPERATORS_2(ObjectDependencyInfo, transform, geometry); + BLI_STRUCT_EQUALITY_OPERATORS_3(ObjectDependencyInfo, transform, geometry, camera_parameters); }; - static constexpr ObjectDependencyInfo all_object_deps{true, true}; + static constexpr ObjectDependencyInfo all_object_deps{true, true, true}; /** * Maps `session_uid` to the corresponding data-block. @@ -41,6 +42,7 @@ struct GeometryNodesEvalDependencies { bool needs_own_transform = false; bool needs_active_camera = false; + bool needs_scene_render_params = false; bool time_dependent = false; /** @@ -67,11 +69,12 @@ struct GeometryNodesEvalDependencies { */ void merge(const GeometryNodesEvalDependencies &other); - BLI_STRUCT_EQUALITY_OPERATORS_5(GeometryNodesEvalDependencies, + BLI_STRUCT_EQUALITY_OPERATORS_6(GeometryNodesEvalDependencies, ids, objects_info, needs_own_transform, needs_active_camera, + needs_scene_render_params, time_dependent); }; diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 8d9fcd00e24..34ba9d4da15 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -31,6 +31,7 @@ set(SRC nodes/node_geo_blur_attribute.cc nodes/node_geo_boolean.cc nodes/node_geo_bounding_box.cc + nodes/node_geo_camera_info.cc nodes/node_geo_collection_info.cc nodes/node_geo_common.cc nodes/node_geo_convex_hull.cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_camera_info.cc b/source/blender/nodes/geometry/nodes/node_geo_camera_info.cc new file mode 100644 index 00000000000..2f1440cf1c3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_camera_info.cc @@ -0,0 +1,85 @@ +#include "BKE_camera.h" + +#include "DEG_depsgraph_query.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes::node_geo_camera_info_cc { + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.use_custom_socket_order(); + + b.add_output("Projection Matrix").description("Camera projection matrix"); + b.add_output("Focal Length").description("Perspective camera focal length"); + b.add_output("Sensor").description("Size of the camera sensor"); + b.add_output("Shift").description("Camera shift"); + b.add_output("Clip Start").description("Camera near clipping distance"); + b.add_output("Clip End").description("Camera far clipping distance"); + b.add_output("Focus Distance") + .description("Distance to the focus point for depth of field"); + b.add_output("Is Orthographic") + .description("Whether the camera is using orthographic projection"); + b.add_output("Orthographic Scale") + .description("Orthographic camera scale (similar to zoom)"); + + b.add_input("Camera").hide_label(); +} + +static void node_geo_exec(GeoNodeExecParams params) +{ + const Scene *scene = DEG_get_evaluated_scene(params.depsgraph()); + if (!scene) { + params.set_default_remaining_outputs(); + return; + } + + const Object *camera_obj = params.get_input("Camera"); + + if (!camera_obj || camera_obj->type != OB_CAMERA) { + params.set_default_remaining_outputs(); + return; + } + + Camera *camera = static_cast(camera_obj->data); + if (!camera) { + params.set_default_remaining_outputs(); + return; + } + + CameraParams camera_params; + BKE_camera_params_init(&camera_params); + BKE_camera_params_from_object(&camera_params, camera_obj); + BKE_camera_params_compute_viewplane( + &camera_params, scene->r.xsch, scene->r.ysch, scene->r.xasp, scene->r.yasp); + BKE_camera_params_compute_matrix(&camera_params); + + const float4x4 projection_matrix(camera_params.winmat); + float focus_distance = BKE_camera_object_dof_distance(camera_obj); + + params.set_output("Projection Matrix", projection_matrix); + params.set_output("Focal Length", camera_params.lens); + params.set_output("Sensor", float3{camera_params.sensor_x, camera_params.sensor_y, 0.0f}); + params.set_output("Shift", float3{camera_params.shiftx, camera_params.shifty, 0.0f}); + params.set_output("Clip Start", camera_params.clip_start); + params.set_output("Clip End", camera_params.clip_end); + params.set_output("Focus Distance", focus_distance); + params.set_output("Is Orthographic", camera_params.is_ortho); + params.set_output("Orthographic Scale", camera_params.ortho_scale); +} + +static void node_register() +{ + static blender::bke::bNodeType ntype; + + geo_node_type_base(&ntype, "GeometryNodeCameraInfo"); + ntype.ui_name = "Camera Info"; + ntype.ui_description = "Retrieve information from a camera object"; + ntype.nclass = NODE_CLASS_INPUT; + ntype.geometry_node_execute = node_geo_exec; + ntype.declare = node_declare; + blender::bke::node_register_type(ntype); +} +NOD_REGISTER_NODE(node_register) + +} // namespace blender::nodes::node_geo_camera_info_cc diff --git a/source/blender/nodes/intern/geometry_nodes_dependencies.cc b/source/blender/nodes/intern/geometry_nodes_dependencies.cc index de3d584dde8..00996235a8c 100644 --- a/source/blender/nodes/intern/geometry_nodes_dependencies.cc +++ b/source/blender/nodes/intern/geometry_nodes_dependencies.cc @@ -44,6 +44,7 @@ void GeometryNodesEvalDependencies::add_object(Object *object, object_deps); deps.geometry |= object_deps.geometry; deps.transform |= object_deps.transform; + deps.camera_parameters |= object_deps.camera_parameters; } void GeometryNodesEvalDependencies::merge(const GeometryNodesEvalDependencies &other) @@ -58,6 +59,7 @@ void GeometryNodesEvalDependencies::merge(const GeometryNodesEvalDependencies &o } this->needs_own_transform |= other.needs_own_transform; this->needs_active_camera |= other.needs_active_camera; + this->needs_scene_render_params |= other.needs_scene_render_params; this->time_dependent |= other.time_dependent; } @@ -165,6 +167,20 @@ static void add_own_transform_dependencies(const bNodeTree &tree, deps.needs_own_transform |= needs_own_transform; } +static bool needs_scene_render_params(const bNodeTree &ntree) +{ + for (const bNode *node : ntree.nodes_by_type("GeometryNodeCameraInfo")) { + if (node->is_muted()) { + continue; + } + const bNodeSocket &projection_matrix_socket = node->output_by_identifier("Projection Matrix"); + if (projection_matrix_socket.is_logically_linked()) { + return true; + } + } + return false; +} + static void gather_geometry_nodes_eval_dependencies( const bNodeTree &ntree, GeometryNodesEvalDependencies &deps, @@ -175,6 +191,7 @@ static void gather_geometry_nodes_eval_dependencies( add_eval_dependencies_from_socket(*socket, deps); } deps.needs_active_camera |= has_enabled_nodes_of_type(ntree, "GeometryNodeInputActiveCamera"); + deps.needs_scene_render_params |= needs_scene_render_params(ntree); deps.time_dependent |= has_enabled_nodes_of_type(ntree, "GeometryNodeSimulationInput") || has_enabled_nodes_of_type(ntree, "GeometryNodeInputSceneTime");