/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2019 Blender Foundation. */ /** \file * \ingroup draw_engine */ #include "DRW_render.h" #include "UI_resources.h" #include "BKE_anim_path.h" #include "BKE_camera.h" #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_global.h" #include "BKE_mball.h" #include "BKE_mesh.h" #include "BKE_modifier.h" #include "BKE_movieclip.h" #include "BKE_object.h" #include "BKE_tracking.h" #include "BLI_listbase.h" #include "DNA_camera_types.h" #include "DNA_constraint_types.h" #include "DNA_curve_types.h" #include "DNA_fluid_types.h" #include "DNA_lightprobe_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" #include "DNA_modifier_types.h" #include "DNA_pointcache_types.h" #include "DNA_rigidbody_types.h" #include "DEG_depsgraph_query.h" #include "ED_view3d.h" #include "overlay_private.hh" #include "draw_common.h" #include "draw_manager_text.h" void OVERLAY_extra_cache_init(OVERLAY_Data *vedata) { OVERLAY_PassList *psl = vedata->psl; OVERLAY_TextureList *txl = vedata->txl; OVERLAY_PrivateData *pd = vedata->stl->pd; const bool is_select = DRW_state_is_select(); DRWState state_blend = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; DRW_PASS_CREATE(psl->extra_blend_ps, state_blend | pd->clipping_state); DRW_PASS_CREATE(psl->extra_centers_ps, state_blend | pd->clipping_state); { DRWState state = DRW_STATE_WRITE_COLOR; DRW_PASS_CREATE(psl->extra_grid_ps, state | pd->clipping_state); DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); DRWShadingGroup *grp; GPUShader *sh = OVERLAY_shader_extra_grid(); GPUTexture *tex = DRW_state_is_fbo() ? dtxl->depth : txl->dummy_depth_tx; pd->extra_grid_grp = grp = DRW_shgroup_create(sh, psl->extra_grid_ps); DRW_shgroup_uniform_texture(grp, "depthBuffer", tex); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); } for (int i = 0; i < 2; i++) { /* Non Meshes Pass (Camera, empties, lights ...) */ GPUShader *sh; GPUVertFormat *format; DRWShadingGroup *grp, *grp_sub; OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get(); OVERLAY_ExtraCallBuffers *cb = &pd->extra_call_buffers[i]; DRWPass **p_extra_ps = &psl->extra_ps[i]; DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : DRWState(0); DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; DRW_PASS_CREATE(*p_extra_ps, state | pd->clipping_state | infront_state); DRWPass *extra_ps = *p_extra_ps; #define BUF_INSTANCE DRW_shgroup_call_buffer_instance #define BUF_POINT(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_POINTS) #define BUF_LINE(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_LINES) /* Sorted by shader to avoid state changes during render. */ { format = formats->instance_extra; sh = OVERLAY_shader_extra(is_select); grp = DRW_shgroup_create(sh, extra_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); grp_sub = DRW_shgroup_create_sub(grp); cb->camera_distances = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_distances_get()); cb->camera_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_frame_get()); cb->camera_tria[0] = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_tria_wire_get()); cb->camera_tria[1] = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_tria_get()); cb->empty_axes = BUF_INSTANCE(grp_sub, format, DRW_cache_bone_arrows_get()); cb->empty_capsule_body = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_capsule_body_get()); cb->empty_capsule_cap = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_capsule_cap_get()); cb->empty_circle = BUF_INSTANCE(grp_sub, format, DRW_cache_circle_get()); cb->empty_cone = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_cone_get()); cb->empty_cube = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_cube_get()); cb->empty_cylinder = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_cylinder_get()); cb->empty_image_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_quad_wires_get()); cb->empty_plain_axes = BUF_INSTANCE(grp_sub, format, DRW_cache_plain_axes_get()); cb->empty_single_arrow = BUF_INSTANCE(grp_sub, format, DRW_cache_single_arrow_get()); cb->empty_sphere = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_sphere_get()); cb->empty_sphere_solid = BUF_INSTANCE(grp_sub, format, DRW_cache_sphere_get(DRW_LOD_LOW)); cb->field_cone_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_cone_limit_get()); cb->field_curve = BUF_INSTANCE(grp_sub, format, DRW_cache_field_curve_get()); cb->field_force = BUF_INSTANCE(grp_sub, format, DRW_cache_field_force_get()); cb->field_sphere_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_sphere_limit_get()); cb->field_tube_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_tube_limit_get()); cb->field_vortex = BUF_INSTANCE(grp_sub, format, DRW_cache_field_vortex_get()); cb->field_wind = BUF_INSTANCE(grp_sub, format, DRW_cache_field_wind_get()); cb->light_area[0] = BUF_INSTANCE(grp_sub, format, DRW_cache_light_area_disk_lines_get()); cb->light_area[1] = BUF_INSTANCE(grp_sub, format, DRW_cache_light_area_square_lines_get()); cb->light_point = BUF_INSTANCE(grp_sub, format, DRW_cache_light_point_lines_get()); cb->light_spot = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_lines_get()); cb->light_sun = BUF_INSTANCE(grp_sub, format, DRW_cache_light_sun_lines_get()); cb->probe_cube = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_cube_get()); cb->probe_grid = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_grid_get()); cb->probe_planar = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_planar_get()); cb->solid_quad = BUF_INSTANCE(grp_sub, format, DRW_cache_quad_get()); cb->speaker = BUF_INSTANCE(grp_sub, format, DRW_cache_speaker_get()); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_state_enable(grp_sub, DRW_STATE_DEPTH_ALWAYS); DRW_shgroup_state_disable(grp_sub, DRW_STATE_DEPTH_LESS_EQUAL); cb->origin_xform = BUF_INSTANCE(grp_sub, format, DRW_cache_bone_arrows_get()); } { format = formats->instance_extra; grp = DRW_shgroup_create(sh, psl->extra_blend_ps); /* NOTE: not the same pass! */ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_state_enable(grp_sub, DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK); cb->camera_volume = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_volume_get()); cb->camera_volume_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_volume_wire_get()); cb->light_spot_cone_back = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_volume_get()); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_state_enable(grp_sub, DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_FRONT); cb->light_spot_cone_front = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_volume_get()); } { format = formats->instance_pos; sh = OVERLAY_shader_extra_groundline(); grp = DRW_shgroup_create(sh, extra_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); cb->groundline = BUF_INSTANCE(grp, format, DRW_cache_groundline_get()); } { sh = OVERLAY_shader_extra_wire(false, is_select); grp = DRW_shgroup_create(sh, extra_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); cb->extra_dashed_lines = BUF_LINE(grp, formats->pos_color); cb->extra_lines = BUF_LINE(grp, formats->wire_extra); } { sh = OVERLAY_shader_extra_wire(true, is_select); cb->extra_wire = grp = DRW_shgroup_create(sh, extra_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); } { sh = OVERLAY_shader_extra_loose_point(); cb->extra_loose_points = grp = DRW_shgroup_create(sh, extra_ps); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); /* Buffer access for drawing isolated points, matching `extra_lines`. */ cb->extra_points = BUF_POINT(grp, formats->point_extra); } { format = formats->pos; sh = OVERLAY_shader_extra_point(); grp = DRW_shgroup_create(sh, psl->extra_centers_ps); /* NOTE: not the same pass! */ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_uniform_vec4_copy(grp_sub, "ucolor", G_draw.block.color_active); cb->center_active = BUF_POINT(grp_sub, format); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_uniform_vec4_copy(grp_sub, "ucolor", G_draw.block.color_select); cb->center_selected = BUF_POINT(grp_sub, format); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_uniform_vec4_copy(grp_sub, "ucolor", G_draw.block.color_deselect); cb->center_deselected = BUF_POINT(grp_sub, format); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_uniform_vec4_copy(grp_sub, "ucolor", G_draw.block.color_library_select); cb->center_selected_lib = BUF_POINT(grp_sub, format); grp_sub = DRW_shgroup_create_sub(grp); DRW_shgroup_uniform_vec4_copy(grp_sub, "ucolor", G_draw.block.color_library); cb->center_deselected_lib = BUF_POINT(grp_sub, format); } } } void OVERLAY_extra_point(OVERLAY_ExtraCallBuffers *cb, const float point[3], const float color[4]) { DRW_buffer_add_entry(cb->extra_points, point, color); } void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb, const float start[3], const float end[3], const float color[4]) { DRW_buffer_add_entry(cb->extra_dashed_lines, end, color); DRW_buffer_add_entry(cb->extra_dashed_lines, start, color); } void OVERLAY_extra_line(OVERLAY_ExtraCallBuffers *cb, const float start[3], const float end[3], const int color_id) { DRW_buffer_add_entry(cb->extra_lines, start, &color_id); DRW_buffer_add_entry(cb->extra_lines, end, &color_id); } OVERLAY_ExtraCallBuffers *OVERLAY_extra_call_buffer_get(OVERLAY_Data *vedata, Object *ob) { bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0; OVERLAY_PrivateData *pd = vedata->stl->pd; return &pd->extra_call_buffers[do_in_front]; } void OVERLAY_extra_loose_points(OVERLAY_ExtraCallBuffers *cb, GPUBatch *geom, const float mat[4][4], const float color[4]) { float draw_mat[4][4]; pack_v4_in_mat4(draw_mat, mat, color); DRW_shgroup_call_obmat(cb->extra_loose_points, geom, draw_mat); } void OVERLAY_extra_wire(OVERLAY_ExtraCallBuffers *cb, GPUBatch *geom, const float mat[4][4], const float color[4]) { float draw_mat[4][4]; const float col[4] = {UNPACK3(color), 0.0f /* No stipples. */}; pack_v4_in_mat4(draw_mat, mat, col); DRW_shgroup_call_obmat(cb->extra_wire, geom, draw_mat); } /* -------------------------------------------------------------------- */ /** \name Empties * \{ */ void OVERLAY_empty_shape(OVERLAY_ExtraCallBuffers *cb, const float mat[4][4], const float draw_size, const char draw_type, const float color[4]) { float instdata[4][4]; pack_fl_in_mat4(instdata, mat, draw_size); switch (draw_type) { case OB_PLAINAXES: DRW_buffer_add_entry(cb->empty_plain_axes, color, instdata); break; case OB_SINGLE_ARROW: DRW_buffer_add_entry(cb->empty_single_arrow, color, instdata); break; case OB_CUBE: DRW_buffer_add_entry(cb->empty_cube, color, instdata); break; case OB_CIRCLE: DRW_buffer_add_entry(cb->empty_circle, color, instdata); break; case OB_EMPTY_SPHERE: DRW_buffer_add_entry(cb->empty_sphere, color, instdata); break; case OB_EMPTY_CONE: DRW_buffer_add_entry(cb->empty_cone, color, instdata); break; case OB_ARROWS: DRW_buffer_add_entry(cb->empty_axes, color, instdata); break; case OB_EMPTY_IMAGE: /* This only show the frame. See OVERLAY_image_empty_cache_populate() for the image. */ DRW_buffer_add_entry(cb->empty_image_frame, color, instdata); break; } } void OVERLAY_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) { if (((ob->base_flag & BASE_FROM_DUPLI) != 0) && ((ob->transflag & OB_DUPLICOLLECTION) != 0) && ob->instance_collection) { return; } OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; float *color; switch (ob->empty_drawtype) { case OB_PLAINAXES: case OB_SINGLE_ARROW: case OB_CUBE: case OB_CIRCLE: case OB_EMPTY_SPHERE: case OB_EMPTY_CONE: case OB_ARROWS: DRW_object_wire_theme_get(ob, view_layer, &color); OVERLAY_empty_shape(cb, ob->obmat, ob->empty_drawsize, ob->empty_drawtype, color); break; case OB_EMPTY_IMAGE: OVERLAY_image_empty_cache_populate(vedata, ob); break; } } static void OVERLAY_bounds(OVERLAY_ExtraCallBuffers *cb, Object *ob, const float *color, char boundtype, bool around_origin) { float center[3], size[3], tmp[4][4], final_mat[4][4]; if (ob->type == OB_MBALL && !BKE_mball_is_basis(ob)) { return; } const BoundBox *bb = BKE_object_boundbox_get(ob); BoundBox bb_local; if (bb == nullptr) { const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; BKE_boundbox_init_from_minmax(&bb_local, min, max); bb = &bb_local; } BKE_boundbox_calc_size_aabb(bb, size); if (around_origin) { zero_v3(center); } else { BKE_boundbox_calc_center_aabb(bb, center); } switch (boundtype) { case OB_BOUND_BOX: size_to_mat4(tmp, size); copy_v3_v3(tmp[3], center); mul_m4_m4m4(tmp, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_cube, color, tmp); break; case OB_BOUND_SPHERE: size[0] = max_fff(size[0], size[1], size[2]); size[1] = size[2] = size[0]; size_to_mat4(tmp, size); copy_v3_v3(tmp[3], center); mul_m4_m4m4(tmp, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_sphere, color, tmp); break; case OB_BOUND_CYLINDER: size[0] = max_ff(size[0], size[1]); size[1] = size[0]; size_to_mat4(tmp, size); copy_v3_v3(tmp[3], center); mul_m4_m4m4(tmp, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_cylinder, color, tmp); break; case OB_BOUND_CONE: size[0] = max_ff(size[0], size[1]); size[1] = size[0]; size_to_mat4(tmp, size); copy_v3_v3(tmp[3], center); /* Cone batch has base at 0 and is pointing towards +Y. */ swap_v3_v3(tmp[1], tmp[2]); tmp[3][2] -= size[2]; mul_m4_m4m4(tmp, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_cone, color, tmp); break; case OB_BOUND_CAPSULE: size[0] = max_ff(size[0], size[1]); size[1] = size[0]; scale_m4_fl(tmp, size[0]); copy_v2_v2(tmp[3], center); tmp[3][2] = center[2] + max_ff(0.0f, size[2] - size[0]); mul_m4_m4m4(final_mat, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_capsule_cap, color, final_mat); negate_v3(tmp[2]); tmp[3][2] = center[2] - max_ff(0.0f, size[2] - size[0]); mul_m4_m4m4(final_mat, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_capsule_cap, color, final_mat); tmp[2][2] = max_ff(0.0f, size[2] * 2.0f - size[0] * 2.0f); mul_m4_m4m4(final_mat, ob->obmat, tmp); DRW_buffer_add_entry(cb->empty_capsule_body, color, final_mat); break; } } static void OVERLAY_collision(OVERLAY_ExtraCallBuffers *cb, Object *ob, const float *color) { switch (ob->rigidbody_object->shape) { case RB_SHAPE_BOX: OVERLAY_bounds(cb, ob, color, OB_BOUND_BOX, true); break; case RB_SHAPE_SPHERE: OVERLAY_bounds(cb, ob, color, OB_BOUND_SPHERE, true); break; case RB_SHAPE_CONE: OVERLAY_bounds(cb, ob, color, OB_BOUND_CONE, true); break; case RB_SHAPE_CYLINDER: OVERLAY_bounds(cb, ob, color, OB_BOUND_CYLINDER, true); break; case RB_SHAPE_CAPSULE: OVERLAY_bounds(cb, ob, color, OB_BOUND_CAPSULE, true); break; } } static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, const float *color) { if (ob->data == nullptr) { return; } ID *ob_data = static_cast(ob->data); float *texcoloc = nullptr; float *texcosize = nullptr; switch (GS(ob_data->name)) { case ID_ME: BKE_mesh_texspace_get_reference((Mesh *)ob_data, nullptr, &texcoloc, &texcosize); break; case ID_CU_LEGACY: { Curve *cu = (Curve *)ob_data; BKE_curve_texspace_ensure(cu); texcoloc = cu->loc; texcosize = cu->size; break; } case ID_MB: { MetaBall *mb = (MetaBall *)ob_data; texcoloc = mb->loc; texcosize = mb->size; break; } case ID_CV: case ID_PT: case ID_VO: { /* No user defined texture space support. */ break; } default: BLI_assert(0); } float mat[4][4]; if (texcoloc != nullptr && texcosize != nullptr) { size_to_mat4(mat, texcosize); copy_v3_v3(mat[3], texcoloc); } else { unit_m4(mat); } mul_m4_m4m4(mat, ob->obmat, mat); DRW_buffer_add_entry(cb->empty_cube, color, mat); } static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLayer *view_layer) { int theme_id = DRW_object_wire_theme_get(ob, view_layer, nullptr); float *color = DRW_color_background_blend_get(theme_id); PartDeflect *pd = ob->pd; Curve *cu = (ob->type == OB_CURVES_LEGACY) ? static_cast(ob->data) : nullptr; union { float mat[4][4]; struct { float _pad00[3], size_x; float _pad01[3], size_y; float _pad02[3], size_z; float pos[3], _pad03[1]; }; } instdata; copy_m4_m4(instdata.mat, ob->obmat); instdata.size_x = instdata.size_y = instdata.size_z = ob->empty_drawsize; switch (pd->forcefield) { case PFIELD_FORCE: DRW_buffer_add_entry(cb->field_force, color, &instdata); break; case PFIELD_WIND: instdata.size_z = pd->f_strength; DRW_buffer_add_entry(cb->field_wind, color, &instdata); break; case PFIELD_VORTEX: instdata.size_y = (pd->f_strength < 0.0f) ? -instdata.size_y : instdata.size_y; DRW_buffer_add_entry(cb->field_vortex, color, &instdata); break; case PFIELD_GUIDE: if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->anim_path_accum_length) { instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength; float pos[4]; BKE_where_on_path(ob, 0.0f, pos, nullptr, nullptr, nullptr, nullptr); copy_v3_v3(instdata.pos, ob->obmat[3]); translate_m4(instdata.mat, pos[0], pos[1], pos[2]); DRW_buffer_add_entry(cb->field_curve, color, &instdata); BKE_where_on_path(ob, 1.0f, pos, nullptr, nullptr, nullptr, nullptr); copy_v3_v3(instdata.pos, ob->obmat[3]); translate_m4(instdata.mat, pos[0], pos[1], pos[2]); DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); /* Restore */ copy_v3_v3(instdata.pos, ob->obmat[3]); } break; } if (pd->falloff == PFIELD_FALL_TUBE) { if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) { instdata.size_z = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f; instdata.size_x = (pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f; instdata.size_y = instdata.size_x; DRW_buffer_add_entry(cb->field_tube_limit, color, &instdata); } if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) { instdata.size_z = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f; instdata.size_x = (pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f; instdata.size_y = instdata.size_x; DRW_buffer_add_entry(cb->field_tube_limit, color, &instdata); } } else if (pd->falloff == PFIELD_FALL_CONE) { if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) { float radius = DEG2RADF((pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f); float distance = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f; instdata.size_x = distance * sinf(radius); instdata.size_z = distance * cosf(radius); instdata.size_y = instdata.size_x; DRW_buffer_add_entry(cb->field_cone_limit, color, &instdata); } if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) { float radius = DEG2RADF((pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f); float distance = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f; instdata.size_x = distance * sinf(radius); instdata.size_z = distance * cosf(radius); instdata.size_y = instdata.size_x; DRW_buffer_add_entry(cb->field_cone_limit, color, &instdata); } } else if (pd->falloff == PFIELD_FALL_SPHERE) { if (pd->flag & PFIELD_USEMAX) { instdata.size_x = instdata.size_y = instdata.size_z = pd->maxdist; DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); } if (pd->flag & PFIELD_USEMIN) { instdata.size_x = instdata.size_y = instdata.size_z = pd->mindist; DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); } } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Lights * \{ */ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; Light *la = static_cast(ob->data); float *color_p; DRW_object_wire_theme_get(ob, view_layer, &color_p); /* Remove the alpha. */ float color[4] = {UNPACK3(color_p), 1.0f}; /* Pack render data into object matrix. */ union { float mat[4][4]; struct { float _pad00[3]; union { float area_size_x; float spot_cosine; }; float _pad01[3]; union { float area_size_y; float spot_blend; }; float _pad02[3], clip_sta; float pos[3], clip_end; }; } instdata; copy_m4_m4(instdata.mat, ob->obmat); /* FIXME / TODO: clip_end has no meaning nowadays. * In EEVEE, Only clip_sta is used shadow-mapping. * Clip end is computed automatically based on light power. * For now, always use the custom distance as clip_end. */ instdata.clip_end = la->att_dist; instdata.clip_sta = la->clipsta; DRW_buffer_add_entry(cb->groundline, instdata.pos); if (la->type == LA_LOCAL) { instdata.area_size_x = instdata.area_size_y = la->area_size; DRW_buffer_add_entry(cb->light_point, color, &instdata); } else if (la->type == LA_SUN) { DRW_buffer_add_entry(cb->light_sun, color, &instdata); } else if (la->type == LA_SPOT) { /* Previous implementation was using the clipend distance as cone size. * We cannot do this anymore so we use a fixed size of 10. (see T72871) */ const float3 scale_vec = {10.0f, 10.0f, 10.0f}; rescale_m4(instdata.mat, scale_vec); /* For cycles and eevee the spot attenuation is * y = (1/(1 + x^2) - a)/((1 - a) b) * We solve the case where spot attenuation y = 1 and y = 0 * root for y = 1 is (-1 - c) / c * root for y = 0 is (1 - a) / a * and use that to position the blend circle. */ float a = cosf(la->spotsize * 0.5f); float b = la->spotblend; float c = a * b - a - b; /* Optimized version or root1 / root0 */ instdata.spot_blend = sqrtf((-a - c * a) / (c - c * a)); instdata.spot_cosine = a; /* HACK: We pack the area size in alpha color. This is decoded by the shader. */ color[3] = -max_ff(la->area_size, FLT_MIN); DRW_buffer_add_entry(cb->light_spot, color, &instdata); if ((la->mode & LA_SHOW_CONE) && !DRW_state_is_select()) { const float color_inside[4] = {0.0f, 0.0f, 0.0f, 0.5f}; const float color_outside[4] = {1.0f, 1.0f, 1.0f, 0.3f}; DRW_buffer_add_entry(cb->light_spot_cone_front, color_inside, &instdata); DRW_buffer_add_entry(cb->light_spot_cone_back, color_outside, &instdata); } } else if (la->type == LA_AREA) { bool uniform_scale = !ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE); int sqr = ELEM(la->area_shape, LA_AREA_SQUARE, LA_AREA_RECT); instdata.area_size_x = la->area_size; instdata.area_size_y = uniform_scale ? la->area_size : la->area_sizey; DRW_buffer_add_entry(cb->light_area[sqr], color, &instdata); } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Light-probe * \{ */ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; float *color_p; int theme_id = DRW_object_wire_theme_get(ob, view_layer, &color_p); const LightProbe *prb = (LightProbe *)ob->data; const bool show_clipping = (prb->flag & LIGHTPROBE_FLAG_SHOW_CLIP_DIST) != 0; const bool show_parallax = (prb->flag & LIGHTPROBE_FLAG_SHOW_PARALLAX) != 0; const bool show_influence = (prb->flag & LIGHTPROBE_FLAG_SHOW_INFLUENCE) != 0; const bool show_data = (ob->base_flag & BASE_SELECTED) || DRW_state_is_select(); union { float mat[4][4]; struct { float _pad00[4]; float _pad01[4]; float _pad02[3], clip_sta; float pos[3], clip_end; }; } instdata; copy_m4_m4(instdata.mat, ob->obmat); switch (prb->type) { case LIGHTPROBE_TYPE_CUBE: instdata.clip_sta = show_clipping ? prb->clipsta : -1.0; instdata.clip_end = show_clipping ? prb->clipend : -1.0; DRW_buffer_add_entry(cb->probe_cube, color_p, &instdata); DRW_buffer_add_entry(cb->groundline, instdata.pos); if (show_influence) { char shape = (prb->attenuation_type == LIGHTPROBE_SHAPE_BOX) ? OB_CUBE : OB_EMPTY_SPHERE; float f = 1.0f - prb->falloff; OVERLAY_empty_shape(cb, ob->obmat, prb->distinf, shape, color_p); OVERLAY_empty_shape(cb, ob->obmat, prb->distinf * f, shape, color_p); } if (show_parallax) { char shape = (prb->parallax_type == LIGHTPROBE_SHAPE_BOX) ? OB_CUBE : OB_EMPTY_SPHERE; float dist = ((prb->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) ? prb->distpar : prb->distinf; OVERLAY_empty_shape(cb, ob->obmat, dist, shape, color_p); } break; case LIGHTPROBE_TYPE_GRID: instdata.clip_sta = show_clipping ? prb->clipsta : -1.0; instdata.clip_end = show_clipping ? prb->clipend : -1.0; DRW_buffer_add_entry(cb->probe_grid, color_p, &instdata); if (show_influence) { float f = 1.0f - prb->falloff; OVERLAY_empty_shape(cb, ob->obmat, 1.0 + prb->distinf, OB_CUBE, color_p); OVERLAY_empty_shape(cb, ob->obmat, 1.0 + prb->distinf * f, OB_CUBE, color_p); } /* Data dots */ if (show_data) { instdata.mat[0][3] = prb->grid_resolution_x; instdata.mat[1][3] = prb->grid_resolution_y; instdata.mat[2][3] = prb->grid_resolution_z; /* Put theme id in matrix. */ if (theme_id == TH_ACTIVE) { instdata.mat[3][3] = 1.0; } else /* TH_SELECT */ { instdata.mat[3][3] = 2.0; } uint cell_count = prb->grid_resolution_x * prb->grid_resolution_y * prb->grid_resolution_z; DRWShadingGroup *grp = DRW_shgroup_create_sub(vedata->stl->pd->extra_grid_grp); DRW_shgroup_uniform_mat4_copy(grp, "gridModelMatrix", instdata.mat); DRW_shgroup_call_procedural_points(grp, nullptr, cell_count); } break; case LIGHTPROBE_TYPE_PLANAR: DRW_buffer_add_entry(cb->probe_planar, color_p, &instdata); if (DRW_state_is_select() && (prb->flag & LIGHTPROBE_FLAG_SHOW_DATA)) { DRW_buffer_add_entry(cb->solid_quad, color_p, &instdata); } if (show_influence) { normalize_v3_length(instdata.mat[2], prb->distinf); DRW_buffer_add_entry(cb->empty_cube, color_p, &instdata); mul_v3_fl(instdata.mat[2], 1.0f - prb->falloff); DRW_buffer_add_entry(cb->empty_cube, color_p, &instdata); } zero_v3(instdata.mat[2]); DRW_buffer_add_entry(cb->empty_cube, color_p, &instdata); normalize_m4_m4(instdata.mat, ob->obmat); OVERLAY_empty_shape(cb, instdata.mat, ob->empty_drawsize, OB_SINGLE_ARROW, color_p); break; } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Speaker * \{ */ void OVERLAY_speaker_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; float *color_p; DRW_object_wire_theme_get(ob, view_layer, &color_p); DRW_buffer_add_entry(cb->speaker, color_p, ob->obmat); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Camera * \{ */ union OVERLAY_CameraInstanceData { /* Pack render data into object matrix and object color. */ struct { float color[4]; float mat[4][4]; }; struct { float _pad0[2]; float volume_sta; union { float depth; float focus; float volume_end; }; float _pad00[3]; union { float corner_x; float dist_color_id; }; float _pad01[3]; union { float corner_y; }; float _pad02[3]; union { float center_x; float clip_sta; float mist_sta; }; float pos[3]; union { float center_y; float clip_end; float mist_end; }; }; }; static void camera_view3d_reconstruction( OVERLAY_ExtraCallBuffers *cb, Scene *scene, View3D *v3d, Object *ob, const float color[4]) { const DRWContextState *draw_ctx = DRW_context_state_get(); const bool is_select = DRW_state_is_select(); MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); if (clip == nullptr) { return; } const bool is_solid_bundle = (v3d->bundle_drawtype == OB_EMPTY_SPHERE) && ((v3d->shading.type != OB_SOLID) || !XRAY_FLAG_ENABLED(v3d)); MovieTracking *tracking = &clip->tracking; /* Index must start in 1, to mimic BKE_tracking_track_get_indexed. */ int track_index = 1; float bundle_color_custom[3]; float *bundle_color_solid = G_draw.block.color_bundle_solid; float *bundle_color_unselected = G_draw.block.color_wire; uchar text_color_selected[4], text_color_unselected[4]; /* Color Management: Exception here as texts are drawn in sRGB space directly. */ UI_GetThemeColor4ubv(TH_SELECT, text_color_selected); UI_GetThemeColor4ubv(TH_TEXT, text_color_unselected); float camera_mat[4][4]; BKE_tracking_get_camera_object_matrix(ob, camera_mat); LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { float tracking_object_mat[4][4]; if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { copy_m4_m4(tracking_object_mat, camera_mat); } else { const int framenr = BKE_movieclip_remap_scene_to_clip_frame( clip, DEG_get_ctime(draw_ctx->depsgraph)); float object_mat[4][4]; BKE_tracking_camera_get_reconstructed_interpolate( tracking, tracking_object, framenr, object_mat); float object_imat[4][4]; invert_m4_m4(object_imat, object_mat); mul_m4_m4m4(tracking_object_mat, ob->obmat, object_imat); } ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { if ((track->flag & TRACK_HAS_BUNDLE) == 0) { continue; } bool is_selected = TRACK_SELECTED(track); float bundle_mat[4][4]; copy_m4_m4(bundle_mat, tracking_object_mat); translate_m4(bundle_mat, track->bundle_pos[0], track->bundle_pos[1], track->bundle_pos[2]); const float *bundle_color; if (track->flag & TRACK_CUSTOMCOLOR) { /* Meh, hardcoded srgb transform here. */ /* TODO: change the actual DNA color to be linear. */ srgb_to_linearrgb_v3_v3(bundle_color_custom, track->color); bundle_color = bundle_color_custom; } else if (is_solid_bundle) { bundle_color = bundle_color_solid; } else if (is_selected) { bundle_color = color; } else { bundle_color = bundle_color_unselected; } if (is_select) { DRW_select_load_id(ob->runtime.select_id | (track_index << 16)); track_index++; } if (is_solid_bundle) { if (is_selected) { OVERLAY_empty_shape(cb, bundle_mat, v3d->bundle_size, v3d->bundle_drawtype, color); } const float bundle_color_v4[4] = { bundle_color[0], bundle_color[1], bundle_color[2], 1.0f, }; bundle_mat[3][3] = v3d->bundle_size; /* See shader. */ DRW_buffer_add_entry(cb->empty_sphere_solid, bundle_color_v4, bundle_mat); } else { OVERLAY_empty_shape(cb, bundle_mat, v3d->bundle_size, v3d->bundle_drawtype, bundle_color); } if ((v3d->flag2 & V3D_SHOW_BUNDLENAME) && !is_select) { DRWTextStore *dt = DRW_text_cache_ensure(); DRW_text_cache_add(dt, bundle_mat[3], track->name, strlen(track->name), 10, 0, DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, is_selected ? text_color_selected : text_color_unselected); } } if ((v3d->flag2 & V3D_SHOW_CAMERAPATH) && (tracking_object->flag & TRACKING_OBJECT_CAMERA) && !is_select) { MovieTrackingReconstruction *reconstruction; reconstruction = BKE_tracking_object_get_reconstruction(tracking, tracking_object); if (reconstruction->camnr) { MovieReconstructedCamera *camera = reconstruction->cameras; float v0[3], v1[3]; for (int a = 0; a < reconstruction->camnr; a++, camera++) { copy_v3_v3(v0, v1); copy_v3_v3(v1, camera->mat[3]); mul_m4_v3(camera_mat, v1); if (a > 0) { /* This one is suboptimal (gl_lines instead of gl_line_strip) * but we keep this for simplicity */ OVERLAY_extra_line(cb, v0, v1, TH_CAMERA_PATH); } } } } } } static float camera_offaxis_shiftx_get(Scene *scene, Object *ob, const OVERLAY_CameraInstanceData *instdata, bool right_eye) { Camera *cam = static_cast(ob->data); if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; const float shiftx = BKE_camera_multiview_shift_x(&scene->r, ob, viewnames[right_eye]); const float delta_shiftx = shiftx - cam->shiftx; const float width = instdata->corner_x * 2.0f; return delta_shiftx * width; } return 0.0; } /** * Draw the stereo 3d support elements (cameras, plane, volume). * They are only visible when not looking through the camera: */ static void camera_stereoscopy_extra(OVERLAY_ExtraCallBuffers *cb, Scene *scene, View3D *v3d, Object *ob, const OVERLAY_CameraInstanceData *instdata) { OVERLAY_CameraInstanceData stereodata = *instdata; Camera *cam = static_cast(ob->data); const bool is_select = DRW_state_is_select(); const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) != 0; const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) != 0; const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME) != 0; if (!is_stereo3d_cameras) { /* Draw single camera. */ DRW_buffer_add_entry_struct(cb->camera_frame, instdata); } for (int eye = 0; eye < 2; eye++) { ob = BKE_camera_multiview_render(scene, ob, viewnames[eye]); BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[eye], stereodata.mat); stereodata.corner_x = instdata->corner_x; stereodata.corner_y = instdata->corner_y; stereodata.center_x = instdata->center_x + camera_offaxis_shiftx_get(scene, ob, instdata, eye); stereodata.center_y = instdata->center_y; stereodata.depth = instdata->depth; if (is_stereo3d_cameras) { DRW_buffer_add_entry_struct(cb->camera_frame, &stereodata); /* Connecting line between cameras. */ OVERLAY_extra_line_dashed(cb, stereodata.pos, instdata->pos, G_draw.block.color_wire); } if (is_stereo3d_volume && !is_select) { float r = (eye == 1) ? 2.0f : 1.0f; stereodata.volume_sta = -cam->clip_start; stereodata.volume_end = -cam->clip_end; /* Encode eye + intensity and alpha (see shader) */ copy_v2_fl2(stereodata.color, r + 0.15f, 1.0f); DRW_buffer_add_entry_struct(cb->camera_volume_frame, &stereodata); if (v3d->stereo3d_volume_alpha > 0.0f) { /* Encode eye + intensity and alpha (see shader) */ copy_v2_fl2(stereodata.color, r + 0.999f, v3d->stereo3d_volume_alpha); DRW_buffer_add_entry_struct(cb->camera_volume, &stereodata); } /* restore */ copy_v3_v3(stereodata.color, instdata->color); } } if (is_stereo3d_plane && !is_select) { if (cam->stereo.convergence_mode == CAM_S3D_TOE) { /* There is no real convergence plane but we highlight the center * point where the views are pointing at. */ // zero_v3(stereodata.mat[0]); /* We reconstruct from Z and Y */ // zero_v3(stereodata.mat[1]); /* Y doesn't change */ zero_v3(stereodata.mat[2]); zero_v3(stereodata.mat[3]); for (int i = 0; i < 2; i++) { float mat[4][4]; /* Need normalized version here. */ BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[i], mat); add_v3_v3(stereodata.mat[2], mat[2]); madd_v3_v3fl(stereodata.mat[3], mat[3], 0.5f); } normalize_v3(stereodata.mat[2]); cross_v3_v3v3(stereodata.mat[0], stereodata.mat[1], stereodata.mat[2]); } else if (cam->stereo.convergence_mode == CAM_S3D_PARALLEL) { /* Show plane at the given distance between the views even if it makes no sense. */ zero_v3(stereodata.pos); for (int i = 0; i < 2; i++) { float mat[4][4]; BKE_camera_multiview_model_matrix_scaled(&scene->r, ob, viewnames[i], mat); madd_v3_v3fl(stereodata.pos, mat[3], 0.5f); } } else if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { /* Nothing to do. Everything is already setup. */ } stereodata.volume_sta = -cam->stereo.convergence_distance; stereodata.volume_end = -cam->stereo.convergence_distance; /* Encode eye + intensity and alpha (see shader) */ copy_v2_fl2(stereodata.color, 0.1f, 1.0f); DRW_buffer_add_entry_struct(cb->camera_volume_frame, &stereodata); if (v3d->stereo3d_convergence_alpha > 0.0f) { /* Encode eye + intensity and alpha (see shader) */ copy_v2_fl2(stereodata.color, 0.0f, v3d->stereo3d_convergence_alpha); DRW_buffer_add_entry_struct(cb->camera_volume, &stereodata); } } } void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); OVERLAY_CameraInstanceData instdata; const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; View3D *v3d = draw_ctx->v3d; Scene *scene = draw_ctx->scene; RegionView3D *rv3d = draw_ctx->rv3d; Camera *cam = static_cast(ob->data); Object *camera_object = DEG_get_evaluated_object(draw_ctx->depsgraph, v3d->camera); const bool is_select = DRW_state_is_select(); const bool is_active = (ob == camera_object); const bool look_through = (is_active && (rv3d->persp == RV3D_CAMOB)); const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; const bool is_stereo3d_view = (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); const bool is_stereo3d_display_extra = is_active && is_multiview && (!look_through) && ((v3d->stereo3d_flag) != 0); const bool is_selection_camera_stereo = is_select && look_through && is_multiview && is_stereo3d_view; float vec[4][3], asp[2], shift[2], scale[3], drawsize, center[2], corner[2]; float *color_p; DRW_object_wire_theme_get(ob, view_layer, &color_p); copy_v4_v4(instdata.color, color_p); normalize_m4_m4(instdata.mat, ob->obmat); /* BKE_camera_multiview_model_matrix already accounts for scale, don't do it here. */ if (is_selection_camera_stereo) { copy_v3_fl(scale, 1.0f); } else { copy_v3_fl3(scale, len_v3(ob->obmat[0]), len_v3(ob->obmat[1]), len_v3(ob->obmat[2])); /* Avoid division by 0. */ if (ELEM(0.0f, scale[0], scale[1], scale[2])) { return; } invert_v3(scale); } BKE_camera_view_frame_ex( scene, cam, cam->drawsize, look_through, scale, asp, shift, &drawsize, vec); /* Apply scale to simplify the rest of the drawing. */ invert_v3(scale); for (int i = 0; i < 4; i++) { mul_v3_v3(vec[i], scale); /* Project to z=-1 plane. Makes positioning / scaling easier. (see shader) */ mul_v2_fl(vec[i], 1.0f / fabsf(vec[i][2])); } /* Frame coords */ mid_v2_v2v2(center, vec[0], vec[2]); sub_v2_v2v2(corner, vec[0], center); instdata.corner_x = corner[0]; instdata.corner_y = corner[1]; instdata.center_x = center[0]; instdata.center_y = center[1]; instdata.depth = vec[0][2]; if (look_through) { if (!DRW_state_is_image_render()) { /* Only draw the frame. */ if (is_multiview) { float mat[4][4]; const bool is_right = v3d->multiview_eye == STEREO_RIGHT_ID; const char *view_name = is_right ? STEREO_RIGHT_NAME : STEREO_LEFT_NAME; BKE_camera_multiview_model_matrix(&scene->r, ob, view_name, mat); instdata.center_x += camera_offaxis_shiftx_get(scene, ob, &instdata, is_right); for (int i = 0; i < 4; i++) { /* Partial copy to avoid overriding packed data. */ copy_v3_v3(instdata.mat[i], mat[i]); } } instdata.depth = -instdata.depth; /* Hides the back of the camera wires (see shader). */ DRW_buffer_add_entry_struct(cb->camera_frame, &instdata); } } else { /* Stereo cameras, volumes, plane drawing. */ if (is_stereo3d_display_extra) { camera_stereoscopy_extra(cb, scene, v3d, ob, &instdata); } else { DRW_buffer_add_entry_struct(cb->camera_frame, &instdata); } } if (!look_through) { /* Triangle. */ float tria_size = 0.7f * drawsize / fabsf(instdata.depth); float tria_margin = 0.1f * drawsize / fabsf(instdata.depth); instdata.center_x = center[0]; instdata.center_y = center[1] + instdata.corner_y + tria_margin + tria_size; instdata.corner_x = instdata.corner_y = -tria_size; DRW_buffer_add_entry_struct(cb->camera_tria[is_active], &instdata); } if (cam->flag & CAM_SHOWLIMITS) { /* Scale focus point. */ mul_v3_fl(instdata.mat[0], cam->drawsize); mul_v3_fl(instdata.mat[1], cam->drawsize); instdata.dist_color_id = (is_active) ? 3 : 2; instdata.focus = -BKE_camera_object_dof_distance(ob); instdata.clip_sta = cam->clip_start; instdata.clip_end = cam->clip_end; DRW_buffer_add_entry_struct(cb->camera_distances, &instdata); } if (cam->flag & CAM_SHOWMIST) { World *world = scene->world; if (world) { instdata.dist_color_id = (is_active) ? 1 : 0; instdata.focus = 1.0f; /* Disable */ instdata.mist_sta = world->miststa; instdata.mist_end = world->miststa + world->mistdist; DRW_buffer_add_entry_struct(cb->camera_distances, &instdata); } } /* Motion Tracking. */ if ((v3d->flag2 & V3D_SHOW_RECONSTRUCTION) != 0) { camera_view3d_reconstruction(cb, scene, v3d, ob, color_p); } /* 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); } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Relationships & constraints * \{ */ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, Depsgraph *depsgraph, Scene *scene, Object *ob) { float *relation_color = G_draw.block.color_wire; float *constraint_color = G_draw.block.color_grid_axis_z; /* ? */ if (ob->parent && (DRW_object_visibility_in_active_context(ob->parent) & OB_VISIBLE_SELF)) { float *parent_pos = ob->runtime.parent_display_origin; OVERLAY_extra_line_dashed(cb, parent_pos, ob->obmat[3], relation_color); } /* Drawing the hook lines. */ for (ModifierData *md = static_cast(ob->modifiers.first); md; md = md->next) { if (md->type == eModifierType_Hook) { HookModifierData *hmd = (HookModifierData *)md; float center[3]; mul_v3_m4v3(center, ob->obmat, hmd->cent); if (hmd->object) { OVERLAY_extra_line_dashed(cb, hmd->object->obmat[3], center, relation_color); } OVERLAY_extra_point(cb, center, relation_color); } } if (ob->rigidbody_constraint) { Object *rbc_ob1 = ob->rigidbody_constraint->ob1; Object *rbc_ob2 = ob->rigidbody_constraint->ob2; if (rbc_ob1 && (DRW_object_visibility_in_active_context(rbc_ob1) & OB_VISIBLE_SELF)) { OVERLAY_extra_line_dashed(cb, rbc_ob1->obmat[3], ob->obmat[3], relation_color); } if (rbc_ob2 && (DRW_object_visibility_in_active_context(rbc_ob2) & OB_VISIBLE_SELF)) { OVERLAY_extra_line_dashed(cb, rbc_ob2->obmat[3], ob->obmat[3], relation_color); } } /* Drawing the constraint lines */ if (!BLI_listbase_is_empty(&ob->constraints)) { bConstraint *curcon; bConstraintOb *cob; ListBase *list = &ob->constraints; cob = BKE_constraints_make_evalob(depsgraph, scene, ob, nullptr, CONSTRAINT_OBTYPE_OBJECT); for (curcon = static_cast(list->first); curcon; curcon = curcon->next) { if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_OBJECTSOLVER)) { /* special case for object solver and follow track constraints because they don't fill * constraint targets properly (design limitation -- scene is needed for their target * but it can't be accessed from get_targets callback) */ Object *camob = nullptr; if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) { bFollowTrackConstraint *data = (bFollowTrackConstraint *)curcon->data; camob = data->camera ? data->camera : scene->camera; } else if (curcon->type == CONSTRAINT_TYPE_OBJECTSOLVER) { bObjectSolverConstraint *data = (bObjectSolverConstraint *)curcon->data; camob = data->camera ? data->camera : scene->camera; } if (camob) { OVERLAY_extra_line_dashed(cb, camob->obmat[3], ob->obmat[3], constraint_color); } } else { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); ListBase targets = {nullptr, nullptr}; if ((curcon->ui_expand_flag & (1 << 0)) && BKE_constraint_targets_get(curcon, &targets)) { bConstraintTarget *ct; BKE_constraint_custom_object_space_init(cob, curcon); for (ct = static_cast(targets.first); ct; ct = ct->next) { /* calculate target's matrix */ if (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE) { copy_m4_m4(ct->matrix, cob->space_obj_world_matrix); } else if (cti->get_target_matrix) { cti->get_target_matrix(depsgraph, curcon, cob, ct, DEG_get_ctime(depsgraph)); } else { unit_m4(ct->matrix); } OVERLAY_extra_line_dashed(cb, ct->matrix[3], ob->obmat[3], constraint_color); } BKE_constraint_targets_flush(curcon, &targets, true); } } } /* NOTE: Don't use #BKE_constraints_clear_evalob here as that will reset `ob->constinv`. */ MEM_freeN(cob); } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Volumetric / Smoke sim * \{ */ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb, OVERLAY_Data *data, Object *ob, ModifierData *md, Scene *scene, const float *color) { FluidModifierData *fmd = (FluidModifierData *)md; FluidDomainSettings *fds = fmd->domain; /* Don't show smoke before simulation starts, this could be made an option in the future. */ const bool draw_velocity = (fds->draw_velocity && fds->fluid && scene->r.cfra >= fds->point_cache[0]->startframe); /* Show gridlines only for slices with no interpolation. */ const bool show_gridlines = (fds->show_gridlines && fds->fluid && fds->axis_slice_method == AXIS_SLICE_SINGLE && (fds->interp_method == FLUID_DISPLAY_INTERP_CLOSEST || fds->coba_field == FLUID_DOMAIN_FIELD_FLAGS)); const bool color_with_flags = (fds->gridlines_color_field == FLUID_GRIDLINE_COLOR_TYPE_FLAGS); const bool color_range = (fds->gridlines_color_field == FLUID_GRIDLINE_COLOR_TYPE_RANGE && fds->use_coba && fds->coba_field != FLUID_DOMAIN_FIELD_FLAGS); /* Small cube showing voxel size. */ { float min[3]; madd_v3fl_v3fl_v3fl_v3i(min, fds->p0, fds->cell_size, fds->res_min); float voxel_cubemat[4][4] = {{0.0f}}; /* scale small cube to voxel size */ voxel_cubemat[0][0] = fds->cell_size[0] / 2.0f; voxel_cubemat[1][1] = fds->cell_size[1] / 2.0f; voxel_cubemat[2][2] = fds->cell_size[2] / 2.0f; voxel_cubemat[3][3] = 1.0f; /* translate small cube to corner */ copy_v3_v3(voxel_cubemat[3], min); /* move small cube into the domain (otherwise its centered on vertex of domain object) */ translate_m4(voxel_cubemat, 1.0f, 1.0f, 1.0f); mul_m4_m4m4(voxel_cubemat, ob->obmat, voxel_cubemat); DRW_buffer_add_entry(cb->empty_cube, color, voxel_cubemat); } int slice_axis = -1; if (fds->axis_slice_method == AXIS_SLICE_SINGLE) { float viewinv[4][4]; DRW_view_viewmat_get(nullptr, viewinv, true); const int axis = (fds->slice_axis == SLICE_AXIS_AUTO) ? axis_dominant_v3_single(viewinv[2]) : fds->slice_axis - 1; slice_axis = axis; } if (draw_velocity) { const bool use_needle = (fds->vector_draw_type == VECTOR_DRAW_NEEDLE); const bool use_mac = (fds->vector_draw_type == VECTOR_DRAW_MAC); const bool draw_mac_x = (fds->vector_draw_mac_components & VECTOR_DRAW_MAC_X); const bool draw_mac_y = (fds->vector_draw_mac_components & VECTOR_DRAW_MAC_Y); const bool draw_mac_z = (fds->vector_draw_mac_components & VECTOR_DRAW_MAC_Z); const bool cell_centered = (fds->vector_field == FLUID_DOMAIN_VECTOR_FIELD_FORCE); int line_count = 1; if (use_needle) { line_count = 6; } else if (use_mac) { line_count = 3; } line_count *= fds->res[0] * fds->res[1] * fds->res[2]; if (fds->axis_slice_method == AXIS_SLICE_SINGLE) { line_count /= fds->res[slice_axis]; } DRW_smoke_ensure_velocity(fmd); GPUShader *sh = OVERLAY_shader_volume_velocity(use_needle, use_mac); DRWShadingGroup *grp = DRW_shgroup_create(sh, data->psl->extra_ps[0]); DRW_shgroup_uniform_texture(grp, "velocityX", fds->tex_velocity_x); DRW_shgroup_uniform_texture(grp, "velocityY", fds->tex_velocity_y); DRW_shgroup_uniform_texture(grp, "velocityZ", fds->tex_velocity_z); DRW_shgroup_uniform_float_copy(grp, "displaySize", fds->vector_scale); DRW_shgroup_uniform_float_copy(grp, "slicePosition", fds->slice_depth); DRW_shgroup_uniform_vec3_copy(grp, "cellSize", fds->cell_size); DRW_shgroup_uniform_vec3_copy(grp, "domainOriginOffset", fds->p0); DRW_shgroup_uniform_ivec3_copy(grp, "adaptiveCellOffset", fds->res_min); DRW_shgroup_uniform_int_copy(grp, "sliceAxis", slice_axis); DRW_shgroup_uniform_bool_copy(grp, "scaleWithMagnitude", fds->vector_scale_with_magnitude); DRW_shgroup_uniform_bool_copy(grp, "isCellCentered", cell_centered); if (use_mac) { DRW_shgroup_uniform_bool_copy(grp, "drawMACX", draw_mac_x); DRW_shgroup_uniform_bool_copy(grp, "drawMACY", draw_mac_y); DRW_shgroup_uniform_bool_copy(grp, "drawMACZ", draw_mac_z); } DRW_shgroup_call_procedural_lines(grp, ob, line_count); } if (show_gridlines) { GPUShader *sh = OVERLAY_shader_volume_gridlines(color_with_flags, color_range); DRWShadingGroup *grp = DRW_shgroup_create(sh, data->psl->extra_ps[0]); DRW_shgroup_uniform_ivec3_copy(grp, "volumeSize", fds->res); DRW_shgroup_uniform_float_copy(grp, "slicePosition", fds->slice_depth); DRW_shgroup_uniform_vec3_copy(grp, "cellSize", fds->cell_size); DRW_shgroup_uniform_vec3_copy(grp, "domainOriginOffset", fds->p0); DRW_shgroup_uniform_ivec3_copy(grp, "adaptiveCellOffset", fds->res_min); DRW_shgroup_uniform_int_copy(grp, "sliceAxis", slice_axis); if (color_with_flags || color_range) { DRW_fluid_ensure_flags(fmd); DRW_shgroup_uniform_texture(grp, "flagTexture", fds->tex_flags); } if (color_range) { DRW_fluid_ensure_range_field(fmd); DRW_shgroup_uniform_texture(grp, "fieldTexture", fds->tex_range_field); DRW_shgroup_uniform_float_copy(grp, "lowerBound", fds->gridlines_lower_bound); DRW_shgroup_uniform_float_copy(grp, "upperBound", fds->gridlines_upper_bound); DRW_shgroup_uniform_vec4_copy(grp, "rangeColor", fds->gridlines_range_color); DRW_shgroup_uniform_int_copy(grp, "cellFilter", fds->gridlines_cell_filter); } const int line_count = 4 * fds->res[0] * fds->res[1] * fds->res[2] / fds->res[slice_axis]; DRW_shgroup_call_procedural_lines(grp, ob, line_count); } if (draw_velocity || show_gridlines) { } } /** \} */ /* -------------------------------------------------------------------- */ static void OVERLAY_object_center(OVERLAY_ExtraCallBuffers *cb, Object *ob, OVERLAY_PrivateData *pd, const Scene *scene, ViewLayer *view_layer) { const bool is_library = ID_REAL_USERS(&ob->id) > 1 || ID_IS_LINKED(ob); BKE_view_layer_synced_ensure(scene, view_layer); if (ob == BKE_view_layer_active_object_get(view_layer)) { DRW_buffer_add_entry(cb->center_active, ob->obmat[3]); } else if (ob->base_flag & BASE_SELECTED) { DRWCallBuffer *cbuf = (is_library) ? cb->center_selected_lib : cb->center_selected; DRW_buffer_add_entry(cbuf, ob->obmat[3]); } else if (pd->v3d_flag & V3D_DRAW_CENTERS) { DRWCallBuffer *cbuf = (is_library) ? cb->center_deselected_lib : cb->center_deselected; DRW_buffer_add_entry(cbuf, ob->obmat[3]); } } static void OVERLAY_object_name(Object *ob, int theme_id) { DRWTextStore *dt = DRW_text_cache_ensure(); uchar color[4]; /* Color Management: Exception here as texts are drawn in sRGB space directly. */ UI_GetThemeColor4ubv(theme_id, color); DRW_text_cache_add(dt, ob->obmat[3], ob->id.name + 2, strlen(ob->id.name + 2), 10, 0, DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, color); } void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob) { OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); OVERLAY_PrivateData *pd = vedata->stl->pd; const DRWContextState *draw_ctx = DRW_context_state_get(); ViewLayer *view_layer = draw_ctx->view_layer; Scene *scene = draw_ctx->scene; ModifierData *md = nullptr; const bool is_select_mode = DRW_state_is_select(); const bool is_paint_mode = (draw_ctx->object_mode & (OB_MODE_ALL_PAINT | OB_MODE_ALL_PAINT_GPENCIL)) != 0; const bool from_dupli = (ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) != 0; const bool has_bounds = !ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_EMPTY, OB_SPEAKER, OB_LIGHTPROBE); const bool has_texspace = has_bounds && !ELEM(ob->type, OB_EMPTY, OB_LATTICE, OB_ARMATURE, OB_GPENCIL); const bool draw_relations = ((pd->v3d_flag & V3D_HIDE_HELPLINES) == 0) && !is_select_mode; const bool draw_obcenters = !is_paint_mode && (pd->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_ORIGINS) == 0; const bool draw_texspace = (ob->dtx & OB_TEXSPACE) && has_texspace; const bool draw_obname = (ob->dtx & OB_DRAWNAME) && DRW_state_show_text(); const bool draw_bounds = has_bounds && ((ob->dt == OB_BOUNDBOX) || ((ob->dtx & OB_DRAWBOUNDOX) && !from_dupli)); const bool draw_xform = draw_ctx->object_mode == OB_MODE_OBJECT && (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN) && (ob->base_flag & BASE_SELECTED) && !is_select_mode; /* Don't show fluid domain overlay extras outside of cache range. */ const bool draw_volume = !from_dupli && (md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) && BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime) && (((FluidModifierData *)md)->domain != nullptr) && (scene->r.cfra >= (((FluidModifierData *)md)->domain->cache_frame_start)) && (scene->r.cfra <= (((FluidModifierData *)md)->domain->cache_frame_end)); float *color; int theme_id = DRW_object_wire_theme_get(ob, view_layer, &color); if (ob->pd && ob->pd->forcefield) { OVERLAY_forcefield(cb, ob, view_layer); } if (draw_bounds) { OVERLAY_bounds(cb, ob, color, ob->boundtype, false); } /* Helpers for when we're transforming origins. */ if (draw_xform) { const float color_xform[4] = {0.15f, 0.15f, 0.15f, 0.7f}; DRW_buffer_add_entry(cb->origin_xform, color_xform, ob->obmat); } /* don't show object extras in set's */ if (!from_dupli) { if (draw_obcenters) { OVERLAY_object_center(cb, ob, pd, scene, view_layer); } if (draw_relations) { OVERLAY_relationship_lines(cb, draw_ctx->depsgraph, draw_ctx->scene, ob); } if (draw_obname) { OVERLAY_object_name(ob, theme_id); } if (draw_texspace) { OVERLAY_texture_space(cb, ob, color); } if (ob->rigidbody_object != nullptr) { OVERLAY_collision(cb, ob, color); } if (ob->dtx & OB_AXIS) { DRW_buffer_add_entry(cb->empty_axes, color, ob->obmat); } if (draw_volume) { OVERLAY_volume_extra(cb, vedata, ob, md, scene, color); } } } void OVERLAY_extra_blend_draw(OVERLAY_Data *vedata) { DRW_draw_pass(vedata->psl->extra_blend_ps); } void OVERLAY_extra_draw(OVERLAY_Data *vedata) { DRW_draw_pass(vedata->psl->extra_ps[0]); } void OVERLAY_extra_in_front_draw(OVERLAY_Data *vedata) { DRW_draw_pass(vedata->psl->extra_ps[1]); } void OVERLAY_extra_centers_draw(OVERLAY_Data *vedata) { OVERLAY_PassList *psl = vedata->psl; DRW_draw_pass(psl->extra_grid_ps); DRW_draw_pass(psl->extra_centers_ps); }