Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/draw/engines/overlay/overlay_outline.cc')
-rw-r--r--source/blender/draw/engines/overlay/overlay_outline.cc390
1 files changed, 390 insertions, 0 deletions
diff --git a/source/blender/draw/engines/overlay/overlay_outline.cc b/source/blender/draw/engines/overlay/overlay_outline.cc
new file mode 100644
index 00000000000..3213fe144e9
--- /dev/null
+++ b/source/blender/draw/engines/overlay/overlay_outline.cc
@@ -0,0 +1,390 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2019 Blender Foundation. */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#include "DRW_render.h"
+
+#include "BKE_global.h"
+#include "BKE_gpencil.h"
+
+#include "BKE_object.h"
+
+#include "DNA_gpencil_types.h"
+
+#include "UI_resources.h"
+
+#include "overlay_private.hh"
+
+/* Returns the normal plane in NDC space. */
+static void gpencil_depth_plane(Object *ob, float r_plane[4])
+{
+ /* TODO: put that into private data. */
+ float viewinv[4][4];
+ DRW_view_viewmat_get(nullptr, viewinv, true);
+ float *camera_z_axis = viewinv[2];
+ float *camera_pos = viewinv[3];
+
+ /* Find the normal most likely to represent the grease pencil object. */
+ /* TODO: This does not work quite well if you use
+ * strokes not aligned with the object axes. Maybe we could try to
+ * compute the minimum axis of all strokes. But this would be more
+ * computationally heavy and should go into the GPData evaluation. */
+ const BoundBox *bbox = BKE_object_boundbox_get(ob);
+ /* Convert bbox to matrix */
+ float mat[4][4], size[3], center[3];
+ BKE_boundbox_calc_size_aabb(bbox, size);
+ BKE_boundbox_calc_center_aabb(bbox, center);
+ unit_m4(mat);
+ copy_v3_v3(mat[3], center);
+ /* Avoid division by 0.0 later. */
+ add_v3_fl(size, 1e-8f);
+ rescale_m4(mat, size);
+ /* BBox space to World. */
+ mul_m4_m4m4(mat, ob->obmat, mat);
+ /* BBox center in world space. */
+ copy_v3_v3(center, mat[3]);
+ /* View Vector. */
+ if (DRW_view_is_persp_get(nullptr)) {
+ /* BBox center to camera vector. */
+ sub_v3_v3v3(r_plane, camera_pos, mat[3]);
+ }
+ else {
+ copy_v3_v3(r_plane, camera_z_axis);
+ }
+ /* World to BBox space. */
+ invert_m4(mat);
+ /* Normalize the vector in BBox space. */
+ mul_mat3_m4_v3(mat, r_plane);
+ normalize_v3(r_plane);
+
+ transpose_m4(mat);
+ /* mat is now a "normal" matrix which will transform
+ * BBox space normal to world space. */
+ mul_mat3_m4_v3(mat, r_plane);
+ normalize_v3(r_plane);
+
+ plane_from_point_normal_v3(r_plane, center, r_plane);
+}
+
+void OVERLAY_outline_init(OVERLAY_Data *vedata)
+{
+ OVERLAY_FramebufferList *fbl = vedata->fbl;
+ OVERLAY_TextureList *txl = vedata->txl;
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+
+ if (DRW_state_is_fbo()) {
+ /* TODO: only alloc if needed. */
+ DRW_texture_ensure_fullscreen_2d(&txl->temp_depth_tx, GPU_DEPTH24_STENCIL8, DRWTextureFlag(0));
+ DRW_texture_ensure_fullscreen_2d(&txl->outlines_id_tx, GPU_R16UI, DRWTextureFlag(0));
+
+ GPU_framebuffer_ensure_config(
+ &fbl->outlines_prepass_fb,
+ {GPU_ATTACHMENT_TEXTURE(txl->temp_depth_tx), GPU_ATTACHMENT_TEXTURE(txl->outlines_id_tx)});
+
+ if (pd->antialiasing.enabled) {
+ GPU_framebuffer_ensure_config(&fbl->outlines_resolve_fb,
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(txl->overlay_color_tx),
+ GPU_ATTACHMENT_TEXTURE(txl->overlay_line_tx),
+ });
+ }
+ else {
+ GPU_framebuffer_ensure_config(&fbl->outlines_resolve_fb,
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay),
+ });
+ }
+ }
+}
+
+void OVERLAY_outline_cache_init(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+ OVERLAY_TextureList *txl = vedata->txl;
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
+ DRWShadingGroup *grp = nullptr;
+
+ const float outline_width = UI_GetThemeValuef(TH_OUTLINE_WIDTH);
+ const bool do_expand = (U.pixelsize > 1.0) || (outline_width > 2.0f);
+
+ {
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
+ DRW_PASS_CREATE(psl->outlines_prepass_ps, state | pd->clipping_state);
+
+ GPUShader *sh_geom = OVERLAY_shader_outline_prepass(pd->xray_enabled_and_not_wire);
+
+ pd->outlines_grp = grp = DRW_shgroup_create(sh_geom, psl->outlines_prepass_ps);
+ DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
+ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+
+ GPUShader *sh_geom_ptcloud = OVERLAY_shader_outline_prepass_pointcloud();
+
+ pd->outlines_ptcloud_grp = grp = DRW_shgroup_create(sh_geom_ptcloud, psl->outlines_prepass_ps);
+ DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
+ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+
+ GPUShader *sh_gpencil = OVERLAY_shader_outline_prepass_gpencil();
+
+ pd->outlines_gpencil_grp = grp = DRW_shgroup_create(sh_gpencil, psl->outlines_prepass_ps);
+ DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
+ DRW_shgroup_uniform_float_copy(grp, "gpStrokeIndexOffset", 0.0);
+ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+
+ GPUShader *sh_curves = OVERLAY_shader_outline_prepass_curves();
+
+ pd->outlines_curves_grp = grp = DRW_shgroup_create(sh_curves, psl->outlines_prepass_ps);
+ DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0);
+ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+ }
+
+ /* outlines_prepass_ps is still needed for selection of probes. */
+ if (!(pd->v3d_flag & V3D_SELECT_OUTLINE)) {
+ return;
+ }
+
+ {
+ /* We can only do alpha blending with lineOutput just after clearing the buffer. */
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL;
+ DRW_PASS_CREATE(psl->outlines_detect_ps, state);
+
+ GPUShader *sh = OVERLAY_shader_outline_detect();
+
+ grp = DRW_shgroup_create(sh, psl->outlines_detect_ps);
+ /* Don't occlude the "outline" detection pass if in xray mode (too much flickering). */
+ DRW_shgroup_uniform_float_copy(grp, "alphaOcclu", (pd->xray_enabled) ? 1.0f : 0.35f);
+ DRW_shgroup_uniform_bool_copy(grp, "doThickOutlines", do_expand);
+ DRW_shgroup_uniform_bool_copy(grp, "doAntiAliasing", pd->antialiasing.enabled);
+ DRW_shgroup_uniform_bool_copy(grp, "isXrayWires", pd->xray_enabled_and_not_wire);
+ DRW_shgroup_uniform_texture_ref(grp, "outlineId", &txl->outlines_id_tx);
+ DRW_shgroup_uniform_texture_ref(grp, "sceneDepth", &dtxl->depth);
+ DRW_shgroup_uniform_texture_ref(grp, "outlineDepth", &txl->temp_depth_tx);
+ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ }
+}
+
+typedef struct iterData {
+ Object *ob;
+ DRWShadingGroup *stroke_grp;
+ int cfra;
+ float plane[4];
+} iterData;
+
+static void gpencil_layer_cache_populate(bGPDlayer *gpl,
+ bGPDframe * /*gpf*/,
+ bGPDstroke * /*gps*/,
+ void *thunk)
+{
+ iterData *iter = (iterData *)thunk;
+ bGPdata *gpd = (bGPdata *)iter->ob->data;
+
+ const bool is_screenspace = (gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS) != 0;
+ const bool is_stroke_order_3d = (gpd->draw_mode == GP_DRAWMODE_3D);
+
+ float object_scale = mat4_to_scale(iter->ob->obmat);
+ /* Negate thickness sign to tag that strokes are in screen space.
+ * Convert to world units (by default, 1 meter = 2000 pixels). */
+ float thickness_scale = (is_screenspace) ? -1.0f : (gpd->pixfactor / 2000.0f);
+
+ GPUVertBuf *position_tx = DRW_cache_gpencil_position_buffer_get(iter->ob, iter->cfra);
+ GPUVertBuf *color_tx = DRW_cache_gpencil_color_buffer_get(iter->ob, iter->cfra);
+
+ DRWShadingGroup *grp = iter->stroke_grp = DRW_shgroup_create_sub(iter->stroke_grp);
+ DRW_shgroup_uniform_bool_copy(grp, "gpStrokeOrder3d", is_stroke_order_3d);
+ DRW_shgroup_uniform_float_copy(grp, "gpThicknessScale", object_scale);
+ DRW_shgroup_uniform_float_copy(grp, "gpThicknessOffset", float(gpl->line_change));
+ DRW_shgroup_uniform_float_copy(grp, "gpThicknessWorldScale", thickness_scale);
+ DRW_shgroup_uniform_vec4_copy(grp, "gpDepthPlane", iter->plane);
+ DRW_shgroup_buffer_texture(grp, "gp_pos_tx", position_tx);
+ DRW_shgroup_buffer_texture(grp, "gp_col_tx", color_tx);
+}
+
+static void gpencil_stroke_cache_populate(bGPDlayer * /*gpl*/,
+ bGPDframe * /*gpf*/,
+ bGPDstroke *gps,
+ void *thunk)
+{
+ iterData *iter = (iterData *)thunk;
+
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1);
+
+ bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0;
+ bool show_stroke = (gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0;
+ // TODO: What about simplify Fill?
+ bool show_fill = (gps->tot_triangles > 0) && (gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0;
+
+ if (hide_material) {
+ return;
+ }
+
+ struct GPUBatch *geom = DRW_cache_gpencil_get(iter->ob, iter->cfra);
+
+ if (show_fill) {
+ int vfirst = gps->runtime.fill_start * 3;
+ int vcount = gps->tot_triangles * 3;
+ DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount);
+ }
+
+ if (show_stroke) {
+ int vfirst = gps->runtime.stroke_start * 3;
+ bool is_cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0) && (gps->totpoints > 2);
+ int vcount = (gps->totpoints + (int)is_cyclic) * 2 * 3;
+ DRW_shgroup_call_range(iter->stroke_grp, iter->ob, geom, vfirst, vcount);
+ }
+}
+
+static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
+{
+ /* No outlines in edit mode. */
+ bGPdata *gpd = (bGPdata *)ob->data;
+ if (gpd && GPENCIL_ANY_MODE(gpd)) {
+ return;
+ }
+
+ iterData iter{};
+ iter.ob = ob;
+ iter.stroke_grp = pd->outlines_gpencil_grp;
+ iter.cfra = pd->cfra;
+
+ if (gpd->draw_mode == GP_DRAWMODE_2D) {
+ gpencil_depth_plane(ob, iter.plane);
+ }
+
+ BKE_gpencil_visible_stroke_advanced_iter(nullptr,
+ ob,
+ gpencil_layer_cache_populate,
+ gpencil_stroke_cache_populate,
+ &iter,
+ false,
+ pd->cfra);
+}
+
+static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob)
+{
+ struct GPUBatch *geom = DRW_cache_volume_selection_surface_get(ob);
+ if (geom == nullptr) {
+ return;
+ }
+
+ DRWShadingGroup *shgroup = pd->outlines_grp;
+ DRW_shgroup_call(shgroup, geom, ob);
+}
+
+static void OVERLAY_outline_curves(OVERLAY_PrivateData *pd, Object *ob)
+{
+ DRWShadingGroup *shgroup = pd->outlines_curves_grp;
+ DRW_shgroup_curves_create_sub(ob, shgroup, nullptr);
+}
+
+static void OVERLAY_outline_pointcloud(OVERLAY_PrivateData *pd, Object *ob)
+{
+ if (pd->wireframe_mode) {
+ /* Looks bad in this case. Could be relaxed if we draw a
+ * wireframe of some sort in the future. */
+ return;
+ }
+
+ DRWShadingGroup *shgroup = pd->outlines_ptcloud_grp;
+ DRW_shgroup_pointcloud_create_sub(ob, shgroup, nullptr);
+}
+
+void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
+ Object *ob,
+ OVERLAY_DupliData *dupli,
+ bool init_dupli)
+{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ const DRWContextState *draw_ctx = DRW_context_state_get();
+ struct GPUBatch *geom;
+ DRWShadingGroup *shgroup = nullptr;
+ const bool draw_outline = ob->dt > OB_BOUNDBOX;
+
+ /* Early exit: outlines of bounding boxes are not drawn. */
+ if (!draw_outline) {
+ return;
+ }
+
+ if (ob->type == OB_GPENCIL) {
+ OVERLAY_outline_gpencil(pd, ob);
+ return;
+ }
+
+ if (ob->type == OB_VOLUME) {
+ OVERLAY_outline_volume(pd, ob);
+ return;
+ }
+
+ if (ob->type == OB_CURVES) {
+ OVERLAY_outline_curves(pd, ob);
+ return;
+ }
+
+ if (ob->type == OB_POINTCLOUD) {
+ OVERLAY_outline_pointcloud(pd, ob);
+ return;
+ }
+
+ if (dupli && !init_dupli) {
+ geom = dupli->outline_geom;
+ shgroup = dupli->outline_shgrp;
+ }
+ else {
+ /* This fixes only the biggest case which is a plane in ortho view. */
+ int flat_axis = 0;
+ bool is_flat_object_viewed_from_side = ((draw_ctx->rv3d->persp == RV3D_ORTHO) &&
+ DRW_object_is_flat(ob, &flat_axis) &&
+ DRW_object_axis_orthogonal_to_view(ob, flat_axis));
+
+ if (pd->xray_enabled_and_not_wire || is_flat_object_viewed_from_side) {
+ geom = DRW_cache_object_edge_detection_get(ob, nullptr);
+ }
+ else {
+ geom = DRW_cache_object_surface_get(ob);
+ }
+
+ if (geom) {
+ shgroup = pd->outlines_grp;
+ }
+ }
+
+ if (shgroup && geom) {
+ DRW_shgroup_call(shgroup, geom, ob);
+ }
+
+ if (init_dupli) {
+ dupli->outline_shgrp = shgroup;
+ dupli->outline_geom = geom;
+ }
+}
+
+void OVERLAY_outline_draw(OVERLAY_Data *vedata)
+{
+ OVERLAY_FramebufferList *fbl = vedata->fbl;
+ OVERLAY_PassList *psl = vedata->psl;
+ const float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+
+ bool do_outlines = psl->outlines_prepass_ps != nullptr &&
+ !DRW_pass_is_empty(psl->outlines_prepass_ps);
+
+ if (DRW_state_is_fbo() && do_outlines) {
+ DRW_stats_group_start("Outlines");
+
+ /* Render filled polygon on a separate framebuffer */
+ GPU_framebuffer_bind(fbl->outlines_prepass_fb);
+ GPU_framebuffer_clear_color_depth_stencil(fbl->outlines_prepass_fb, clearcol, 1.0f, 0x00);
+ DRW_draw_pass(psl->outlines_prepass_ps);
+
+ /* Search outline pixels */
+ GPU_framebuffer_bind(fbl->outlines_resolve_fb);
+ DRW_draw_pass(psl->outlines_detect_ps);
+
+ DRW_stats_group_end();
+ }
+}