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/engines/select/select_engine.c
Jeroen Bakker 1f41bdc6f3 Eevee Cryptomatte: Store hashes in render result meta data
Stores cryptomatte hashes as meta data to the render result. Compositors could
use this for lookup on names in stead of hashes.

Differential Revision: https://developer.blender.org/D9553
2021-01-05 15:03:05 +01:00

419 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 2019, Blender Foundation.
*/
/** \file
* \ingroup draw_engine
*
* Engine for drawing a selection map where the pixels indicate the selection indices.
*/
#include "DNA_screen_types.h"
#include "UI_resources.h"
#include "DRW_engine.h"
#include "DRW_select_buffer.h"
#include "draw_cache_impl.h"
#include "draw_manager.h"
#include "select_engine.h"
#include "select_private.h"
#define SELECT_ENGINE "SELECT_ENGINE"
/* *********** STATIC *********** */
static struct {
struct GPUFrameBuffer *framebuffer_select_id;
struct GPUTexture *texture_u32;
SELECTID_Shaders sh_data[GPU_SHADER_CFG_LEN];
struct SELECTID_Context context;
uint runtime_new_objects;
} e_data = {NULL}; /* Engine data */
/* Shaders */
extern char datatoc_common_view_lib_glsl[];
extern char datatoc_selection_id_3D_vert_glsl[];
extern char datatoc_selection_id_frag_glsl[];
/* -------------------------------------------------------------------- */
/** \name Utils
* \{ */
static void select_engine_framebuffer_setup(void)
{
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
int size[2];
size[0] = GPU_texture_width(dtxl->depth);
size[1] = GPU_texture_height(dtxl->depth);
if (e_data.framebuffer_select_id == NULL) {
e_data.framebuffer_select_id = GPU_framebuffer_create("framebuffer_select_id");
}
if ((e_data.texture_u32 != NULL) && ((GPU_texture_width(e_data.texture_u32) != size[0]) ||
(GPU_texture_height(e_data.texture_u32) != size[1]))) {
GPU_texture_free(e_data.texture_u32);
e_data.texture_u32 = NULL;
}
/* Make sure the depth texture is attached.
* It may disappear when loading another Blender session. */
GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, dtxl->depth, 0, 0);
if (e_data.texture_u32 == NULL) {
e_data.texture_u32 = GPU_texture_create_2d(
"select_buf_ids", size[0], size[1], 1, GPU_R32UI, NULL);
GPU_framebuffer_texture_attach(e_data.framebuffer_select_id, e_data.texture_u32, 0, 0);
GPU_framebuffer_check_valid(e_data.framebuffer_select_id, NULL);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Functions
* \{ */
static void select_engine_init(void *vedata)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
eGPUShaderConfig sh_cfg = draw_ctx->sh_cfg;
SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl;
SELECTID_Shaders *sh_data = &e_data.sh_data[sh_cfg];
/* Prepass */
if (!sh_data->select_id_flat) {
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg];
sh_data->select_id_flat = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib,
datatoc_common_view_lib_glsl,
datatoc_selection_id_3D_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_selection_id_frag_glsl, NULL},
.defs = (const char *[]){sh_cfg_data->def, NULL},
});
}
if (!sh_data->select_id_uniform) {
const GPUShaderConfigData *sh_cfg_data = &GPU_shader_cfg_data[sh_cfg];
sh_data->select_id_uniform = GPU_shader_create_from_arrays({
.vert = (const char *[]){sh_cfg_data->lib,
datatoc_common_view_lib_glsl,
datatoc_selection_id_3D_vert_glsl,
NULL},
.frag = (const char *[]){datatoc_selection_id_frag_glsl, NULL},
.defs = (const char *[]){sh_cfg_data->def, "#define UNIFORM_ID\n", NULL},
});
}
if (!stl->g_data) {
/* Alloc transient pointers */
stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__);
}
{
/* Create view from a subregion */
const DRWView *view_default = DRW_view_default_get();
float viewmat[4][4], winmat[4][4], winmat_subregion[4][4];
DRW_view_viewmat_get(view_default, viewmat, false);
DRW_view_winmat_get(view_default, winmat, false);
projmat_from_subregion(winmat,
(int[2]){draw_ctx->region->winx, draw_ctx->region->winy},
e_data.context.last_rect.xmin,
e_data.context.last_rect.xmax,
e_data.context.last_rect.ymin,
e_data.context.last_rect.ymax,
winmat_subregion);
stl->g_data->view_subregion = DRW_view_create(viewmat, winmat_subregion, NULL, NULL, NULL);
/* Create view with depth offset */
stl->g_data->view_faces = (DRWView *)view_default;
stl->g_data->view_edges = DRW_view_create_with_zoffset(view_default, draw_ctx->rv3d, 1.0f);
stl->g_data->view_verts = DRW_view_create_with_zoffset(view_default, draw_ctx->rv3d, 1.1f);
}
}
static void select_cache_init(void *vedata)
{
SELECTID_PassList *psl = ((SELECTID_Data *)vedata)->psl;
SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl;
SELECTID_PrivateData *pd = stl->g_data;
const DRWContextState *draw_ctx = DRW_context_state_get();
SELECTID_Shaders *sh = &e_data.sh_data[draw_ctx->sh_cfg];
if (e_data.context.select_mode == -1) {
e_data.context.select_mode = select_id_get_object_select_mode(draw_ctx->scene,
draw_ctx->obact);
BLI_assert(e_data.context.select_mode != 0);
}
DRWState state = DRW_STATE_DEFAULT;
state |= RV3D_CLIPPING_ENABLED(draw_ctx->v3d, draw_ctx->rv3d) ? DRW_STATE_CLIP_PLANES : 0;
{
DRW_PASS_CREATE(psl->depth_only_pass, state);
pd->shgrp_depth_only = DRW_shgroup_create(sh->select_id_uniform, psl->depth_only_pass);
DRW_PASS_CREATE(psl->select_id_face_pass, state);
if (e_data.context.select_mode & SCE_SELECT_FACE) {
pd->shgrp_face_flat = DRW_shgroup_create(sh->select_id_flat, psl->select_id_face_pass);
}
else {
pd->shgrp_face_unif = DRW_shgroup_create(sh->select_id_uniform, psl->select_id_face_pass);
DRW_shgroup_uniform_int_copy(pd->shgrp_face_unif, "id", 0);
}
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
DRW_PASS_CREATE(psl->select_id_edge_pass, state | DRW_STATE_FIRST_VERTEX_CONVENTION);
pd->shgrp_edge = DRW_shgroup_create(sh->select_id_flat, psl->select_id_edge_pass);
}
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
DRW_PASS_CREATE(psl->select_id_vert_pass, state);
pd->shgrp_vert = DRW_shgroup_create(sh->select_id_flat, psl->select_id_vert_pass);
DRW_shgroup_uniform_float_copy(pd->shgrp_vert, "sizeVertex", G_draw.block.sizeVertex);
}
}
/* Check if the viewport has changed. */
float(*persmat)[4] = draw_ctx->rv3d->persmat;
e_data.context.is_dirty = !compare_m4m4(e_data.context.persmat, persmat, FLT_EPSILON);
if (!e_data.context.is_dirty) {
/* Check if any of the drawn objects have been transformed. */
Object **ob = &e_data.context.objects_drawn[0];
for (uint i = e_data.context.objects_drawn_len; i--; ob++) {
DrawData *data = DRW_drawdata_get(&(*ob)->id, &draw_engine_select_type);
if (data && (data->recalc & ID_RECALC_TRANSFORM) != 0) {
data->recalc &= ~ID_RECALC_TRANSFORM;
e_data.context.is_dirty = true;
}
}
}
if (e_data.context.is_dirty) {
/* Remove all tags from drawn or culled objects. */
copy_m4_m4(e_data.context.persmat, persmat);
e_data.context.objects_drawn_len = 0;
e_data.context.index_drawn_len = 1;
select_engine_framebuffer_setup();
GPU_framebuffer_bind(e_data.framebuffer_select_id);
GPU_framebuffer_clear_color_depth(e_data.framebuffer_select_id, (const float[4]){0.0f}, 1.0f);
}
e_data.runtime_new_objects = 0;
}
static void select_cache_populate(void *vedata, Object *ob)
{
SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
SELECTID_ObjectData *sel_data = (SELECTID_ObjectData *)DRW_drawdata_get(
&ob->id, &draw_engine_select_type);
if (!e_data.context.is_dirty && sel_data && sel_data->is_drawn) {
/* The object indices have already been drawn. Fill depth pass.
* Opti: Most of the time this depth pass is not used. */
struct Mesh *me = ob->data;
if (e_data.context.select_mode & SCE_SELECT_FACE) {
struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me);
DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_faces, ob->obmat);
}
else if (ob->dt >= OB_SOLID) {
#ifdef USE_CAGE_OCCLUSION
struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me);
#else
struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_surface(me);
#endif
DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_faces, ob->obmat);
}
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
struct GPUBatch *geom_edges = DRW_mesh_batch_cache_get_edges_with_select_id(me);
DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_edges, ob->obmat);
}
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
struct GPUBatch *geom_verts = DRW_mesh_batch_cache_get_verts_with_select_id(me);
DRW_shgroup_call_obmat(stl->g_data->shgrp_depth_only, geom_verts, ob->obmat);
}
return;
}
float min[3], max[3];
select_id_object_min_max(ob, min, max);
if (DRW_culling_min_max_test(stl->g_data->view_subregion, ob->obmat, min, max)) {
if (sel_data == NULL) {
sel_data = (SELECTID_ObjectData *)DRW_drawdata_ensure(
&ob->id, &draw_engine_select_type, sizeof(SELECTID_ObjectData), NULL, NULL);
}
sel_data->dd.recalc = 0;
sel_data->drawn_index = e_data.context.objects_drawn_len;
sel_data->is_drawn = true;
struct ObjectOffsets *ob_offsets =
&e_data.context.index_offsets[e_data.context.objects_drawn_len];
uint offset = e_data.context.index_drawn_len;
select_id_draw_object(vedata,
draw_ctx->v3d,
ob,
e_data.context.select_mode,
offset,
&ob_offsets->vert,
&ob_offsets->edge,
&ob_offsets->face);
ob_offsets->offset = offset;
e_data.context.index_drawn_len = ob_offsets->vert;
e_data.context.objects_drawn[e_data.context.objects_drawn_len] = ob;
e_data.context.objects_drawn_len++;
e_data.runtime_new_objects++;
}
else if (sel_data) {
sel_data->is_drawn = false;
}
}
static void select_draw_scene(void *vedata)
{
SELECTID_StorageList *stl = ((SELECTID_Data *)vedata)->stl;
SELECTID_PassList *psl = ((SELECTID_Data *)vedata)->psl;
if (!e_data.runtime_new_objects) {
/* Nothing new needs to be drawn. */
return;
}
DRW_view_set_active(stl->g_data->view_faces);
if (!DRW_pass_is_empty(psl->depth_only_pass)) {
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->depth_only_fb);
GPU_framebuffer_clear_depth(dfbl->depth_only_fb, 1.0f);
DRW_draw_pass(psl->depth_only_pass);
}
/* Setup framebuffer */
GPU_framebuffer_bind(e_data.framebuffer_select_id);
DRW_draw_pass(psl->select_id_face_pass);
if (e_data.context.select_mode & SCE_SELECT_EDGE) {
DRW_view_set_active(stl->g_data->view_edges);
DRW_draw_pass(psl->select_id_edge_pass);
}
if (e_data.context.select_mode & SCE_SELECT_VERTEX) {
DRW_view_set_active(stl->g_data->view_verts);
DRW_draw_pass(psl->select_id_vert_pass);
}
}
static void select_engine_free(void)
{
for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) {
SELECTID_Shaders *sh_data = &e_data.sh_data[sh_data_index];
DRW_SHADER_FREE_SAFE(sh_data->select_id_flat);
DRW_SHADER_FREE_SAFE(sh_data->select_id_uniform);
}
DRW_TEXTURE_FREE_SAFE(e_data.texture_u32);
GPU_FRAMEBUFFER_FREE_SAFE(e_data.framebuffer_select_id);
MEM_SAFE_FREE(e_data.context.objects);
MEM_SAFE_FREE(e_data.context.index_offsets);
MEM_SAFE_FREE(e_data.context.objects_drawn);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Engine Type
* \{ */
static const DrawEngineDataSize select_data_size = DRW_VIEWPORT_DATA_SIZE(SELECTID_Data);
DrawEngineType draw_engine_select_type = {
NULL,
NULL,
N_("Select ID"),
&select_data_size,
&select_engine_init,
&select_engine_free,
&select_cache_init,
&select_cache_populate,
NULL,
&select_draw_scene,
NULL,
NULL,
NULL,
NULL,
};
/* Note: currently unused, we may want to register so we can see this when debugging the view. */
RenderEngineType DRW_engine_viewport_select_type = {
NULL,
NULL,
SELECT_ENGINE,
N_("Select ID"),
RE_INTERNAL | RE_USE_STEREO_VIEWPORT | RE_USE_GPU_CONTEXT,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&draw_engine_select_type,
{NULL, NULL, NULL},
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Exposed `select_private.h` functions
* \{ */
struct SELECTID_Context *DRW_select_engine_context_get(void)
{
return &e_data.context;
}
GPUFrameBuffer *DRW_engine_select_framebuffer_get(void)
{
return e_data.framebuffer_select_id;
}
GPUTexture *DRW_engine_select_texture_get(void)
{
return e_data.texture_u32;
}
/** \} */
#undef SELECT_ENGINE