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:
authorClément Foucault <foucault.clem@gmail.com>2019-05-20 19:01:42 +0300
committerClément Foucault <foucault.clem@gmail.com>2019-05-22 14:29:04 +0300
commit45c085a1718eee2b5616dd2af7f37749f79ab593 (patch)
tree55bfec4b2bfd658019ebe0b0d65dfdcb01901251 /source/blender/draw/intern
parent88a725eff8f42bcaa5921f35b988fa07d0e619e7 (diff)
DRW: Add DRWView to improve different view handling
This will have multiple benefit. TODO detail benefits (culling, more explicit, handling of clipping planes) For now the view usage is wrapped to make changes needed more progressive.
Diffstat (limited to 'source/blender/draw/intern')
-rw-r--r--source/blender/draw/intern/DRW_render.h54
-rw-r--r--source/blender/draw/intern/draw_manager.c87
-rw-r--r--source/blender/draw/intern/draw_manager.h84
-rw-r--r--source/blender/draw/intern/draw_manager_data.c535
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c484
5 files changed, 760 insertions, 484 deletions
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,