diff options
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_materials.c | 5 | ||||
-rw-r--r-- | source/blender/draw/intern/DRW_render.h | 54 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.c | 87 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager.h | 84 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager_data.c | 535 | ||||
-rw-r--r-- | source/blender/draw/intern/draw_manager_exec.c | 484 | ||||
-rw-r--r-- | source/blender/draw/modes/edit_mesh_mode.c | 3 | ||||
-rw-r--r-- | source/blender/draw/modes/shaders/common_view_lib.glsl | 5 | ||||
-rw-r--r-- | source/blender/gpu/GPU_viewport.h | 2 | ||||
-rw-r--r-- | source/blender/gpu/intern/gpu_viewport.c | 6 |
10 files changed, 775 insertions, 490 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 44e9b98e8e2..6e0bd7f31d3 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -1130,11 +1130,10 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) #define ADD_SHGROUP_CALL(shgrp, ob, geom, oedata) \ do { \ if (oedata) { \ - DRW_shgroup_call_object_with_callback( \ - shgrp, geom, ob, EEVEE_lightprobes_obj_visibility_cb, oedata); \ + DRW_shgroup_call_object_with_callback(shgrp, geom, ob, oedata); \ } \ else { \ - DRW_shgroup_call_object_ex(shgrp, geom, ob, false); \ + DRW_shgroup_call_object(shgrp, geom, ob); \ } \ } while (0) diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 6d290f9a2d3..391d041e408 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -81,6 +81,7 @@ struct rcti; typedef struct DRWInterface DRWInterface; typedef struct DRWPass DRWPass; +typedef struct DRWView DRWView; typedef struct DRWShadingGroup DRWShadingGroup; typedef struct DRWUniform DRWUniform; @@ -411,10 +412,10 @@ void DRW_shgroup_call_object_ex(DRWShadingGroup *shgroup, DRW_shgroup_call_object_ex(shgroup, geom, ob, true) /* TODO(fclem) remove this when we have DRWView */ +/* user_data is used by DRWCallVisibilityFn defined in DRWView. */ void DRW_shgroup_call_object_with_callback(DRWShadingGroup *shgroup, struct GPUBatch *geom, struct Object *ob, - DRWCallVisibilityFn *callback, void *user_data); void DRW_shgroup_call_instances(DRWShadingGroup *shgroup, @@ -520,6 +521,7 @@ bool DRW_shgroup_is_empty(DRWShadingGroup *shgroup); /* Passes */ DRWPass *DRW_pass_create(const char *name, DRWState state); +/* TODO Replace with passes inheritance. */ void DRW_pass_state_set(DRWPass *pass, DRWState state); void DRW_pass_state_add(DRWPass *pass, DRWState state); void DRW_pass_state_remove(DRWPass *pass, DRWState state); @@ -532,6 +534,50 @@ bool DRW_pass_is_empty(DRWPass *pass); #define DRW_PASS_CREATE(pass, state) (pass = DRW_pass_create(#pass, state)) +/* Views */ +DRWView *DRW_view_create(const float viewmat[4][4], + const float winmat[4][4], + const float (*culling_viewmat)[4], + const float (*culling_winmat)[4], + DRWCallVisibilityFn *visibility_fn); +DRWView *DRW_view_create_sub(const DRWView *parent_view, + const float viewmat[4][4], + const float winmat[4][4]); + +void DRW_view_update(DRWView *view, + const float viewmat[4][4], + const float winmat[4][4], + const float (*culling_viewmat)[4], + const float (*culling_winmat)[4]); +void DRW_view_update_sub(DRWView *view, const float viewmat[4][4], const float winmat[4][4]); + +const DRWView *DRW_view_default_get(void); +void DRW_view_default_set(DRWView *view); + +void DRW_view_set_active(DRWView *view); + +void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); + +/* For all getters, if view is NULL, default view is assumed. */ +void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse); +void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse); +void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse); + +void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners); +void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4]); + +/* These are in view-space, so negative if in perspective. + * Extract near and far clip distance from the projection matrix. */ +float DRW_view_near_distance_get(const DRWView *view); +float DRW_view_far_distance_get(const DRWView *view); +bool DRW_view_is_persp_get(const DRWView *view); + +/* Culling, return true if object is inside view frustum. */ +/* TODO */ +// bool DRW_culling_sphere_test(DRWView *view, BoundSphere *bsphere); +// bool DRW_culling_box_test(DRWView *view, BoundBox *bbox); +// bool DRW_culling_plane_test(DRWView *view, float plane[4]); + /* Viewport */ typedef enum { /* keep in sync with the union struct DRWMatrixState. */ @@ -645,9 +691,9 @@ void DRW_state_clip_planes_reset(void); void DRW_state_clip_planes_set_from_rv3d(struct RegionView3D *rv3d); /* Culling, return true if object is inside view frustum. */ -bool DRW_culling_sphere_test(BoundSphere *bsphere); -bool DRW_culling_box_test(BoundBox *bbox); -bool DRW_culling_plane_test(float plane[4]); +bool DRW_culling_sphere_test(const BoundSphere *bsphere); +bool DRW_culling_box_test(const BoundBox *bbox); +bool DRW_culling_plane_test(const float plane[4]); void DRW_culling_frustum_corners_get(BoundBox *corners); void DRW_culling_frustum_planes_get(float planes[6][4]); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 8ded73aa678..90e1ff708d9 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -538,9 +538,11 @@ static void drw_viewport_cache_resize(void) BLI_memblock_clear(DST.vmempool->calls, NULL); BLI_memblock_clear(DST.vmempool->states, NULL); + BLI_memblock_clear(DST.vmempool->cullstates, NULL); BLI_memblock_clear(DST.vmempool->shgroups, NULL); BLI_memblock_clear(DST.vmempool->uniforms, NULL); BLI_memblock_clear(DST.vmempool->passes, NULL); + BLI_memblock_clear(DST.vmempool->views, NULL); BLI_memblock_clear(DST.vmempool->images, NULL); } @@ -611,12 +613,18 @@ static void drw_viewport_var_init(void) if (DST.vmempool->states == NULL) { DST.vmempool->states = BLI_memblock_create(sizeof(DRWCallState), false); } + if (DST.vmempool->cullstates == NULL) { + DST.vmempool->cullstates = BLI_memblock_create(sizeof(DRWCullingState), false); + } if (DST.vmempool->shgroups == NULL) { DST.vmempool->shgroups = BLI_memblock_create(sizeof(DRWShadingGroup), false); } if (DST.vmempool->uniforms == NULL) { DST.vmempool->uniforms = BLI_memblock_create(sizeof(DRWUniform), false); } + if (DST.vmempool->views == NULL) { + DST.vmempool->views = BLI_memblock_create(sizeof(DRWView), false); + } if (DST.vmempool->passes == NULL) { DST.vmempool->passes = BLI_memblock_create(sizeof(DRWPass), false); } @@ -638,31 +646,38 @@ static void drw_viewport_var_init(void) DST.vmempool = NULL; } + DST.primary_view_ct = 0; + if (rv3d != NULL) { - /* Refresh DST.screenvecs */ - copy_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]); - copy_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]); - normalize_v3(DST.screenvecs[0]); - normalize_v3(DST.screenvecs[1]); + normalize_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]); + normalize_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]); - /* Refresh DST.pixelsize */ DST.pixsize = rv3d->pixsize; + DST.view_default = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL); + copy_v4_v4(DST.view_default->storage.viewcamtexcofac, rv3d->viewcamtexcofac); - copy_m4_m4(DST.original_mat.mat[DRW_MAT_PERS], rv3d->persmat); - copy_m4_m4(DST.original_mat.mat[DRW_MAT_PERSINV], rv3d->persinv); - copy_m4_m4(DST.original_mat.mat[DRW_MAT_VIEW], rv3d->viewmat); - copy_m4_m4(DST.original_mat.mat[DRW_MAT_VIEWINV], rv3d->viewinv); - copy_m4_m4(DST.original_mat.mat[DRW_MAT_WIN], rv3d->winmat); - invert_m4_m4(DST.original_mat.mat[DRW_MAT_WININV], rv3d->winmat); - - memcpy(DST.view_data.matstate.mat, DST.original_mat.mat, sizeof(DST.original_mat.mat)); + if (DST.draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) { + int plane_len = (rv3d->viewlock & RV3D_BOXCLIP) ? 4 : 6; + DRW_view_clip_planes_set(DST.view_default, rv3d->clip, plane_len); + } - copy_v4_v4(DST.view_data.viewcamtexcofac, rv3d->viewcamtexcofac); + /* TODO should be set to NULL. */ + DST.view_active = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL); } else { - copy_v4_fl4(DST.view_data.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); + zero_v3(DST.screenvecs[0]); + zero_v3(DST.screenvecs[1]); + + DST.pixsize = 1.0f; + DST.view_default = NULL; + + /* TODO should be set to NULL. */ + float mat[4][4]; + unit_m4(mat); + DST.view_active = DRW_view_create(mat, mat, NULL, NULL, NULL); } + /* fclem: Is this still needed ? */ if (DST.draw_ctx.object_edit) { ED_view3d_init_mats_rv3d(DST.draw_ctx.object_edit, rv3d); } @@ -674,61 +689,49 @@ static void drw_viewport_var_init(void) G_draw.view_ubo = DRW_uniformbuffer_create(sizeof(ViewUboStorage), NULL); } - DST.override_mat = 0; - DST.dirty_mat = true; - DST.state_cache_id = 1; - - DST.clipping.updated = false; - memset(DST.object_instance_data, 0x0, sizeof(DST.object_instance_data)); } +/* TODO remove all of the DRW_viewport_matrix_* functions. */ + void DRW_viewport_matrix_get(float mat[4][4], DRWViewportMatrixType type) { BLI_assert(type >= 0 && type < DRW_MAT_COUNT); /* Can't use this in render mode. */ - BLI_assert(((DST.override_mat & (1 << type)) != 0) || DST.draw_ctx.rv3d != NULL); + // BLI_assert(((DST.override_mat & (1 << type)) != 0) || DST.draw_ctx.rv3d != NULL); - copy_m4_m4(mat, DST.view_data.matstate.mat[type]); + copy_m4_m4(mat, DST.view_active->storage.matstate.mat[type]); } void DRW_viewport_matrix_get_all(DRWMatrixState *state) { - memcpy(state, DST.view_data.matstate.mat, sizeof(DRWMatrixState)); + memcpy(state, DST.view_active->storage.matstate.mat, sizeof(DRWMatrixState)); } void DRW_viewport_matrix_override_set(const float mat[4][4], DRWViewportMatrixType type) { BLI_assert(type < DRW_MAT_COUNT); - copy_m4_m4(DST.view_data.matstate.mat[type], mat); - DST.override_mat |= (1 << type); - DST.dirty_mat = true; - DST.clipping.updated = false; + copy_m4_m4(DST.view_active->storage.matstate.mat[type], mat); + DST.view_active->is_dirty = true; } void DRW_viewport_matrix_override_unset(DRWViewportMatrixType type) { BLI_assert(type < DRW_MAT_COUNT); - copy_m4_m4(DST.view_data.matstate.mat[type], DST.original_mat.mat[type]); - DST.override_mat &= ~(1 << type); - DST.dirty_mat = true; - DST.clipping.updated = false; + copy_m4_m4(DST.view_active->storage.matstate.mat[type], + DST.view_default->storage.matstate.mat[type]); + DST.view_active->is_dirty = true; } void DRW_viewport_matrix_override_set_all(DRWMatrixState *state) { - memcpy(DST.view_data.matstate.mat, state, sizeof(DRWMatrixState)); - DST.override_mat = 0xFFFFFF; - DST.dirty_mat = true; - DST.clipping.updated = false; + memcpy(DST.view_active->storage.matstate.mat, state, sizeof(DRWMatrixState)); + DST.view_active->is_dirty = true; } void DRW_viewport_matrix_override_unset_all(void) { - memcpy(DST.view_data.matstate.mat, DST.original_mat.mat, sizeof(DRWMatrixState)); - DST.override_mat = 0; - DST.dirty_mat = true; - DST.clipping.updated = false; + DRW_viewport_matrix_override_set_all(&DST.view_default->storage.matstate); } bool DRW_viewport_is_persp_get(void) @@ -738,7 +741,7 @@ bool DRW_viewport_is_persp_get(void) return rv3d->is_persp; } else { - return DST.view_data.matstate.mat[DRW_MAT_WIN][3][3] == 0.0f; + return DST.view_active->storage.matstate.winmat[3][3] == 0.0f; } } diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 96dcfb9d07d..0ccffceb37a 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -43,6 +43,7 @@ /* Use draw manager to call GPU_select, see: DRW_draw_select_loop */ #define USE_GPU_SELECT +#define DRW_DEBUG_CULLING #define DRW_DEBUG_USE_UNIFORM_NAME 0 #define DRW_UNIFORM_BUFFER_NAME 64 @@ -91,9 +92,7 @@ /* Used by DRWCallState.flag */ enum { - DRW_CALL_CULLED = (1 << 0), DRW_CALL_NEGSCALE = (1 << 1), - DRW_CALL_BYPASS_CULLING = (1 << 2), }; /* Used by DRWCallState.matflag */ @@ -104,22 +103,24 @@ enum { DRW_CALL_OBJECTINFO = (1 << 3), }; -typedef struct DRWCallState { - DRWCallVisibilityFn *visibility_cb; +typedef struct DRWCullingState { + uint32_t mask; + /* Culling: Using Bounding Sphere for now for faster culling. + * Not ideal for planes. Could be extended. */ + BoundSphere bsphere; + /* Grrr only used by EEVEE. */ void *user_data; +} DRWCullingState; +typedef struct DRWCallState { + DRWCullingState *culling; uchar flag; - uchar cache_id; /* Compared with DST.state_cache_id to see if matrices are still valid. */ - uchar matflag; /* Which matrices to compute. */ + uchar matflag; /* Which matrices to compute. */ short ob_index; - /* Culling: Using Bounding Sphere for now for faster culling. - * Not ideal for planes. */ - BoundSphere bsphere; /* Matrices */ float model[4][4]; float modelinverse[4][4]; - float modelviewprojection[4][4]; - float orcotexfac[2][3]; /* Not view dependent */ + float orcotexfac[2][3]; float ob_random; } DRWCallState; @@ -213,24 +214,43 @@ struct DRWPass { char name[MAX_PASS_NAME]; }; +/* keep in sync with viewBlock */ +typedef struct ViewUboStorage { + DRWMatrixState matstate; + float clipplanes[6][4]; + /* Should not be here. Not view dependant (only main view). */ + float viewcamtexcofac[4]; +} ViewUboStorage; + +#define MAX_CULLED_VIEWS 32 + +struct DRWView { + /** Parent view if this is a sub view. NULL otherwise. */ + struct DRWView *parent; + + ViewUboStorage storage; + /** Number of active clipplanes. */ + int clip_planes_len; + /** Does culling result needs to be updated. */ + bool is_dirty; + /** Culling */ + uint32_t culling_mask; + BoundBox frustum_corners; + BoundSphere frustum_bsphere; + float frustum_planes[6][4]; + /** Custom visibility function. */ + DRWCallVisibilityFn *visibility_fn; + void *user_data; +}; + /* TODO(fclem): Future awaits */ #if 0 -typedef struct DRWView { - /* Culling function, culling result etc...*/ -} DRWView; - typedef struct ModelUboStorage { float model[4][4]; float modelinverse[4][4]; } ModelUboStorage; #endif -typedef struct ViewUboStorage { - DRWMatrixState matstate; - float viewcamtexcofac[4]; - float clipplanes[2][4]; -} ViewUboStorage; - /* ------------- DRAW DEBUG ------------ */ typedef struct DRWDebugLine { @@ -258,7 +278,6 @@ typedef struct DRWManager { DRWInstanceData *object_instance_data[MAX_INSTANCE_DATA_SIZE]; /* State of the object being evaluated if already allocated. */ DRWCallState *ob_state; - uchar state_cache_id; /* Could be larger but 254 view changes is already a lot! */ struct DupliObject *dupli_source; struct Object *dupli_parent; struct Object *dupli_origin; @@ -302,21 +321,12 @@ typedef struct DRWManager { bool buffer_finish_called; /* Avoid bad usage of DRW_render_instance_buffer_finish */ - /* View dependent uniforms. */ - DRWMatrixState original_mat; /* Original rv3d matrices. */ - int override_mat; /* Bitflag of which matrices are overridden. */ - int clip_planes_len; /* Number of active clipplanes. */ - bool dirty_mat; - - /* keep in sync with viewBlock */ - ViewUboStorage view_data; - - struct { - float frustum_planes[6][4]; - BoundBox frustum_corners; - BoundSphere frustum_bsphere; - bool updated; - } clipping; + DRWView *view_default; + DRWView *view_active; + uint primary_view_ct; + /** TODO(fclem) Remove this. Only here to support + * shaders without common_view_lib.glsl */ + ViewUboStorage view_storage_cpy; #ifdef USE_GPU_SELECT uint select_id; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 837b68ccb56..12f60b5386c 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -39,6 +39,10 @@ #include "BLI_mempool.h" #include "BLI_memblock.h" +#ifdef DRW_DEBUG_CULLING +# include "BLI_math_bits.h" +#endif + #include "GPU_buffers.h" #include "intern/gpu_codegen.h" @@ -389,8 +393,6 @@ static DRWCallState *drw_call_state_create(DRWShadingGroup *shgroup, float (*obm { DRWCallState *state = BLI_memblock_alloc(DST.vmempool->states); state->flag = 0; - state->cache_id = 0; - state->visibility_cb = NULL; state->matflag = 0; /* Matrices */ @@ -407,18 +409,23 @@ static DRWCallState *drw_call_state_create(DRWShadingGroup *shgroup, float (*obm drw_call_state_update_matflag(state, shgroup, ob); + DRWCullingState *cull = BLI_memblock_alloc(DST.vmempool->cullstates); + state->culling = cull; + if (ob != NULL) { float corner[3]; BoundBox *bbox = BKE_object_boundbox_get(ob); /* Get BoundSphere center and radius from the BoundBox. */ - mid_v3_v3v3(state->bsphere.center, bbox->vec[0], bbox->vec[6]); + mid_v3_v3v3(cull->bsphere.center, bbox->vec[0], bbox->vec[6]); mul_v3_m4v3(corner, obmat, bbox->vec[0]); - mul_m4_v3(obmat, state->bsphere.center); - state->bsphere.radius = len_v3v3(state->bsphere.center, corner); + mul_m4_v3(obmat, cull->bsphere.center); + cull->bsphere.radius = len_v3v3(cull->bsphere.center, corner); } else { + /* TODO(fclem) Bypass alloc if we can (see if eevee's + * probe visibility collection still works). */ /* Bypass test. */ - state->bsphere.radius = -1.0f; + cull->bsphere.radius = -1.0f; } return state; @@ -531,8 +538,6 @@ void DRW_shgroup_call_object_ex(DRWShadingGroup *shgroup, BLI_LINKS_APPEND(&shgroup->calls, call); call->state = drw_call_state_object(shgroup, ob->obmat, ob); - /* NOTE this will disable culling for the whole object. */ - call->state->flag |= (bypass_culling) ? DRW_CALL_BYPASS_CULLING : 0; call->batch = geom; call->vert_first = 0; call->vert_count = 0; /* Auto from batch. */ @@ -541,12 +546,15 @@ void DRW_shgroup_call_object_ex(DRWShadingGroup *shgroup, call->select_id = DST.select_id; call->inst_selectid = NULL; #endif + if (bypass_culling) { + /* NOTE this will disable culling for the whole object. */ + call->state->culling->bsphere.radius = -1.0f; + } } void DRW_shgroup_call_object_with_callback(DRWShadingGroup *shgroup, GPUBatch *geom, Object *ob, - DRWCallVisibilityFn *callback, void *user_data) { BLI_assert(geom != NULL); @@ -555,8 +563,7 @@ void DRW_shgroup_call_object_with_callback(DRWShadingGroup *shgroup, BLI_LINKS_APPEND(&shgroup->calls, call); call->state = drw_call_state_object(shgroup, ob->obmat, ob); - call->state->visibility_cb = callback; - call->state->user_data = user_data; + call->state->culling->user_data = user_data; call->batch = geom; call->vert_first = 0; call->vert_count = 0; /* Auto from batch. */ @@ -859,23 +866,16 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) } else { /* Only here to support builtin shaders. This should not be used by engines. */ + /* TODO remove. */ + DRWMatrixState *matstate = &DST.view_storage_cpy.matstate; + drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEW, matstate->viewmat, 16, 1); + drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEW_INV, matstate->viewinv, 16, 1); + drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEWPROJECTION, matstate->persmat, 16, 1); + drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_VIEWPROJECTION_INV, matstate->persinv, 16, 1); + drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_PROJECTION, matstate->winmat, 16, 1); + drw_shgroup_builtin_uniform(shgroup, GPU_UNIFORM_PROJECTION_INV, matstate->wininv, 16, 1); drw_shgroup_builtin_uniform( - shgroup, GPU_UNIFORM_VIEW, DST.view_data.matstate.mat[DRW_MAT_VIEW], 16, 1); - drw_shgroup_builtin_uniform( - shgroup, GPU_UNIFORM_VIEW_INV, DST.view_data.matstate.mat[DRW_MAT_VIEWINV], 16, 1); - drw_shgroup_builtin_uniform( - shgroup, GPU_UNIFORM_VIEWPROJECTION, DST.view_data.matstate.mat[DRW_MAT_PERS], 16, 1); - drw_shgroup_builtin_uniform(shgroup, - GPU_UNIFORM_VIEWPROJECTION_INV, - DST.view_data.matstate.mat[DRW_MAT_PERSINV], - 16, - 1); - drw_shgroup_builtin_uniform( - shgroup, GPU_UNIFORM_PROJECTION, DST.view_data.matstate.mat[DRW_MAT_WIN], 16, 1); - drw_shgroup_builtin_uniform( - shgroup, GPU_UNIFORM_PROJECTION_INV, DST.view_data.matstate.mat[DRW_MAT_WININV], 16, 1); - drw_shgroup_builtin_uniform( - shgroup, GPU_UNIFORM_CAMERATEXCO, DST.view_data.viewcamtexcofac, 3, 2); + shgroup, GPU_UNIFORM_CAMERATEXCO, DST.view_storage_cpy.viewcamtexcofac, 4, 1); } /* Not supported. */ @@ -1066,6 +1066,480 @@ DRWShadingGroup *DRW_shgroup_create_sub(DRWShadingGroup *shgroup) /** \} */ /* -------------------------------------------------------------------- */ +/** \name View (DRW_view) + * \{ */ + +/* Extract the 8 corners from a Projection Matrix. + * Although less accurate, this solution can be simplified as follows: + * BKE_boundbox_init_from_minmax(&bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const + * float[3]){1.0f, 1.0f, 1.0f}); for (int i = 0; i < 8; i++) {mul_project_m4_v3(projinv, + * bbox.vec[i]);} + */ +static void draw_frustum_boundbox_calc(const float (*viewinv)[4], + const float (*projmat)[4], + BoundBox *r_bbox) +{ + float left, right, bottom, top, near, far; + bool is_persp = projmat[3][3] == 0.0f; + +#if 0 /* Equivalent to this but it has accuracy problems. */ + BKE_boundbox_init_from_minmax( + &bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const float[3]){1.0f, 1.0f, 1.0f}); + for (int i = 0; i < 8; i++) { + mul_project_m4_v3(projinv, bbox.vec[i]); + } +#endif + + projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far); + + if (is_persp) { + left *= near; + right *= near; + bottom *= near; + top *= near; + } + + r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near; + r_bbox->vec[0][0] = r_bbox->vec[3][0] = left; + r_bbox->vec[4][0] = r_bbox->vec[7][0] = right; + r_bbox->vec[0][1] = r_bbox->vec[4][1] = bottom; + r_bbox->vec[7][1] = r_bbox->vec[3][1] = top; + + /* Get the coordinates of the far plane. */ + if (is_persp) { + float sca_far = far / near; + left *= sca_far; + right *= sca_far; + bottom *= sca_far; + top *= sca_far; + } + + r_bbox->vec[1][2] = r_bbox->vec[2][2] = r_bbox->vec[6][2] = r_bbox->vec[5][2] = -far; + r_bbox->vec[1][0] = r_bbox->vec[2][0] = left; + r_bbox->vec[6][0] = r_bbox->vec[5][0] = right; + r_bbox->vec[1][1] = r_bbox->vec[5][1] = bottom; + r_bbox->vec[2][1] = r_bbox->vec[6][1] = top; + + /* Transform into world space. */ + for (int i = 0; i < 8; i++) { + mul_m4_v3(viewinv, r_bbox->vec[i]); + } +} + +static void draw_frustum_culling_planes_calc(const BoundBox *bbox, float (*frustum_planes)[4]) +{ + /* TODO See if planes_from_projmat cannot do the job. */ + + /* Compute clip planes using the world space frustum corners. */ + for (int p = 0; p < 6; p++) { + int q, r, s; + switch (p) { + case 0: + q = 1; + r = 2; + s = 3; + break; /* -X */ + case 1: + q = 0; + r = 4; + s = 5; + break; /* -Y */ + case 2: + q = 1; + r = 5; + s = 6; + break; /* +Z (far) */ + case 3: + q = 2; + r = 6; + s = 7; + break; /* +Y */ + case 4: + q = 0; + r = 3; + s = 7; + break; /* -Z (near) */ + default: + q = 4; + r = 7; + s = 6; + break; /* +X */ + } + + normal_quad_v3(frustum_planes[p], bbox->vec[p], bbox->vec[q], bbox->vec[r], bbox->vec[s]); + /* Increase precision and use the mean of all 4 corners. */ + frustum_planes[p][3] = -dot_v3v3(frustum_planes[p], bbox->vec[p]); + frustum_planes[p][3] += -dot_v3v3(frustum_planes[p], bbox->vec[q]); + frustum_planes[p][3] += -dot_v3v3(frustum_planes[p], bbox->vec[r]); + frustum_planes[p][3] += -dot_v3v3(frustum_planes[p], bbox->vec[s]); + frustum_planes[p][3] *= 0.25f; + } +} + +static void draw_frustum_bound_sphere_calc(const BoundBox *bbox, + const float (*viewinv)[4], + const float (*projmat)[4], + const float (*projinv)[4], + BoundSphere *bsphere) +{ + /* Extract Bounding Sphere */ + if (projmat[3][3] != 0.0f) { + /* Orthographic */ + /* The most extreme points on the near and far plane. (normalized device coords). */ + const float *nearpoint = bbox->vec[0]; + const float *farpoint = bbox->vec[6]; + + /* just use median point */ + mid_v3_v3v3(bsphere->center, farpoint, nearpoint); + bsphere->radius = len_v3v3(bsphere->center, farpoint); + } + else if (projmat[2][0] == 0.0f && projmat[2][1] == 0.0f) { + /* Perspective with symmetrical frustum. */ + + /* We obtain the center and radius of the circumscribed circle of the + * isosceles trapezoid composed by the diagonals of the near and far clipping plane */ + + /* center of each clipping plane */ + float mid_min[3], mid_max[3]; + mid_v3_v3v3(mid_min, bbox->vec[3], bbox->vec[4]); + mid_v3_v3v3(mid_max, bbox->vec[2], bbox->vec[5]); + + /* square length of the diagonals of each clipping plane */ + float a_sq = len_squared_v3v3(bbox->vec[3], bbox->vec[4]); + float b_sq = len_squared_v3v3(bbox->vec[2], bbox->vec[5]); + + /* distance squared between clipping planes */ + float h_sq = len_squared_v3v3(mid_min, mid_max); + + float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq); + + /* The goal is to get the smallest sphere, + * not the sphere that passes through each corner */ + CLAMP(fac, 0.0f, 1.0f); + + interp_v3_v3v3(bsphere->center, mid_min, mid_max, fac); + + /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */ + bsphere->radius = len_v3v3(bsphere->center, bbox->vec[1]); + } + else { + /* Perspective with asymmetrical frustum. */ + + /* We put the sphere center on the line that goes from origin + * to the center of the far clipping plane. */ + + /* Detect which of the corner of the far clipping plane is the farthest to the origin */ + float nfar[4]; /* most extreme far point in NDC space */ + float farxy[2]; /* farpoint projection onto the near plane */ + float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */ + float nearpoint[3]; /* most extreme near point in camera coordinate */ + float farcenter[3] = {0.0f}; /* center of far cliping plane in camera coordinate */ + float F = -1.0f, N; /* square distance of far and near point to origin */ + float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */ + float e, s; /* far and near clipping distance (<0) */ + float c; /* slope of center line = distance of far clipping center + * to z axis / far clipping distance. */ + float z; /* projection of sphere center on z axis (<0) */ + + /* Find farthest corner and center of far clip plane. */ + float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */ + for (int i = 0; i < 4; i++) { + float point[3]; + mul_v3_project_m4_v3(point, projinv, corner); + float len = len_squared_v3(point); + if (len > F) { + copy_v3_v3(nfar, corner); + copy_v3_v3(farpoint, point); + F = len; + } + add_v3_v3(farcenter, point); + /* rotate by 90 degree to walk through the 4 points of the far clip plane */ + float tmp = corner[0]; + corner[0] = -corner[1]; + corner[1] = tmp; + } + + /* the far center is the average of the far clipping points */ + mul_v3_fl(farcenter, 0.25f); + /* the extreme near point is the opposite point on the near clipping plane */ + copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f); + mul_v3_project_m4_v3(nearpoint, projinv, nfar); + /* this is a frustum projection */ + N = len_squared_v3(nearpoint); + e = farpoint[2]; + s = nearpoint[2]; + /* distance to view Z axis */ + f = len_v2(farpoint); + /* get corresponding point on the near plane */ + mul_v2_v2fl(farxy, farpoint, s / e); + /* this formula preserve the sign of n */ + sub_v2_v2(nearpoint, farxy); + n = f * s / e - len_v2(nearpoint); + c = len_v2(farcenter) / e; + /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */ + z = (F - N) / (2.0f * (e - s + c * (f - n))); + + bsphere->center[0] = farcenter[0] * z / e; + bsphere->center[1] = farcenter[1] * z / e; + bsphere->center[2] = z; + bsphere->radius = len_v3v3(bsphere->center, farpoint); + + /* Transform to world space. */ + mul_m4_v3(viewinv, bsphere->center); + } +} + +static void draw_matrix_state_from_view(DRWMatrixState *mstate, + const float viewmat[4][4], + const float winmat[4][4]) +{ + /* If only one the matrices is negative, then the + * polygon winding changes and we don't want that. */ + BLI_assert(is_negative_m4(viewmat) != is_negative_m4(winmat)); + + copy_m4_m4(mstate->viewmat, viewmat); + invert_m4_m4(mstate->viewinv, mstate->viewmat); + + copy_m4_m4(mstate->winmat, winmat); + invert_m4_m4(mstate->wininv, mstate->winmat); + + mul_m4_m4m4(mstate->persmat, winmat, viewmat); + invert_m4_m4(mstate->persinv, mstate->persmat); +} + +/* Create a view with culling. */ +DRWView *DRW_view_create(const float viewmat[4][4], + const float winmat[4][4], + const float (*culling_viewmat)[4], + const float (*culling_winmat)[4], + DRWCallVisibilityFn *visibility_fn) +{ + DRWView *view = BLI_memblock_alloc(DST.vmempool->views); + + if (DST.primary_view_ct < MAX_CULLED_VIEWS) { + view->culling_mask = 1u << DST.primary_view_ct++; + } + else { + view->culling_mask = 0u; + } + view->clip_planes_len = 0; + view->visibility_fn = visibility_fn; + view->parent = NULL; + + /* TODO move elsewhere */ + if (DST.view_default) { + copy_v4_v4(view->storage.viewcamtexcofac, DST.view_default->storage.viewcamtexcofac); + } + + DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat); + + return view; +} + +/* Create a view with culling done by another view. */ +DRWView *DRW_view_create_sub(const DRWView *parent_view, + const float viewmat[4][4], + const float winmat[4][4]) +{ + BLI_assert(parent_view && parent_view->parent == NULL); + + DRWView *view = BLI_memblock_alloc(DST.vmempool->views); + + /* Perform copy. */ + *view = *parent_view; + view->parent = (DRWView *)parent_view; + + /* TODO move elsewhere */ + if (DST.view_default) { + copy_v4_v4(view->storage.viewcamtexcofac, DST.view_default->storage.viewcamtexcofac); + } + + DRW_view_update_sub(view, viewmat, winmat); + + return view; +} + +/** + * DRWView Update: + * This is meant to be done on existing views when rendering in a loop and there is no + * need to allocate more DRWViews. + **/ + +/* Update matrices of a view created with DRW_view_create_sub. */ +void DRW_view_update_sub(DRWView *view, const float viewmat[4][4], const float winmat[4][4]) +{ + BLI_assert(view->parent != NULL); + DRWMatrixState *mstate = &view->storage.matstate; + + view->is_dirty = true; + + draw_matrix_state_from_view(mstate, viewmat, winmat); +} + +/* Update matrices of a view created with DRW_view_create. */ +void DRW_view_update(DRWView *view, + const float viewmat[4][4], + const float winmat[4][4], + const float (*culling_viewmat)[4], + const float (*culling_winmat)[4]) +{ + /* DO NOT UPDATE THE DEFAULT VIEW. + * Create subviews instead, or a copy. */ + BLI_assert(view != DST.view_default); + BLI_assert(view->parent == NULL); + DRWMatrixState *mstate = &view->storage.matstate; + + view->is_dirty = true; + + draw_matrix_state_from_view(mstate, viewmat, winmat); + + /* Prepare frustum culling. */ + +#ifdef DRW_DEBUG_CULLING + static float mv[MAX_CULLED_VIEWS][4][4], mw[MAX_CULLED_VIEWS][4][4]; + + /* Select view here. */ + if (view->culling_mask != 0) { + uint index = bitscan_forward_uint(view->culling_mask); + + if (G.debug_value == 0) { + copy_m4_m4(mv[index], culling_viewmat ? culling_viewmat : viewmat); + copy_m4_m4(mw[index], culling_winmat ? culling_winmat : winmat); + } + else { + culling_winmat = mw[index]; + culling_viewmat = mv[index]; + } + } +#endif + + float wininv[4][4]; + if (culling_winmat) { + winmat = culling_winmat; + invert_m4_m4(wininv, winmat); + } + else { + copy_m4_m4(wininv, mstate->wininv); + } + + float viewinv[4][4]; + if (culling_viewmat) { + viewmat = culling_viewmat; + invert_m4_m4(viewinv, viewmat); + } + else { + copy_m4_m4(viewinv, mstate->viewinv); + } + + draw_frustum_boundbox_calc(viewinv, winmat, &view->frustum_corners); + draw_frustum_culling_planes_calc(&view->frustum_corners, view->frustum_planes); + draw_frustum_bound_sphere_calc( + &view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere); + +#ifdef DRW_DEBUG_CULLING + if (G.debug_value != 0) { + DRW_debug_sphere( + view->frustum_bsphere.center, view->frustum_bsphere.radius, (const float[4]){1, 1, 0, 1}); + DRW_debug_bbox(&view->frustum_corners, (const float[4]){1, 1, 0, 1}); + } +#endif +} + +/* Return default view if it is a viewport render. */ +const DRWView *DRW_view_default_get(void) +{ + return DST.view_default; +} + +/* MUST only be called once per render and only in render mode. Sets default view. */ +void DRW_view_default_set(DRWView *view) +{ + BLI_assert(DST.view_default == NULL); + DST.view_default = view; +} + +/** + * This only works if DRWPasses have been tagged with DRW_STATE_CLIP_PLANES, + * and if the shaders have support for it (see usage of gl_ClipDistance). + */ +void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len) +{ + BLI_assert(plane_len <= MAX_CLIP_PLANES); + view->clip_planes_len = plane_len; + if (plane_len > 0) { + memcpy(view->storage.clipplanes, planes, sizeof(float) * 4 * plane_len); + } +} + +/* Return world space frustum corners. */ +void DRW_view_frustum_corners_get(const DRWView *view, BoundBox *corners) +{ + memcpy(corners, &view->frustum_corners, sizeof(view->frustum_corners)); +} + +/* Return world space frustum sides as planes. + * See draw_frustum_culling_planes_calc() for the plane order. */ +void DRW_view_frustum_planes_get(const DRWView *view, float planes[6][4]) +{ + memcpy(planes, &view->frustum_planes, sizeof(view->frustum_planes)); +} + +bool DRW_view_is_persp_get(const DRWView *view) +{ + view = (view) ? view : DST.view_active; + return view->storage.matstate.winmat[3][3] == 0.0f; +} + +float DRW_view_near_distance_get(const DRWView *view) +{ + view = (view) ? view : DST.view_active; + const float(*projmat)[4] = view->storage.matstate.winmat; + + if (DRW_view_is_persp_get(view)) { + return -projmat[3][2] / (projmat[2][2] - 1.0f); + } + else { + return -(projmat[3][2] + 1.0f) / projmat[2][2]; + } +} + +float DRW_view_far_distance_get(const DRWView *view) +{ + view = (view) ? view : DST.view_active; + const float(*projmat)[4] = view->storage.matstate.winmat; + + if (DRW_view_is_persp_get(view)) { + return -projmat[3][2] / (projmat[2][2] + 1.0f); + } + else { + return -(projmat[3][2] - 1.0f) / projmat[2][2]; + } +} + +void DRW_view_viewmat_get(const DRWView *view, float mat[4][4], bool inverse) +{ + view = (view) ? view : DST.view_active; + const DRWMatrixState *state = &view->storage.matstate; + copy_m4_m4(mat, (inverse) ? state->viewinv : state->viewmat); +} + +void DRW_view_winmat_get(const DRWView *view, float mat[4][4], bool inverse) +{ + view = (view) ? view : DST.view_active; + const DRWMatrixState *state = &view->storage.matstate; + copy_m4_m4(mat, (inverse) ? state->wininv : state->winmat); +} + +void DRW_view_persmat_get(const DRWView *view, float mat[4][4], bool inverse) +{ + view = (view) ? view : DST.view_active; + const DRWMatrixState *state = &view->storage.matstate; + copy_m4_m4(mat, (inverse) ? state->persinv : state->persmat); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Passes (DRW_pass) * \{ */ @@ -1118,8 +1592,8 @@ void DRW_pass_foreach_shgroup(DRWPass *pass, } typedef struct ZSortData { - float *axis; - float *origin; + const float *axis; + const float *origin; } ZSortData; static int pass_shgroup_dist_sort(void *thunk, const void *a, const void *b) @@ -1182,8 +1656,7 @@ static int pass_shgroup_dist_sort(void *thunk, const void *a, const void *b) */ void DRW_pass_sort_shgroup_z(DRWPass *pass) { - float(*viewinv)[4]; - viewinv = DST.view_data.matstate.mat[DRW_MAT_VIEWINV]; + const float(*viewinv)[4] = DST.view_active->storage.matstate.viewinv; ZSortData zsortdata = {viewinv[2], viewinv[3]}; diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 49927b74e72..986c8fe18f6 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -238,7 +238,7 @@ void drw_state_set(DRWState state) int test; if ((test = CHANGED_TO(DRW_STATE_CLIP_PLANES))) { if (test == 1) { - for (int i = 0; i < DST.clip_planes_len; ++i) { + for (int i = 0; i < DST.view_active->clip_planes_len; ++i) { glEnable(GL_CLIP_DISTANCE0 + i); } } @@ -395,274 +395,47 @@ void DRW_state_reset(void) void DRW_state_clip_planes_len_set(uint plane_len) { BLI_assert(plane_len <= MAX_CLIP_PLANES); - DST.clip_planes_len = plane_len; + /* DUMMY TO REMOVE */ } void DRW_state_clip_planes_reset(void) { - DST.clip_planes_len = 0; + /* DUMMY TO REMOVE */ } void DRW_state_clip_planes_set_from_rv3d(RegionView3D *rv3d) { - int max_len = 6; - int real_len = (rv3d->viewlock & RV3D_BOXCLIP) ? 4 : max_len; - while (real_len < max_len) { - /* Fill in dummy values that wont change results (6 is hard coded in shaders). */ - copy_v4_v4(rv3d->clip[real_len], rv3d->clip[3]); - real_len++; - } - - DRW_state_clip_planes_len_set(max_len); + /* DUMMY TO REMOVE */ } /** \} */ /* -------------------------------------------------------------------- */ -/** \name Clipping (DRW_clipping) +/** \name Culling (DRW_culling) * \{ */ -/* Extract the 8 corners from a Projection Matrix. - * Although less accurate, this solution can be simplified as follows: - * BKE_boundbox_init_from_minmax(&bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const - * float[3]){1.0f, 1.0f, 1.0f}); for (int i = 0; i < 8; i++) {mul_project_m4_v3(projinv, - * bbox.vec[i]);} - */ -static void draw_frustum_boundbox_calc(const float (*projmat)[4], BoundBox *r_bbox) +static bool draw_call_is_culled(DRWCall *call, DRWView *view) { - float left, right, bottom, top, near, far; - bool is_persp = projmat[3][3] == 0.0f; - - projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far); - - if (is_persp) { - left *= near; - right *= near; - bottom *= near; - top *= near; - } - - r_bbox->vec[0][2] = r_bbox->vec[3][2] = r_bbox->vec[7][2] = r_bbox->vec[4][2] = -near; - r_bbox->vec[0][0] = r_bbox->vec[3][0] = left; - r_bbox->vec[4][0] = r_bbox->vec[7][0] = right; - r_bbox->vec[0][1] = r_bbox->vec[4][1] = bottom; - r_bbox->vec[7][1] = r_bbox->vec[3][1] = top; - - /* Get the coordinates of the far plane. */ - if (is_persp) { - float sca_far = far / near; - left *= sca_far; - right *= sca_far; - bottom *= sca_far; - top *= sca_far; - } - - r_bbox->vec[1][2] = r_bbox->vec[2][2] = r_bbox->vec[6][2] = r_bbox->vec[5][2] = -far; - r_bbox->vec[1][0] = r_bbox->vec[2][0] = left; - r_bbox->vec[6][0] = r_bbox->vec[5][0] = right; - r_bbox->vec[1][1] = r_bbox->vec[5][1] = bottom; - r_bbox->vec[2][1] = r_bbox->vec[6][1] = top; + return (call->state->culling->mask & view->culling_mask) != 0; } -static void draw_clipping_setup_from_view(void) +/* Set active view for rendering. */ +void DRW_view_set_active(DRWView *view) { - if (DST.clipping.updated) { - return; - } - - float(*viewinv)[4] = DST.view_data.matstate.mat[DRW_MAT_VIEWINV]; - float(*projmat)[4] = DST.view_data.matstate.mat[DRW_MAT_WIN]; - float(*projinv)[4] = DST.view_data.matstate.mat[DRW_MAT_WININV]; - BoundSphere *bsphere = &DST.clipping.frustum_bsphere; - - /* Extract Clipping Planes */ - BoundBox bbox; -#if 0 /* It has accuracy problems. */ - BKE_boundbox_init_from_minmax( - &bbox, (const float[3]){-1.0f, -1.0f, -1.0f}, (const float[3]){1.0f, 1.0f, 1.0f}); - for (int i = 0; i < 8; i++) { - mul_project_m4_v3(projinv, bbox.vec[i]); - } -#else - draw_frustum_boundbox_calc(projmat, &bbox); -#endif - /* Transform into world space. */ - for (int i = 0; i < 8; i++) { - mul_m4_v3(viewinv, bbox.vec[i]); - } - - memcpy(&DST.clipping.frustum_corners, &bbox, sizeof(BoundBox)); - - /* Compute clip planes using the world space frustum corners. */ - for (int p = 0; p < 6; p++) { - int q, r, s; - switch (p) { - case 0: - q = 1; - r = 2; - s = 3; - break; /* -X */ - case 1: - q = 0; - r = 4; - s = 5; - break; /* -Y */ - case 2: - q = 1; - r = 5; - s = 6; - break; /* +Z (far) */ - case 3: - q = 2; - r = 6; - s = 7; - break; /* +Y */ - case 4: - q = 0; - r = 3; - s = 7; - break; /* -Z (near) */ - default: - q = 4; - r = 7; - s = 6; - break; /* +X */ - } - if (DST.frontface == GL_CW) { - SWAP(int, q, s); - } - - normal_quad_v3( - DST.clipping.frustum_planes[p], bbox.vec[p], bbox.vec[q], bbox.vec[r], bbox.vec[s]); - /* Increase precision and use the mean of all 4 corners. */ - DST.clipping.frustum_planes[p][3] = -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[p]); - DST.clipping.frustum_planes[p][3] += -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[q]); - DST.clipping.frustum_planes[p][3] += -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[r]); - DST.clipping.frustum_planes[p][3] += -dot_v3v3(DST.clipping.frustum_planes[p], bbox.vec[s]); - DST.clipping.frustum_planes[p][3] *= 0.25f; - } - - /* Extract Bounding Sphere */ - if (projmat[3][3] != 0.0f) { - /* Orthographic */ - /* The most extreme points on the near and far plane. (normalized device coords). */ - float *nearpoint = bbox.vec[0]; - float *farpoint = bbox.vec[6]; - - /* just use median point */ - mid_v3_v3v3(bsphere->center, farpoint, nearpoint); - bsphere->radius = len_v3v3(bsphere->center, farpoint); - } - else if (projmat[2][0] == 0.0f && projmat[2][1] == 0.0f) { - /* Perspective with symmetrical frustum. */ - - /* We obtain the center and radius of the circumscribed circle of the - * isosceles trapezoid composed by the diagonals of the near and far clipping plane */ - - /* center of each clipping plane */ - float mid_min[3], mid_max[3]; - mid_v3_v3v3(mid_min, bbox.vec[3], bbox.vec[4]); - mid_v3_v3v3(mid_max, bbox.vec[2], bbox.vec[5]); - - /* square length of the diagonals of each clipping plane */ - float a_sq = len_squared_v3v3(bbox.vec[3], bbox.vec[4]); - float b_sq = len_squared_v3v3(bbox.vec[2], bbox.vec[5]); - - /* distance squared between clipping planes */ - float h_sq = len_squared_v3v3(mid_min, mid_max); - - float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq); - - /* The goal is to get the smallest sphere, - * not the sphere that passes through each corner */ - CLAMP(fac, 0.0f, 1.0f); - - interp_v3_v3v3(bsphere->center, mid_min, mid_max, fac); - - /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */ - bsphere->radius = len_v3v3(bsphere->center, bbox.vec[1]); - } - else { - /* Perspective with asymmetrical frustum. */ - - /* We put the sphere center on the line that goes from origin - * to the center of the far clipping plane. */ - - /* Detect which of the corner of the far clipping plane is the farthest to the origin */ - float nfar[4]; /* most extreme far point in NDC space */ - float farxy[2]; /* farpoint projection onto the near plane */ - float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */ - float nearpoint[3]; /* most extreme near point in camera coordinate */ - float farcenter[3] = {0.0f}; /* center of far cliping plane in camera coordinate */ - float F = -1.0f, N; /* square distance of far and near point to origin */ - float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */ - float e, s; /* far and near clipping distance (<0) */ - float c; /* slope of center line = distance of far clipping center - * to z axis / far clipping distance. */ - float z; /* projection of sphere center on z axis (<0) */ - - /* Find farthest corner and center of far clip plane. */ - float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */ - for (int i = 0; i < 4; i++) { - float point[3]; - mul_v3_project_m4_v3(point, projinv, corner); - float len = len_squared_v3(point); - if (len > F) { - copy_v3_v3(nfar, corner); - copy_v3_v3(farpoint, point); - F = len; - } - add_v3_v3(farcenter, point); - /* rotate by 90 degree to walk through the 4 points of the far clip plane */ - float tmp = corner[0]; - corner[0] = -corner[1]; - corner[1] = tmp; - } - - /* the far center is the average of the far clipping points */ - mul_v3_fl(farcenter, 0.25f); - /* the extreme near point is the opposite point on the near clipping plane */ - copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f); - mul_v3_project_m4_v3(nearpoint, projinv, nfar); - /* this is a frustum projection */ - N = len_squared_v3(nearpoint); - e = farpoint[2]; - s = nearpoint[2]; - /* distance to view Z axis */ - f = len_v2(farpoint); - /* get corresponding point on the near plane */ - mul_v2_v2fl(farxy, farpoint, s / e); - /* this formula preserve the sign of n */ - sub_v2_v2(nearpoint, farxy); - n = f * s / e - len_v2(nearpoint); - c = len_v2(farcenter) / e; - /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */ - z = (F - N) / (2.0f * (e - s + c * (f - n))); - - bsphere->center[0] = farcenter[0] * z / e; - bsphere->center[1] = farcenter[1] * z / e; - bsphere->center[2] = z; - bsphere->radius = len_v3v3(bsphere->center, farpoint); - - /* Transform to world space. */ - mul_m4_v3(viewinv, bsphere->center); - } - - DST.clipping.updated = true; + DST.view_active = (view) ? view : DST.view_default; } /* Return True if the given BoundSphere intersect the current view frustum */ -bool DRW_culling_sphere_test(BoundSphere *bsphere) +static bool draw_culling_sphere_test(const BoundSphere *frustum_bsphere, + const float (*frustum_planes)[4], + const BoundSphere *bsphere) { - draw_clipping_setup_from_view(); - /* Bypass test if radius is negative. */ if (bsphere->radius < 0.0f) { return true; } /* Do a rough test first: Sphere VS Sphere intersect. */ - BoundSphere *frustum_bsphere = &DST.clipping.frustum_bsphere; float center_dist_sq = len_squared_v3v3(bsphere->center, frustum_bsphere->center); float radius_sum = bsphere->radius + frustum_bsphere->radius; if (center_dist_sq > SQUARE(radius_sum)) { @@ -674,26 +447,21 @@ bool DRW_culling_sphere_test(BoundSphere *bsphere) /* TODO order planes with sides first then far then near clip. Should be better culling * heuristic when sculpting. */ for (int p = 0; p < 6; p++) { - float dist = plane_point_side_v3(DST.clipping.frustum_planes[p], bsphere->center); + float dist = plane_point_side_v3(frustum_planes[p], bsphere->center); if (dist < -bsphere->radius) { return false; } } - return true; } -/* Return True if the given BoundBox intersect the current view frustum. - * bbox must be in world space. */ -bool DRW_culling_box_test(BoundBox *bbox) +static bool draw_culling_box_test(const float (*frustum_planes)[4], const BoundBox *bbox) { - draw_clipping_setup_from_view(); - /* 6 view frustum planes */ for (int p = 0; p < 6; p++) { /* 8 box vertices. */ for (int v = 0; v < 8; v++) { - float dist = plane_point_side_v3(DST.clipping.frustum_planes[p], bbox->vec[v]); + float dist = plane_point_side_v3(frustum_planes[p], bbox->vec[v]); if (dist > 0.0f) { /* At least one point in front of this plane. * Go to next plane. */ @@ -705,139 +473,134 @@ bool DRW_culling_box_test(BoundBox *bbox) } } } - return true; } -/* Return True if the current view frustum is inside or intersect the given plane */ -bool DRW_culling_plane_test(float plane[4]) +static bool draw_culling_plane_test(const BoundBox *corners, const float plane[4]) { - draw_clipping_setup_from_view(); - /* Test against the 8 frustum corners. */ for (int c = 0; c < 8; c++) { - float dist = plane_point_side_v3(plane, DST.clipping.frustum_corners.vec[c]); + float dist = plane_point_side_v3(plane, corners->vec[c]); if (dist < 0.0f) { return true; } } - return false; } -void DRW_culling_frustum_corners_get(BoundBox *corners) +/* Return True if the given BoundSphere intersect the current view frustum. + * bsphere must be in world space. */ +bool DRW_culling_sphere_test(const BoundSphere *bsphere) { - draw_clipping_setup_from_view(); - memcpy(corners, &DST.clipping.frustum_corners, sizeof(BoundBox)); + return draw_culling_sphere_test( + &DST.view_active->frustum_bsphere, DST.view_active->frustum_planes, bsphere); } -/* See draw_clipping_setup_from_view() for the plane order. */ -void DRW_culling_frustum_planes_get(float planes[6][4]) +/* Return True if the given BoundBox intersect the current view frustum. + * bbox must be in world space. */ +bool DRW_culling_box_test(const BoundBox *bbox) { - draw_clipping_setup_from_view(); - memcpy(planes, &DST.clipping.frustum_planes, sizeof(DST.clipping.frustum_planes)); + return draw_culling_box_test(DST.view_active->frustum_planes, bbox); } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Draw (DRW_draw) - * \{ */ - -static void draw_visibility_eval(DRWCallState *st) +/* Return True if the view frustum is inside or intersect the given plane. + * plane must be in world space. */ +bool DRW_culling_plane_test(const float plane[4]) { - bool culled = st->flag & DRW_CALL_CULLED; - - if (st->cache_id != DST.state_cache_id) { - /* Update culling result for this view. */ - culled = !DRW_culling_sphere_test(&st->bsphere); - } + return draw_culling_plane_test(&DST.view_active->frustum_corners, plane); +} - if (st->visibility_cb) { - culled = !st->visibility_cb(!culled, st->user_data); - } +void DRW_culling_frustum_corners_get(BoundBox *corners) +{ + *corners = DST.view_active->frustum_corners; +} - SET_FLAG_FROM_TEST(st->flag, culled, DRW_CALL_CULLED); +void DRW_culling_frustum_planes_get(float planes[6][4]) +{ + memcpy(planes, DST.view_active->frustum_planes, sizeof(float) * 6 * 4); } -static void draw_matrices_model_prepare(DRWCallState *st) +static void draw_compute_culling(DRWView *view) { - if (st->cache_id == DST.state_cache_id) { - /* Values are already updated for this view. */ - return; - } - else { - st->cache_id = DST.state_cache_id; - } + view = view->parent ? view->parent : view; - /* No need to go further the call will not be used. */ - if ((st->flag & DRW_CALL_CULLED) != 0 && (st->flag & DRW_CALL_BYPASS_CULLING) == 0) { + /* TODO(fclem) multithread this. */ + /* TODO(fclem) compute all dirty views at once. */ + if (!view->is_dirty) { return; } - if (st->matflag & DRW_CALL_MODELVIEWPROJECTION) { - mul_m4_m4m4(st->modelviewprojection, DST.view_data.matstate.mat[DRW_MAT_PERS], st->model); + BLI_memblock_iter iter; + BLI_memblock_iternew(DST.vmempool->cullstates, &iter); + DRWCullingState *cull; + while ((cull = BLI_memblock_iterstep(&iter))) { + if (cull->bsphere.radius < 0.0) { + cull->mask = 0; + } + else { + bool culled = !draw_culling_sphere_test( + &view->frustum_bsphere, view->frustum_planes, &cull->bsphere); + +#ifdef DRW_DEBUG_CULLING + if (G.debug_value != 0) { + if (culled) { + DRW_debug_sphere( + cull->bsphere.center, cull->bsphere.radius, (const float[4]){1, 0, 0, 1}); + } + else { + DRW_debug_sphere( + cull->bsphere.center, cull->bsphere.radius, (const float[4]){0, 1, 0, 1}); + } + } +#endif + + if (view->visibility_fn) { + culled = !view->visibility_fn(!culled, cull->user_data); + } + + SET_FLAG_FROM_TEST(cull->mask, culled, view->culling_mask); + } } + + view->is_dirty = false; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw (DRW_draw) + * \{ */ + static void draw_geometry_prepare(DRWShadingGroup *shgroup, DRWCall *call) { - /* step 1 : bind object dependent matrices */ - if (call != NULL) { - DRWCallState *state = call->state; + BLI_assert(call); + DRWCallState *state = call->state; - if (shgroup->model != -1) { - GPU_shader_uniform_vector(shgroup->shader, shgroup->model, 16, 1, (float *)state->model); - } - if (shgroup->modelinverse != -1) { - GPU_shader_uniform_vector( - shgroup->shader, shgroup->modelinverse, 16, 1, (float *)state->modelinverse); - } - if (shgroup->modelviewprojection != -1) { - GPU_shader_uniform_vector(shgroup->shader, - shgroup->modelviewprojection, - 16, - 1, - (float *)state->modelviewprojection); - } - if (shgroup->objectinfo != -1) { - float infos[4]; - infos[0] = state->ob_index; - // infos[1]; /* UNUSED. */ - infos[2] = state->ob_random; - infos[3] = (state->flag & DRW_CALL_NEGSCALE) ? -1.0f : 1.0f; - GPU_shader_uniform_vector(shgroup->shader, shgroup->objectinfo, 4, 1, (float *)infos); - } - if (shgroup->orcotexfac != -1) { - GPU_shader_uniform_vector( - shgroup->shader, shgroup->orcotexfac, 3, 2, (float *)state->orcotexfac); - } + if (shgroup->model != -1) { + GPU_shader_uniform_vector(shgroup->shader, shgroup->model, 16, 1, (float *)state->model); } - else { - /* For instancing and batching. */ - float unitmat[4][4]; - unit_m4(unitmat); - - if (shgroup->model != -1) { - GPU_shader_uniform_vector(shgroup->shader, shgroup->model, 16, 1, (float *)unitmat); - } - if (shgroup->modelinverse != -1) { - GPU_shader_uniform_vector(shgroup->shader, shgroup->modelinverse, 16, 1, (float *)unitmat); - } - if (shgroup->modelviewprojection != -1) { - GPU_shader_uniform_vector(shgroup->shader, - shgroup->modelviewprojection, - 16, - 1, - (float *)DST.view_data.matstate.mat[DRW_MAT_PERS]); - } - if (shgroup->objectinfo != -1) { - GPU_shader_uniform_vector(shgroup->shader, shgroup->objectinfo, 4, 1, (float *)unitmat); - } - if (shgroup->orcotexfac != -1) { - float orcofacs[2][3] = {{0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}}; - GPU_shader_uniform_vector(shgroup->shader, shgroup->orcotexfac, 3, 2, (float *)orcofacs); - } + if (shgroup->modelinverse != -1) { + GPU_shader_uniform_vector( + shgroup->shader, shgroup->modelinverse, 16, 1, (float *)state->modelinverse); + } + if (shgroup->objectinfo != -1) { + float infos[4]; + infos[0] = state->ob_index; + // infos[1]; /* UNUSED. */ + infos[2] = state->ob_random; + infos[3] = (state->flag & DRW_CALL_NEGSCALE) ? -1.0f : 1.0f; + GPU_shader_uniform_vector(shgroup->shader, shgroup->objectinfo, 4, 1, (float *)infos); + } + if (shgroup->orcotexfac != -1) { + GPU_shader_uniform_vector( + shgroup->shader, shgroup->orcotexfac, 3, 2, (float *)state->orcotexfac); + } + /* Still supported for compatibility with gpu_shader_* but should be forbidden + * and is slow (since it does not cache the result). */ + if (shgroup->modelviewprojection != -1) { + float mvp[4][4]; + mul_m4_m4m4(mvp, DST.view_active->storage.matstate.persmat, state->model); + GPU_shader_uniform_vector(shgroup->shader, shgroup->modelviewprojection, 16, 1, (float *)mvp); } } @@ -1161,10 +924,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) int callid = 0; for (DRWCall *call = shgroup->calls.first; call; call = call->next) { - /* OPTI/IDEA(clem): Do this preparation in another thread. */ - draw_visibility_eval(call->state); - - if ((call->state->flag & DRW_CALL_CULLED) != 0) { + if (draw_call_is_culled(call, DST.view_active)) { continue; } @@ -1206,29 +966,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) static void drw_update_view(void) { - if (DST.dirty_mat) { - DST.state_cache_id++; - DST.dirty_mat = false; - - DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_data); - - /* Catch integer wrap around. */ - if (UNLIKELY(DST.state_cache_id == 0)) { - DST.state_cache_id = 1; - /* We must reset all CallStates to ensure that not - * a single one stayed with cache_id equal to 1. */ - BLI_memblock_iter iter; - DRWCallState *state; - BLI_memblock_iternew(DST.vmempool->states, &iter); - while ((state = BLI_memblock_iterstep(&iter))) { - state->cache_id = 0; - } - } + /* TODO(fclem) update a big UBO and only bind ranges here. */ + DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_active->storage); - /* TODO dispatch threads to compute matrices/culling */ - } + /* TODO get rid of this. */ + DST.view_storage_cpy = DST.view_active->storage; - draw_clipping_setup_from_view(); + draw_compute_culling(DST.view_active); } static void drw_draw_pass_ex(DRWPass *pass, diff --git a/source/blender/draw/modes/edit_mesh_mode.c b/source/blender/draw/modes/edit_mesh_mode.c index 1bd94cbeecf..2c22acdc879 100644 --- a/source/blender/draw/modes/edit_mesh_mode.c +++ b/source/blender/draw/modes/edit_mesh_mode.c @@ -330,7 +330,8 @@ static DRWPass *edit_mesh_create_overlay_pass(float *face_alpha, float winmat[4][4]; float viewdist = rv3d->dist; - DRW_viewport_matrix_get(winmat, DRW_MAT_WIN); + DRW_view_winmat_get(NULL, winmat, false); + /* 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])); diff --git a/source/blender/draw/modes/shaders/common_view_lib.glsl b/source/blender/draw/modes/shaders/common_view_lib.glsl index 9802b9111fc..845c615c75c 100644 --- a/source/blender/draw/modes/shaders/common_view_lib.glsl +++ b/source/blender/draw/modes/shaders/common_view_lib.glsl @@ -9,9 +9,10 @@ layout(std140) uniform viewBlock mat4 ProjectionMatrix; mat4 ProjectionMatrixInverse; - vec4 CameraTexCoFactors; + vec4 clipPlanes[6]; - vec4 clipPlanes[2]; + /* TODO move it elsewhere. */ + vec4 CameraTexCoFactors; }; uniform mat4 ModelMatrix; diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index e61cfe363df..e7600279d6f 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -39,8 +39,10 @@ typedef struct GPUViewport GPUViewport; typedef struct ViewportMemoryPool { struct BLI_memblock *calls; struct BLI_memblock *states; + struct BLI_memblock *cullstates; struct BLI_memblock *shgroups; struct BLI_memblock *uniforms; + struct BLI_memblock *views; struct BLI_memblock *passes; struct BLI_memblock *images; } ViewportMemoryPool; diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index b825819ceb4..4fd439f4203 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -625,12 +625,18 @@ void GPU_viewport_free(GPUViewport *viewport) if (viewport->vmempool.states != NULL) { BLI_memblock_destroy(viewport->vmempool.states, NULL); } + if (viewport->vmempool.cullstates != NULL) { + BLI_memblock_destroy(viewport->vmempool.cullstates, NULL); + } if (viewport->vmempool.shgroups != NULL) { BLI_memblock_destroy(viewport->vmempool.shgroups, NULL); } if (viewport->vmempool.uniforms != NULL) { BLI_memblock_destroy(viewport->vmempool.uniforms, NULL); } + if (viewport->vmempool.views != NULL) { + BLI_memblock_destroy(viewport->vmempool.views, NULL); + } if (viewport->vmempool.passes != NULL) { BLI_memblock_destroy(viewport->vmempool.passes, NULL); } |