This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/modes/overlay_mode.c
Clément Foucault 9ab99ff26b Sculpt Draw: Add support for wireframe geometry
This introduce the wireframe batches. Creating the indices buffer does
not seems to slow down the sculpt in my testing (but it is kind of hard to
test reliably)

This includes a bit of cleanup in gpu_buffers.c.
2019-02-18 14:17:57 +01:00

434 lines
14 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright 2016, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*/
#include "DNA_mesh_types.h"
#include "DNA_view3d_types.h"
#include "BIF_glutil.h"
#include "BKE_editmesh.h"
#include "BKE_object.h"
#include "BKE_global.h"
#include "GPU_shader.h"
#include "DRW_render.h"
#ifdef __APPLE__
#define USE_GEOM_SHADER_WORKAROUND
#endif
/* Structures */
typedef struct OVERLAY_StorageList {
struct OVERLAY_PrivateData *g_data;
} OVERLAY_StorageList;
typedef struct OVERLAY_PassList {
struct DRWPass *face_orientation_pass;
struct DRWPass *face_wireframe_pass;
} OVERLAY_PassList;
typedef struct OVERLAY_Data {
void *engine_type;
DRWViewportEmptyList *fbl;
DRWViewportEmptyList *txl;
OVERLAY_PassList *psl;
OVERLAY_StorageList *stl;
} OVERLAY_Data;
typedef struct OVERLAY_PrivateData {
DRWShadingGroup *face_orientation_shgrp;
DRWShadingGroup *face_wires_shgrp;
View3DOverlay overlay;
float wire_step_param;
bool ghost_stencil_test;
bool show_overlays;
} OVERLAY_PrivateData; /* Transient data */
typedef struct OVERLAY_Shaders {
/* Face orientation shader */
struct GPUShader *face_orientation;
/* Wireframe shader */
struct GPUShader *select_wireframe;
struct GPUShader *face_wireframe;
} OVERLAY_Shaders;
/* *********** STATIC *********** */
static struct {
OVERLAY_Shaders sh_data[GPU_SHADER_CFG_LEN];
} e_data = {NULL};
/* Shaders */
extern char datatoc_overlay_face_orientation_frag_glsl[];
extern char datatoc_overlay_face_orientation_vert_glsl[];
extern char datatoc_overlay_face_wireframe_vert_glsl[];
extern char datatoc_overlay_face_wireframe_geom_glsl[];
extern char datatoc_overlay_face_wireframe_frag_glsl[];
extern char datatoc_gpu_shader_depth_only_frag_glsl[];
/* Functions */
static void overlay_engine_init(void *vedata)
{
OVERLAY_Data *data = vedata;
OVERLAY_StorageList *stl = data->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) {
DRW_state_clip_planes_set_from_rv3d(draw_ctx->rv3d);
}
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
}
stl->g_data->ghost_stencil_test = false;
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[draw_ctx->sh_cfg];
if (!sh_data->face_orientation) {
/* Face orientation */
sh_data->face_orientation = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, datatoc_overlay_face_orientation_vert_glsl, NULL},
.frag = (const char *[]){datatoc_overlay_face_orientation_frag_glsl, NULL},
.defs = (const char *[]){sh_cfg_data->def, NULL},
});
}
if (!sh_data->face_wireframe) {
sh_data->select_wireframe = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, datatoc_overlay_face_wireframe_vert_glsl, NULL},
.geom = (const char *[]){sh_cfg_data->lib, datatoc_overlay_face_wireframe_geom_glsl, NULL},
.frag = (const char *[]){datatoc_gpu_shader_depth_only_frag_glsl, NULL},
.defs = (const char *[]){sh_cfg_data->def, "#define SELECT_EDGES\n", NULL},
});
#ifdef USE_GEOM_SHADER_WORKAROUND
/* Apple drivers does not support wide wires. Use geometry shader as a workaround. */
sh_data->face_wireframe = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, datatoc_overlay_face_wireframe_vert_glsl, NULL},
.geom = (const char *[]){sh_cfg_data->lib, datatoc_overlay_face_wireframe_geom_glsl, NULL},
.frag = (const char *[]){datatoc_overlay_face_wireframe_frag_glsl, NULL},
.defs = (const char *[]){sh_cfg_data->def, "#define USE_GEOM\n", NULL},
});
#else
sh_data->face_wireframe = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib, datatoc_overlay_face_wireframe_vert_glsl, NULL},
.frag = (const char *[]){datatoc_overlay_face_wireframe_frag_glsl, NULL},
.defs = (const char *[]){sh_cfg_data->def, NULL},
});
#endif
}
}
static void overlay_cache_init(void *vedata)
{
OVERLAY_Data *data = vedata;
OVERLAY_PassList *psl = data->psl;
OVERLAY_StorageList *stl = data->stl;
OVERLAY_PrivateData *g_data = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
RegionView3D *rv3d = draw_ctx->rv3d;
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
const DRWContextState *DCS = DRW_context_state_get();
View3D *v3d = DCS->v3d;
if (v3d) {
g_data->overlay = v3d->overlay;
g_data->show_overlays = (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0;
}
else {
memset(&g_data->overlay, 0, sizeof(g_data->overlay));
g_data->show_overlays = false;
}
if (g_data->show_overlays == false) {
g_data->overlay.flag = 0;
}
if (v3d->shading.type == OB_WIRE) {
g_data->overlay.flag |= V3D_OVERLAY_WIREFRAMES;
g_data->show_overlays = true;
}
{
/* Face Orientation Pass */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND;
psl->face_orientation_pass = DRW_pass_create("Face Orientation", state);
g_data->face_orientation_shgrp = DRW_shgroup_create(
sh_data->face_orientation, psl->face_orientation_pass);
if (rv3d->rflag & RV3D_CLIPPING) {
DRW_shgroup_world_clip_planes_from_rv3d(g_data->face_orientation_shgrp, rv3d);
}
}
{
/* Wireframe */
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS |
DRW_STATE_FIRST_VERTEX_CONVENTION | DRW_STATE_OFFSET_NEGATIVE;
float wire_size = U.pixelsize * 0.5f;
float winmat[4][4];
float viewdist = rv3d->dist;
DRW_viewport_matrix_get(winmat, DRW_MAT_WIN);
/* special exception for ortho camera (viewdist isnt used for perspective cameras) */
if (rv3d->persp == RV3D_CAMOB && rv3d->is_persp == false) {
viewdist = 1.0f / max_ff(fabsf(rv3d->winmat[0][0]), fabsf(rv3d->winmat[1][1]));
}
const float depth_ofs = bglPolygonOffsetCalc((float *)winmat, viewdist, 1.0f);
const bool use_select = (DRW_state_is_select() || DRW_state_is_depth());
GPUShader *face_wires_sh = use_select ? sh_data->select_wireframe : sh_data->face_wireframe;
psl->face_wireframe_pass = DRW_pass_create("Face Wires", state);
g_data->face_wires_shgrp = DRW_shgroup_create(face_wires_sh, psl->face_wireframe_pass);
DRW_shgroup_uniform_float(g_data->face_wires_shgrp, "wireStepParam", &g_data->wire_step_param, 1);
DRW_shgroup_uniform_float_copy(g_data->face_wires_shgrp, "ofs", depth_ofs);
#ifdef USE_GEOM_SHADER_WORKAROUND
DRW_shgroup_uniform_float_copy(g_data->face_wires_shgrp, "wireSize", wire_size);
DRW_shgroup_uniform_vec2(g_data->face_wires_shgrp, "viewportSize", DRW_viewport_size_get(), 1);
DRW_shgroup_uniform_vec2(g_data->face_wires_shgrp, "viewportSizeInv", DRW_viewport_invert_size_get(), 1);
#else
if (!use_select) {
DRW_shgroup_uniform_float_copy(g_data->face_wires_shgrp, "wireSize", wire_size);
}
#endif
if (rv3d->rflag & RV3D_CLIPPING) {
DRW_shgroup_world_clip_planes_from_rv3d(g_data->face_wires_shgrp, rv3d);
}
g_data->wire_step_param = stl->g_data->overlay.wireframe_threshold - 254.0f / 255.0f;
}
}
static void overlay_cache_populate(void *vedata, Object *ob)
{
OVERLAY_Data *data = vedata;
OVERLAY_StorageList *stl = data->stl;
OVERLAY_PrivateData *pd = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
if ((!pd->show_overlays) ||
(ob->dt < OB_WIRE) ||
(!DRW_object_is_renderable(ob) && (ob->dt != OB_WIRE)))
{
return;
}
if (DRW_object_is_renderable(ob) && pd->overlay.flag & V3D_OVERLAY_FACE_ORIENTATION) {
struct GPUBatch *geom = DRW_cache_object_surface_get(ob);
if (geom) {
DRW_shgroup_call_add(pd->face_orientation_shgrp, geom, ob->obmat);
}
}
if ((pd->overlay.flag & V3D_OVERLAY_WIREFRAMES) ||
(v3d->shading.type == OB_WIRE) ||
(ob->dtx & OB_DRAWWIRE) ||
(ob->dt == OB_WIRE))
{
const bool is_edit_mode = BKE_object_is_in_editmode(ob);
bool has_edit_mesh_cage = false;
if (ob->type == OB_MESH) {
/* TODO: Should be its own function. */
Mesh *me = (Mesh *)ob->data;
BMEditMesh *embm = me->edit_mesh;
if (embm) {
has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final);
}
}
/* Don't do that in edit Mesh mode, unless there is a modifier preview. */
if ((((ob != draw_ctx->object_edit) && !is_edit_mode) || has_edit_mesh_cage) ||
ob->type != OB_MESH)
{
const bool is_active = (ob == draw_ctx->obact);
const bool is_sculpt_mode = is_active && (draw_ctx->object_mode & OB_MODE_SCULPT) != 0;
const bool all_wires = (ob->dtx & OB_DRAW_ALL_EDGES);
const bool is_wire = (ob->dt < OB_SOLID);
const int stencil_mask = (ob->dtx & OB_DRAWXRAY) ? 0x00 : 0xFF;
DRWShadingGroup *shgrp = NULL;
const float *rim_col = NULL;
const float *wire_col = NULL;
if (UNLIKELY(ob->base_flag & BASE_FROM_SET)) {
rim_col = G_draw.block.colorDupli;
wire_col = G_draw.block.colorDupli;
}
else if (UNLIKELY(ob->base_flag & BASE_FROM_DUPLI)) {
if (ob->base_flag & BASE_SELECTED) {
if (G.moving & G_TRANSFORM_OBJ) {
rim_col = G_draw.block.colorTransform;
}
else {
rim_col = G_draw.block.colorDupliSelect;
}
}
else {
rim_col = G_draw.block.colorDupli;
}
wire_col = G_draw.block.colorDupli;
}
else if ((ob->base_flag & BASE_SELECTED) &&
(!is_edit_mode && !is_sculpt_mode && !has_edit_mesh_cage))
{
if (G.moving & G_TRANSFORM_OBJ) {
rim_col = G_draw.block.colorTransform;
}
else if (ob == draw_ctx->obact) {
rim_col = G_draw.block.colorActive;
}
else {
rim_col = G_draw.block.colorSelect;
}
wire_col = G_draw.block.colorWire;
}
else {
rim_col = G_draw.block.colorWire;
wire_col = G_draw.block.colorWire;
}
BLI_assert(rim_col && wire_col);
struct GPUBatch *geom;
geom = DRW_cache_object_face_wireframe_get(ob);
if (geom || is_sculpt_mode) {
shgrp = DRW_shgroup_create_sub(pd->face_wires_shgrp);
float wire_step_param = 10.0f;
if (!is_sculpt_mode) {
wire_step_param = (all_wires) ? 1.0f : pd->wire_step_param;
}
DRW_shgroup_uniform_float_copy(shgrp, "wireStepParam", wire_step_param);
if (!(DRW_state_is_select() || DRW_state_is_depth())) {
DRW_shgroup_stencil_mask(shgrp, stencil_mask);
DRW_shgroup_uniform_vec3(shgrp, "wireColor", wire_col, 1);
DRW_shgroup_uniform_vec3(shgrp, "rimColor", rim_col, 1);
}
if (is_sculpt_mode) {
DRW_shgroup_call_sculpt_wires_add(shgrp, ob, ob->obmat);
}
else {
DRW_shgroup_call_add(shgrp, geom, ob->obmat);
}
}
if (is_wire && shgrp != NULL) {
/* If object is wireframe, don't try to use stencil test. */
DRW_shgroup_state_disable(shgrp, DRW_STATE_STENCIL_EQUAL);
if (ob->dtx & OB_DRAWXRAY) {
DRW_shgroup_state_disable(shgrp, DRW_STATE_DEPTH_LESS_EQUAL);
}
}
else if ((ob->dtx & OB_DRAWXRAY) && shgrp != NULL) {
pd->ghost_stencil_test = true;
}
}
}
}
static void overlay_cache_finish(void *vedata)
{
OVERLAY_Data *data = vedata;
OVERLAY_PassList *psl = data->psl;
OVERLAY_StorageList *stl = data->stl;
const DRWContextState *ctx = DRW_context_state_get();
View3D *v3d = ctx->v3d;
/* only in solid mode */
if (v3d->shading.type == OB_SOLID && (v3d->shading.flag & XRAY_FLAG(v3d)) == 0) {
if (stl->g_data->ghost_stencil_test) {
DRW_pass_state_add(psl->face_wireframe_pass, DRW_STATE_STENCIL_EQUAL);
}
}
}
static void overlay_draw_scene(void *vedata)
{
OVERLAY_Data *data = vedata;
OVERLAY_PassList *psl = data->psl;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(dfbl->default_fb);
}
DRW_draw_pass(psl->face_orientation_pass);
MULTISAMPLE_SYNC_ENABLE(dfbl, dtxl);
if (dfbl->multisample_fb != NULL) {
DRW_stats_query_start("Multisample Blit");
GPU_framebuffer_bind(dfbl->multisample_fb);
GPU_framebuffer_clear_color(dfbl->multisample_fb, (const float[4]){0.0f});
/* Special blit: we need the original depth and stencil
* in the Multisample buffer. */
GPU_framebuffer_blit(dfbl->default_fb, 0,
dfbl->multisample_fb, 0,
GPU_DEPTH_BIT | GPU_STENCIL_BIT);
DRW_stats_query_end();
}
DRW_draw_pass(psl->face_wireframe_pass);
/* TODO(fclem): find a way to unify the multisample pass together
* (non meshes + armature + wireframe) */
MULTISAMPLE_SYNC_DISABLE(dfbl, dtxl);
}
static void overlay_engine_free(void)
{
for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) {
OVERLAY_Shaders *sh_data = &e_data.sh_data[sh_data_index];
GPUShader **sh_data_as_array = (GPUShader **)sh_data;
for (int i = 0; i < (sizeof(OVERLAY_Shaders) / sizeof(GPUShader *)); i++) {
DRW_SHADER_FREE_SAFE(sh_data_as_array[i]);
}
}
}
static const DrawEngineDataSize overlay_data_size = DRW_VIEWPORT_DATA_SIZE(OVERLAY_Data);
DrawEngineType draw_engine_overlay_type = {
NULL, NULL,
N_("OverlayEngine"),
&overlay_data_size,
&overlay_engine_init,
&overlay_engine_free,
&overlay_cache_init,
&overlay_cache_populate,
&overlay_cache_finish,
NULL,
&overlay_draw_scene,
NULL,
NULL,
NULL,
};