From ce28af73520f62ec2f1c62b4efdb2fc7fafb43d0 Mon Sep 17 00:00:00 2001 From: Laurynas Duburas Date: Fri, 30 Aug 2024 18:11:14 +0200 Subject: [PATCH] Overlay-Next: Image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Overlay-Next version of image. Rel #102179 Co-authored-by: Clément Foucault Pull Request: https://projects.blender.org/blender/blender/pulls/126243 --- source/blender/draw/CMakeLists.txt | 1 + .../engines/overlay/overlay_next_camera.hh | 310 +++++++++++++++++- .../engines/overlay/overlay_next_empty.hh | 217 +++++++++++- .../engines/overlay/overlay_next_image.hh | 44 +++ .../engines/overlay/overlay_next_instance.cc | 25 +- .../engines/overlay/overlay_next_instance.hh | 1 + .../engines/overlay/overlay_next_private.hh | 26 ++ .../engines/overlay/overlay_next_shader.cc | 5 + .../overlay/shaders/overlay_image_frag.glsl | 3 + .../overlay/shaders/overlay_image_vert.glsl | 2 + .../draw/engines/select/select_instance.hh | 19 ++ 11 files changed, 632 insertions(+), 21 deletions(-) create mode 100644 source/blender/draw/engines/overlay/overlay_next_image.hh diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 256fa877caa..7453bce7327 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -300,6 +300,7 @@ set(SRC engines/overlay/overlay_next_force_field.hh engines/overlay/overlay_next_grease_pencil.hh engines/overlay/overlay_next_grid.hh + engines/overlay/overlay_next_image.hh engines/overlay/overlay_next_instance.hh engines/overlay/overlay_next_lattice.hh engines/overlay/overlay_next_light.hh diff --git a/source/blender/draw/engines/overlay/overlay_next_camera.hh b/source/blender/draw/engines/overlay/overlay_next_camera.hh index 95a2b2476f6..2f8a7d1b951 100644 --- a/source/blender/draw/engines/overlay/overlay_next_camera.hh +++ b/source/blender/draw/engines/overlay/overlay_next_camera.hh @@ -12,9 +12,10 @@ #include "DEG_depsgraph_query.hh" -#include "BKE_movieclip.h" #include "BKE_tracking.h" +#include "BLI_math_rotation.h" + #include "DNA_camera_types.h" #include "ED_view3d.hh" @@ -72,6 +73,22 @@ class Cameras { private: PassSimple ps_ = {"Cameras"}; + + /* Camera background images with "Depth" switched to "Back". + * Shown in camera view behind all objects. */ + PassMain background_ps_ = {"background_ps_"}; + /* Camera background images with "Depth" switched to "Front". + * Shown in camera view in front of all objects. */ + PassMain foreground_ps_ = {"foreground_ps_"}; + + /* Same as `background_ps_` with "View as Render" checked. */ + PassMain background_scene_ps_ = {"background_scene_ps_"}; + /* Same as `foreground_ps_` with "View as Render" checked. */ + PassMain foreground_scene_ps_ = {"foreground_scene_ps_"}; + + View view_reference_images = {"view_reference_images"}; + float view_dist = 0.0f; + struct CallBuffers { const SelectionType selection_type_; CameraInstanceBuf distances_buf = {selection_type_, "camera_distances_buf"}; @@ -123,7 +140,9 @@ class Cameras { const float4x4 object_to_world{ob->object_to_world().ptr()}; - LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { + for (MovieTrackingObject *tracking_object : + ListBaseWrapper(&tracking->objects)) + { float4x4 tracking_object_mat; if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { @@ -140,7 +159,9 @@ class Cameras { tracking_object_mat = object_to_world * math::invert(object_mat); } - LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) { + for (MovieTrackingTrack *track : + ListBaseWrapper(&tracking_object->tracks)) + { if ((track->flag & TRACK_HAS_BUNDLE) == 0) { continue; } @@ -343,8 +364,10 @@ class Cameras { public: Cameras(const SelectionType selection_type) : call_buffers_{selection_type} {}; - void begin_sync() + void begin_sync(Resources &res, State &state, View &view) { + view_dist = state.view_dist_get(view.winmat()); + call_buffers_.distances_buf.clear(); call_buffers_.frame_buf.clear(); call_buffers_.tria_buf.clear(); @@ -355,9 +378,29 @@ class Cameras { call_buffers_.stereo_connect_lines.clear(); call_buffers_.tracking_path.clear(); Empties::begin_sync(call_buffers_.empties); + + /* Init image passes. */ + auto init_pass = [&](PassMain &pass, DRWState draw_state) { + pass.init(); + pass.state_set(draw_state | state.clipping_state); + pass.shader_set(res.shaders.image_plane.get()); + res.select_bind(pass); + }; + + DRWState draw_state; + draw_state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_GREATER | DRW_STATE_BLEND_ALPHA_PREMUL; + init_pass(background_ps_, draw_state); + + draw_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_UNDER_PREMUL; + init_pass(background_scene_ps_, draw_state); + + draw_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; + init_pass(foreground_ps_, draw_state); + init_pass(foreground_scene_ps_, draw_state); } - void object_sync(const ObjectRef &ob_ref, Resources &res, State &state) + void object_sync( + const ObjectRef &ob_ref, ShapeCache &shapes, Manager &manager, Resources &res, State &state) { Object *ob = ob_ref.object; const select::ID select_id = res.select_id(ob_ref); @@ -499,12 +542,12 @@ class Cameras { call_buffers_); } - // TODO: /* Background images. */ - // if (look_through && (cam->flag & CAM_SHOW_BG_IMAGE) && - // !BLI_listbase_is_empty(&cam->bg_images)) - // { - // OVERLAY_image_camera_cache_populate(vedata, ob); - // } + if (is_camera_view && (cam->flag & CAM_SHOW_BG_IMAGE) && + !BLI_listbase_is_empty(&cam->bg_images)) + { + sync_camera_images( + ob_ref, select_id, shapes, manager, state, res, call_buffers_.selection_type_); + } } void end_sync(Resources &res, ShapeCache &shapes, const State &state) @@ -561,6 +604,251 @@ class Cameras { GPU_framebuffer_bind(framebuffer); manager.submit(ps_, view); } + + void draw_scene_background_images(Framebuffer &framebuffer, + const State &state, + Manager &manager, + View &view) + { + if (state.space_type != SPACE_VIEW3D) { + return; + } + + GPU_framebuffer_bind(framebuffer); + + manager.submit(background_scene_ps_, view); + manager.submit(foreground_scene_ps_, view); + } + + void draw_background_images(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + manager.submit(background_ps_, view); + } + + void draw_in_front(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + + view_reference_images.sync(view.viewmat(), + winmat_polygon_offset(view.winmat(), view_dist, -1.0f)); + + manager.submit(foreground_ps_, view_reference_images); + } + + private: + void sync_camera_images(const ObjectRef &ob_ref, + select::ID select_id, + ShapeCache &shapes, + Manager &manager, + const State &state, + Resources &res, + const SelectionType selection_type) + { + Object *ob = ob_ref.object; + const Camera *cam = static_cast(ob->data); + + const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, state.rv3d); + + if (!show_frame || selection_type != SelectionType::DISABLED) { + return; + } + + const bool stereo_eye = Images::images_stereo_eye(state.scene, state.v3d) == STEREO_LEFT_ID; + const char *viewname = (stereo_eye == STEREO_LEFT_ID) ? STEREO_RIGHT_NAME : STEREO_LEFT_NAME; + float4x4 modelmat; + BKE_camera_multiview_model_matrix(&state.scene->r, ob, viewname, modelmat.ptr()); + + for (const CameraBGImage *bgpic : ConstListBaseWrapper(&cam->bg_images)) { + if (bgpic->flag & CAM_BGIMG_FLAG_DISABLED) { + continue; + } + + float aspect = 1.0; + bool use_alpha_premult; + bool use_view_transform = false; + float4x4 mat; + + /* retrieve the image we want to show, continue to next when no image could be found */ + GPUTexture *tex = image_camera_background_texture_get( + bgpic, state, res, aspect, use_alpha_premult, use_view_transform); + + if (tex) { + image_camera_background_matrix_get(cam, bgpic, state, aspect, mat); + + const bool is_foreground = (bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != 0; + /* Alpha is clamped just below 1.0 to fix background images to interfere with foreground + * images. Without this a background image with 1.0 will be rendered on top of a + * transparent foreground image due to the different blending modes they use. */ + const float4 color_premult_alpha{1.0f, 1.0f, 1.0f, std::min(bgpic->alpha, 0.999999f)}; + + PassMain &pass = is_foreground ? + (use_view_transform ? foreground_scene_ps_ : foreground_ps_) : + (use_view_transform ? background_scene_ps_ : background_ps_); + pass.bind_texture("imgTexture", tex); + pass.push_constant("imgPremultiplied", use_alpha_premult); + pass.push_constant("imgAlphaBlend", true); + pass.push_constant("isCameraBackground", true); + pass.push_constant("depthSet", true); + pass.push_constant("ucolor", color_premult_alpha); + ResourceHandle res_handle = manager.resource_handle(mat); + pass.draw(shapes.quad_solid.get(), res_handle, select_id.get()); + } + } + } + + static void image_camera_background_matrix_get(const Camera *cam, + const CameraBGImage *bgpic, + const State &state, + const float image_aspect, + float4x4 &rmat) + { + float4x4 rotate, scale = float4x4::identity(), translate = float4x4::identity(); + + axis_angle_to_mat4_single(rotate.ptr(), 'Z', -bgpic->rotation); + + /* Normalized Object space camera frame corners. */ + float cam_corners[4][3]; + BKE_camera_view_frame(state.scene, cam, cam_corners); + float cam_width = fabsf(cam_corners[0][0] - cam_corners[3][0]); + float cam_height = fabsf(cam_corners[0][1] - cam_corners[1][1]); + float cam_aspect = cam_width / cam_height; + + if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_CROP) { + /* Crop. */ + if (image_aspect > cam_aspect) { + scale[0][0] *= cam_height * image_aspect; + scale[1][1] *= cam_height; + } + else { + scale[0][0] *= cam_width; + scale[1][1] *= cam_width / image_aspect; + } + } + else if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_ASPECT) { + /* Fit. */ + if (image_aspect > cam_aspect) { + scale[0][0] *= cam_width; + scale[1][1] *= cam_width / image_aspect; + } + else { + scale[0][0] *= cam_height * image_aspect; + scale[1][1] *= cam_height; + } + } + else { + /* Stretch. */ + scale[0][0] *= cam_width; + scale[1][1] *= cam_height; + } + + translate[3][0] = bgpic->offset[0]; + translate[3][1] = bgpic->offset[1]; + translate[3][2] = cam_corners[0][2]; + if (cam->type == CAM_ORTHO) { + translate[3].xy() *= cam->ortho_scale; + } + /* These lines are for keeping 2.80 behavior and could be removed to keep 2.79 behavior. */ + translate[3][0] *= min_ff(1.0f, cam_aspect); + translate[3][1] /= max_ff(1.0f, cam_aspect) * (image_aspect / cam_aspect); + /* quad is -1..1 so divide by 2. */ + scale[0][0] *= 0.5f * bgpic->scale * ((bgpic->flag & CAM_BGIMG_FLAG_FLIP_X) ? -1.0 : 1.0); + scale[1][1] *= 0.5f * bgpic->scale * ((bgpic->flag & CAM_BGIMG_FLAG_FLIP_Y) ? -1.0 : 1.0); + /* Camera shift. (middle of cam_corners) */ + translate[3][0] += (cam_corners[0][0] + cam_corners[2][0]) * 0.5f; + translate[3][1] += (cam_corners[0][1] + cam_corners[2][1]) * 0.5f; + + rmat = translate * rotate * scale; + } + + GPUTexture *image_camera_background_texture_get(const CameraBGImage *bgpic, + const State &state, + Resources &res, + float &r_aspect, + bool &r_use_alpha_premult, + bool &r_use_view_transform) + { + ::Image *image = bgpic->ima; + ImageUser *iuser = (ImageUser *)&bgpic->iuser; + MovieClip *clip = nullptr; + GPUTexture *tex = nullptr; + float aspect_x, aspect_y; + int width, height; + int ctime = int(DEG_get_ctime(state.depsgraph)); + r_use_alpha_premult = false; + r_use_view_transform = false; + + switch (bgpic->source) { + case CAM_BGIMG_SOURCE_IMAGE: { + if (image == nullptr) { + return nullptr; + } + r_use_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); + r_use_view_transform = (image->flag & IMA_VIEW_AS_RENDER) != 0; + + BKE_image_user_frame_calc(image, iuser, ctime); + if (image->source == IMA_SRC_SEQUENCE && !(iuser->flag & IMA_USER_FRAME_IN_RANGE)) { + /* Frame is out of range, don't show. */ + return nullptr; + } + + Images::stereo_setup(state.scene, state.v3d, image, iuser); + + iuser->scene = (Scene *)state.scene; + tex = BKE_image_get_gpu_viewer_texture(image, iuser); + iuser->scene = nullptr; + + if (tex == nullptr) { + return nullptr; + } + + width = GPU_texture_original_width(tex); + height = GPU_texture_original_height(tex); + + aspect_x = bgpic->ima->aspx; + aspect_y = bgpic->ima->aspy; + break; + } + + case CAM_BGIMG_SOURCE_MOVIE: { + if (bgpic->flag & CAM_BGIMG_FLAG_CAMERACLIP) { + if (state.scene->camera) { + clip = BKE_object_movieclip_get((Scene *)state.scene, state.scene->camera, true); + } + } + else { + clip = bgpic->clip; + } + + if (clip == nullptr) { + return nullptr; + } + + BKE_movieclip_user_set_frame((MovieClipUser *)&bgpic->cuser, ctime); + tex = BKE_movieclip_get_gpu_texture(clip, (MovieClipUser *)&bgpic->cuser); + if (tex == nullptr) { + return nullptr; + } + + aspect_x = clip->aspx; + aspect_y = clip->aspy; + r_use_view_transform = true; + + BKE_movieclip_get_size(clip, &bgpic->cuser, &width, &height); + + /* Save for freeing. */ + res.bg_movie_clips.append(clip); + break; + } + + default: + /* Unsupported type. */ + return nullptr; + } + + r_aspect = (width * aspect_x) / (height * aspect_y); + return tex; + } }; } // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_empty.hh b/source/blender/draw/engines/overlay/overlay_next_empty.hh index 1a5fa21518f..6fbfc10666f 100644 --- a/source/blender/draw/engines/overlay/overlay_next_empty.hh +++ b/source/blender/draw/engines/overlay/overlay_next_empty.hh @@ -8,6 +8,7 @@ #pragma once +#include "overlay_next_image.hh" #include "overlay_next_private.hh" namespace blender::draw::overlay { @@ -17,8 +18,25 @@ class Empties { using EmptyInstanceBuf = ShapeInstanceBuf; private: + /* Images added by Image > Background. Both added in preset view (like Top, Front, ..) and in + * custom view. Object property "In Front" unchecked. */ + PassSortable images_back_ps_ = {"images_back_ps_"}; + /* All Empty images from cases of `images_ps_`, `images_blend_ps_`, `images_back_ps_` + * with object property "In Front" checked. */ + PassSortable images_front_ps_ = {"images_front_ps_"}; + + /* Images added by Empty > Image and Image > Reference with unchecked image "Opacity". + * Object property "In Front" unchecked. */ + PassMain images_ps_ = {"images_ps_"}; + /* Images added by Empty > Image and Image > Reference with image "Opacity" checked. + * Object property "In Front" unchecked. */ + PassSortable images_blend_ps_ = {"images_blend_ps_"}; + PassSimple ps_ = {"Empties"}; + View view_reference_images = {"view_reference_images"}; + float view_dist = 0.0f; + struct CallBuffers { const SelectionType selection_type_; EmptyInstanceBuf plain_axes_buf = {selection_type_, "plain_axes_buf"}; @@ -34,8 +52,36 @@ class Empties { public: Empties(const SelectionType selection_type) : call_buffers_{selection_type} {}; - void begin_sync() + void begin_sync(Resources &res, const State &state, const View &view) { + view_dist = state.view_dist_get(view.winmat()); + + auto init_pass = [&](PassMain &pass, DRWState draw_state) { + pass.init(); + pass.state_set(draw_state | state.clipping_state); + pass.shader_set(res.shaders.image_plane.get()); + res.select_bind(pass); + }; + + auto init_sortable = [&](PassSortable &pass, DRWState draw_state) { + pass.init(); + PassMain::Sub &sub = pass.sub("ResourceBind", -FLT_MAX); + sub.state_set(draw_state | state.clipping_state); + res.select_bind(pass, sub); + }; + + DRWState draw_state; + + draw_state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + init_pass(images_ps_, draw_state); + + draw_state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA_PREMUL; + init_sortable(images_back_ps_, draw_state); + init_sortable(images_blend_ps_, draw_state); + + draw_state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL; + init_sortable(images_front_ps_, draw_state); + begin_sync(call_buffers_); } @@ -51,10 +97,18 @@ class Empties { call_buffers.image_buf.clear(); } - void object_sync(const ObjectRef &ob_ref, Resources &res, const State &state) + void object_sync(const ObjectRef &ob_ref, + ShapeCache &shapes, + Manager &manager, + Resources &res, + const State &state) { const float4 color = res.object_wire_color(ob_ref, state); const select::ID select_id = res.select_id(ob_ref); + if (ob_ref.object->empty_drawtype == OB_EMPTY_IMAGE) { + image_sync(ob_ref, select_id, shapes, manager, res, state, call_buffers_.image_buf); + return; + } object_sync(select_id, ob_ref.object->object_to_world(), ob_ref.object->empty_drawsize, @@ -94,10 +148,6 @@ class Empties { case OB_ARROWS: call_buffers.arrows_buf.append(data, select_id); break; - case OB_EMPTY_IMAGE: - /* This only show the frame. See OVERLAY_image_empty_cache_populate() for the image. */ - call_buffers.image_buf.append(data, select_id); - break; } } @@ -134,6 +184,161 @@ class Empties { GPU_framebuffer_bind(framebuffer); manager.submit(ps_, view); } + + void draw_background_images(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + manager.submit(images_back_ps_, view); + } + + void draw_images(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + + view_reference_images.sync(view.viewmat(), + winmat_polygon_offset(view.winmat(), view_dist, -1.0f)); + + manager.submit(images_ps_, view_reference_images); + manager.submit(images_blend_ps_, view_reference_images); + } + + void draw_in_front_images(Framebuffer &framebuffer, Manager &manager, View &view) + { + GPU_framebuffer_bind(framebuffer); + + view_reference_images.sync(view.viewmat(), + winmat_polygon_offset(view.winmat(), view_dist, -1.0f)); + + manager.submit(images_front_ps_, view_reference_images); + } + + private: + void image_sync(const ObjectRef &ob_ref, + select::ID select_id, + ShapeCache &shapes, + Manager &manager, + Resources &res, + const State &state, + EmptyInstanceBuf &empty_image_buf) + { + Object *ob = ob_ref.object; + GPUTexture *tex = nullptr; + ::Image *ima = static_cast<::Image *>(ob_ref.object->data); + float4x4 mat; + + const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, state.rv3d); + const bool show_image = show_frame && + BKE_object_empty_image_data_is_visible_in_view3d(ob, state.rv3d); + const bool use_alpha_blend = (ob_ref.object->empty_image_flag & + OB_EMPTY_IMAGE_USE_ALPHA_BLEND) != 0; + const bool use_alpha_premult = ima && (ima->alpha_mode == IMA_ALPHA_PREMUL); + + if (!show_frame) { + return; + } + + { + /* Calling 'BKE_image_get_size' may free the texture. Get the size from 'tex' instead, + * see: #59347 */ + int2 size = int2(0); + if (ima != nullptr) { + ImageUser iuser = *ob->iuser; + Images::stereo_setup(state.scene, state.v3d, ima, &iuser); + tex = BKE_image_get_gpu_texture(ima, &iuser); + if (tex) { + size = int2(GPU_texture_original_width(tex), GPU_texture_original_height(tex)); + } + } + CLAMP_MIN(size.x, 1); + CLAMP_MIN(size.y, 1); + + float2 image_aspect; + calc_image_aspect(ima, size, image_aspect); + + mat = ob->object_to_world(); + mat.x_axis() *= image_aspect.x * 0.5f * ob->empty_drawsize; + mat.y_axis() *= image_aspect.y * 0.5f * ob->empty_drawsize; + mat[3] += float4(mat.x_axis() * (ob->ima_ofs[0] * 2.0f + 1.0f) + + mat.y_axis() * (ob->ima_ofs[1] * 2.0f + 1.0f)); + } + + if (show_frame) { + const float4 color = res.object_wire_color(ob_ref, state); + empty_image_buf.append(ExtraInstanceData(mat, color, 1.0f), select_id); + } + + if (show_image && tex && ((ob->color[3] > 0.0f) || !use_alpha_blend)) { + /* Use the actual depth if we are doing depth tests to determine the distance to the + * object. */ + char depth_mode = DRW_state_is_depth() ? char(OB_EMPTY_IMAGE_DEPTH_DEFAULT) : + ob->empty_image_depth; + PassMain::Sub &pass = create_subpass(state, *ob, use_alpha_blend, mat, res); + pass.bind_texture("imgTexture", tex); + pass.push_constant("imgPremultiplied", use_alpha_premult); + pass.push_constant("imgAlphaBlend", use_alpha_blend); + pass.push_constant("isCameraBackground", false); + pass.push_constant("depthSet", depth_mode != OB_EMPTY_IMAGE_DEPTH_DEFAULT); + pass.push_constant("ucolor", float4(ob->color)); + ResourceHandle res_handle = manager.resource_handle(mat); + pass.draw(shapes.quad_solid.get(), res_handle, select_id.get()); + } + } + + PassMain::Sub &create_subpass(const State &state, + const Object &ob, + const bool use_alpha_blend, + const float4x4 &mat, + const Resources &res) + { + const bool in_front = state.use_in_front && (ob.dtx & OB_DRAW_IN_FRONT); + if (in_front) { + return create_subpass(state, mat, res, images_front_ps_); + } + const char depth_mode = DRW_state_is_depth() ? char(OB_EMPTY_IMAGE_DEPTH_DEFAULT) : + ob.empty_image_depth; + switch (depth_mode) { + case OB_EMPTY_IMAGE_DEPTH_BACK: + return create_subpass(state, mat, res, images_back_ps_); + case OB_EMPTY_IMAGE_DEPTH_FRONT: + return create_subpass(state, mat, res, images_front_ps_); + case OB_EMPTY_IMAGE_DEPTH_DEFAULT: + default: + return use_alpha_blend ? create_subpass(state, mat, res, images_blend_ps_) : images_ps_; + } + } + + static PassMain::Sub &create_subpass(const State &state, + const float4x4 &mat, + const Resources &res, + PassSortable &parent) + { + const float3 tmp = state.camera_position - mat.location(); + const float z = -math::dot(state.camera_forward, tmp); + PassMain::Sub &sub = parent.sub("Sub", z); + sub.shader_set(res.shaders.image_plane.get()); + return sub; + }; + + static void calc_image_aspect(::Image *ima, const int2 &size, float2 &r_image_aspect) + { + /* if no image, make it a 1x1 empty square, honor scale & offset */ + const float2 ima_dim = ima ? float2(size.x, size.y) : float2(1.0f); + + /* Get the image aspect even if the buffer is invalid */ + float2 sca(1.0f); + if (ima) { + if (ima->aspx > ima->aspy) { + sca.y = ima->aspy / ima->aspx; + } + else if (ima->aspx < ima->aspy) { + sca.x = ima->aspx / ima->aspy; + } + } + + const float2 scale_inv(ima_dim.x * sca.x, ima_dim.y * sca.y); + r_image_aspect = (scale_inv.x > scale_inv.y) ? float2(1.0f, scale_inv.y / scale_inv.x) : + float2(scale_inv.x / scale_inv.y, 1.0f); + } }; } // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_image.hh b/source/blender/draw/engines/overlay/overlay_next_image.hh new file mode 100644 index 00000000000..706e60b6b59 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_next_image.hh @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup overlay + */ + +#pragma once + +#include "overlay_next_private.hh" + +namespace blender::draw::overlay { + +class Images { + + public: + static eStereoViews images_stereo_eye(const Scene *scene, const View3D *v3d) + { + if ((scene->r.scemode & R_MULTIVIEW) == 0) { + return STEREO_LEFT_ID; + } + if (v3d->stereo3d_camera != STEREO_3D_ID) { + /* show only left or right camera */ + return eStereoViews(v3d->stereo3d_camera); + } + + return eStereoViews(v3d->multiview_eye); + } + + static void stereo_setup(const Scene *scene, const View3D *v3d, ::Image *ima, ImageUser *iuser) + { + if (BKE_image_is_stereo(ima)) { + iuser->flag |= IMA_SHOW_STEREO; + iuser->multiview_eye = images_stereo_eye(scene, v3d); + BKE_image_multiview_index(ima, iuser); + } + else { + iuser->flag &= ~IMA_SHOW_STEREO; + } + } +}; + +} // namespace blender::draw::overlay diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.cc b/source/blender/draw/engines/overlay/overlay_next_instance.cc index 7f0c37bdc0d..76807043fc8 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.cc +++ b/source/blender/draw/engines/overlay/overlay_next_instance.cc @@ -84,6 +84,9 @@ void Instance::begin_sync() const DRWView *view_legacy = DRW_view_default_get(); View view("OverlayView", view_legacy); + state.camera_position = view.viewinv().location(); + state.camera_forward = view.viewinv().z_axis(); + resources.begin_sync(); background.begin_sync(resources, state); @@ -91,9 +94,9 @@ void Instance::begin_sync() auto begin_sync_layer = [&](OverlayLayer &layer) { layer.bounds.begin_sync(); - layer.cameras.begin_sync(); + layer.cameras.begin_sync(resources, state, view); layer.curves.begin_sync(resources, state, view); - layer.empties.begin_sync(); + layer.empties.begin_sync(resources, state, view); layer.facing.begin_sync(resources, state); layer.force_fields.begin_sync(); layer.fluids.begin_sync(resources, state); @@ -166,10 +169,10 @@ void Instance::object_sync(ObjectRef &ob_ref, Manager &manager) if (!state.hide_overlays) { switch (ob_ref.object->type) { case OB_EMPTY: - layer.empties.object_sync(ob_ref, resources, state); + layer.empties.object_sync(ob_ref, shapes, manager, resources, state); break; case OB_CAMERA: - layer.cameras.object_sync(ob_ref, resources, state); + layer.cameras.object_sync(ob_ref, shapes, manager, resources, state); break; case OB_ARMATURE: break; @@ -323,6 +326,17 @@ void Instance::draw(Manager &manager) GPU_framebuffer_clear_color(resources.overlay_line_fb, clear_color); } + regular.cameras.draw_scene_background_images( + resources.overlay_color_only_fb, state, manager, view); + infront.cameras.draw_scene_background_images( + resources.overlay_color_only_fb, state, manager, view); + + regular.empties.draw_background_images(resources.overlay_color_only_fb, manager, view); + regular.cameras.draw_background_images(resources.overlay_color_only_fb, manager, view); + infront.cameras.draw_background_images(resources.overlay_color_only_fb, manager, view); + + regular.empties.draw_images(resources.overlay_fb, manager, view); + regular.prepass.draw(resources.overlay_line_fb, manager, view); infront.prepass.draw(resources.overlay_line_in_front_fb, manager, view); @@ -367,6 +381,9 @@ void Instance::draw(Manager &manager) /* TODO(: Breaks selection on M1 Max. */ // infront.lattices.draw(resources.overlay_line_in_front_fb, manager, view); + // infront.empties.draw_in_front_images(resources.overlay_in_front_fb, manager, view); + // regular.cameras.draw_in_front(resources.overlay_in_front_fb, manager, view); + // infront.cameras.draw_in_front(resources.overlay_in_front_fb, manager, view); /* Drawn onto the output framebuffer. */ background.draw(manager); diff --git a/source/blender/draw/engines/overlay/overlay_next_instance.hh b/source/blender/draw/engines/overlay/overlay_next_instance.hh index 393acc1f221..76e46a69cfb 100644 --- a/source/blender/draw/engines/overlay/overlay_next_instance.hh +++ b/source/blender/draw/engines/overlay/overlay_next_instance.hh @@ -60,6 +60,7 @@ class Instance { struct OverlayLayer { const SelectionType selection_type_; + Bounds bounds = {selection_type_}; Cameras cameras = {selection_type_}; Curves curves; diff --git a/source/blender/draw/engines/overlay/overlay_next_private.hh b/source/blender/draw/engines/overlay/overlay_next_private.hh index 32e2f53b2ca..7e1d0d22397 100644 --- a/source/blender/draw/engines/overlay/overlay_next_private.hh +++ b/source/blender/draw/engines/overlay/overlay_next_private.hh @@ -8,6 +8,8 @@ #pragma once +#include "BKE_movieclip.h" + #include "BLI_function_ref.hh" #include "GPU_matrix.hh" @@ -57,6 +59,8 @@ struct State { short v3d_flag; /* TODO: move to #View3DOverlay. */ short v3d_gridflag; /* TODO: move to #View3DOverlay. */ int cfra; + float3 camera_position; + float3 camera_forward; DRWState clipping_state; float view_dist_get(const float4x4 &winmat) const @@ -212,6 +216,7 @@ class ShaderModule { ShaderPtr fluid_velocity_streamline; ShaderPtr fluid_velocity_mac; ShaderPtr fluid_velocity_needle; + ShaderPtr image_plane; ShaderPtr lattice_points; ShaderPtr lattice_wire; ShaderPtr particle_dot; @@ -299,9 +304,22 @@ struct Resources : public select::SelectMap { */ TextureRef depth_target_tx; + Vector bg_movie_clips; + Resources(const SelectionType selection_type_, ShaderModule &shader_module) : select::SelectMap(selection_type_), shaders(shader_module){}; + ~Resources() + { + free_movieclips_textures(); + } + + void begin_sync() + { + SelectMap::begin_sync(); + free_movieclips_textures(); + } + ThemeColorID object_wire_theme_id(const ObjectRef &ob_ref, const State &state) const { const bool is_edit = (state.object_mode & OB_MODE_EDIT) && @@ -386,6 +404,14 @@ struct Resources : public select::SelectMap { ThemeColorID theme_id = object_wire_theme_id(ob_ref, state); return background_blend_color(theme_id); } + + void free_movieclips_textures() + { + /* Free Movie clip textures after rendering */ + for (MovieClip *clip : bg_movie_clips) { + BKE_movieclip_free_gputexture(clip); + } + } }; /** diff --git a/source/blender/draw/engines/overlay/overlay_next_shader.cc b/source/blender/draw/engines/overlay/overlay_next_shader.cc index 474d6d4af65..c4224373ea7 100644 --- a/source/blender/draw/engines/overlay/overlay_next_shader.cc +++ b/source/blender/draw/engines/overlay/overlay_next_shader.cc @@ -331,6 +331,11 @@ ShaderModule::ShaderModule(const SelectionType selection_type, const bool clippi info.vertex_inputs_.pop_last(); }); + image_plane = selectable_shader("overlay_image", [](gpu::shader::ShaderCreateInfo &info) { + info.additional_infos_.clear(); + info.additional_info("draw_view", "draw_modelmat_new", "draw_resource_handle_new"); + }); + particle_dot = selectable_shader("overlay_particle_dot", [](gpu::shader::ShaderCreateInfo &info) { info.additional_infos_.clear(); diff --git a/source/blender/draw/engines/overlay/shaders/overlay_image_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_image_frag.glsl index a80d3fbc5b0..f3276523cd6 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_image_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_image_frag.glsl @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma BLENDER_REQUIRE(common_colormanagement_lib.glsl) +#pragma BLENDER_REQUIRE(select_lib.glsl) void main() { @@ -25,4 +26,6 @@ void main() /* Pre-multiplied blending. */ fragColor.rgb *= fragColor.a; + + select_id_output(select_id); } diff --git a/source/blender/draw/engines/overlay/shaders/overlay_image_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_image_vert.glsl index f9e2fdd014f..9e51b0f3fc8 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_image_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_image_vert.glsl @@ -4,9 +4,11 @@ #pragma BLENDER_REQUIRE(common_view_clipping_lib.glsl) #pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(select_lib.glsl) void main() { + select_id_set(drw_CustomID); vec3 world_pos = point_object_to_world(pos); if (isCameraBackground) { /* Model matrix converts to view position to avoid jittering (see #91398). */ diff --git a/source/blender/draw/engines/select/select_instance.hh b/source/blender/draw/engines/select/select_instance.hh index a5962e20f4d..073b58dfc2c 100644 --- a/source/blender/draw/engines/select/select_instance.hh +++ b/source/blender/draw/engines/select/select_instance.hh @@ -191,6 +191,25 @@ struct SelectMap { pass.bind_ssbo(SELECT_ID_OUT, &select_output_buf); } + /* TODO: Deduplicate. */ + /** IMPORTANT: Changes the draw state. Need to be called after the pass's own state_set. */ + void select_bind(PassMain &pass, PassMain::Sub &sub) + { + if (selection_type == SelectionType::DISABLED) { + return; + } + + pass.use_custom_ids = true; + if (disable_depth_test) { + /* TODO: clipping state. */ + sub.state_set(DRW_STATE_WRITE_COLOR); + } + sub.bind_ubo(SELECT_DATA, &info_buf); + /* IMPORTANT: This binds a dummy buffer `in_select_buf` but it is not supposed to be used. */ + sub.bind_ssbo(SELECT_ID_IN, &dummy_select_buf); + sub.bind_ssbo(SELECT_ID_OUT, &select_output_buf); + } + void end_sync() { if (selection_type == SelectionType::DISABLED) {