GPU_matrix: Add GPU_matrix_unproject_precalc

Pre-calculates values needed for unprojecting to avoid
a matrix invert and extracting projection matrix dimensions for
every call to GPU_matrix_unproject.

Use for gizmo selection drawing.
This commit is contained in:
Campbell Barton 2019-06-22 13:19:11 +10:00
parent 1b2b9c6b1f
commit 2642ba13b4
3 changed files with 76 additions and 41 deletions

View File

@ -96,21 +96,38 @@ void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far)
/* 3D Projection between Window and World Space */
struct GPUMatrixUnproject_Precalc {
float model_inverted[4][4];
float view[4];
bool is_persp;
/** Result of 'projmat_dimensions'. */
struct {
float xmin, xmax;
float ymin, ymax;
float zmin, zmax;
} dims;
};
bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_precalc,
const float model[4][4],
const float proj[4][4],
const int view[4]);
void GPU_matrix_project(const float world[3],
const float model[4][4],
const float proj[4][4],
const int view[4],
float win[3]);
float r_win[3]);
bool GPU_matrix_unproject(const float win[3],
const float model[4][4],
const float proj[4][4],
const int view[4],
float world[3]);
void GPU_matrix_unproject_model_inverted(const float win[3],
const float model_inverted[4][4],
const float proj[4][4],
const int view[4],
float world[3]);
float r_world[3]);
void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc,
const float win[3],
float r_world[3]);
/* 2D Projection Matrix */

View File

@ -481,58 +481,73 @@ void GPU_matrix_project(const float world[3],
* But that solution loses much precision.
* Therefore, get the same result without inverting the matrix.
*/
static void gpu_mul_invert_projmat_m4_unmapped_v3(const float projmat[4][4], float co[3])
static void gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(
const struct GPUMatrixUnproject_Precalc *precalc, float co[3])
{
float left, right, bottom, top, near, far;
bool is_persp = projmat[3][3] == 0.0f;
/* 'precalc->dims' is the result of 'projmat_dimensions(proj, ...)'. */
co[0] = precalc->dims.xmin + co[0] * (precalc->dims.xmax - precalc->dims.xmin);
co[1] = precalc->dims.ymin + co[1] * (precalc->dims.ymax - precalc->dims.ymin);
projmat_dimensions(projmat, &left, &right, &bottom, &top, &near, &far);
co[0] = left + co[0] * (right - left);
co[1] = bottom + co[1] * (top - bottom);
if (is_persp) {
co[2] = far * near / (far + co[2] * (near - far));
if (precalc->is_persp) {
co[2] = precalc->dims.zmax * precalc->dims.zmin /
(precalc->dims.zmax + co[2] * (precalc->dims.zmin - precalc->dims.zmax));
co[0] *= co[2];
co[1] *= co[2];
}
else {
co[2] = near + co[2] * (far - near);
co[2] = precalc->dims.zmin + co[2] * (precalc->dims.zmax - precalc->dims.zmin);
}
co[2] *= -1;
}
void GPU_matrix_unproject_model_inverted(const float win[3],
const float model_inverted[4][4],
const float proj[4][4],
const int view[4],
float world[3])
bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
const float model[4][4],
const float proj[4][4],
const int view[4])
{
float in[3];
precalc->is_persp = proj[3][3] == 0.0f;
projmat_dimensions(proj,
&precalc->dims.xmin,
&precalc->dims.xmax,
&precalc->dims.ymin,
&precalc->dims.ymax,
&precalc->dims.zmin,
&precalc->dims.zmax);
for (int i = 0; i < 4; i++) {
precalc->view[i] = (float)view[i];
}
if (!invert_m4_m4(precalc->model_inverted, model)) {
unit_m4(precalc->model_inverted);
return false;
}
return true;
}
copy_v3_v3(in, win);
/* Map x and y from window coordinates */
in[0] = (in[0] - view[0]) / view[2];
in[1] = (in[1] - view[1]) / view[3];
gpu_mul_invert_projmat_m4_unmapped_v3(proj, in);
mul_v3_m4v3(world, model_inverted, in);
void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
const float win[3],
float r_world[3])
{
float in[3] = {
(win[0] - precalc->view[0]) / precalc->view[2],
(win[1] - precalc->view[1]) / precalc->view[3],
win[2],
};
gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(precalc, in);
mul_v3_m4v3(r_world, precalc->model_inverted, in);
}
bool GPU_matrix_unproject(const float win[3],
const float model[4][4],
const float proj[4][4],
const int view[4],
float world[3])
float r_world[3])
{
float model_inverted[4][4];
if (!invert_m4_m4(model_inverted, model)) {
zero_v3(world);
struct GPUMatrixUnproject_Precalc precalc;
if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) {
zero_v3(r_world);
return false;
}
GPU_matrix_unproject_model_inverted(win, model_inverted, proj, view, world);
GPU_matrix_unproject_with_precalc(&precalc, win, r_world);
return true;
}

View File

@ -571,8 +571,11 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
const int viewport[4] = {0, 0, ar->winx, ar->winy};
float co_3d_origin[3];
GPU_matrix_unproject_model_inverted(
co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d_origin);
/* Avoid multiple calculations. */
struct GPUMatrixUnproject_Precalc unproj_precalc;
GPU_matrix_unproject_precalc(&unproj_precalc, rv3d->viewmat, rv3d->winmat, viewport);
GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d_origin);
GLuint *buf_iter = buffer;
int hit_found = -1;
@ -583,7 +586,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos,
wmGizmo *gz = visible_gizmos[buf_iter[3] >> 8];
float co_3d[3];
co_screen[2] = int_as_float(buf_iter[1]);
GPU_matrix_unproject_model_inverted(co_screen, rv3d->viewinv, rv3d->winmat, viewport, co_3d);
GPU_matrix_unproject_with_precalc(&unproj_precalc, co_screen, co_3d);
float select_bias = gz->select_bias;
if ((gz->flag & WM_GIZMO_DRAW_NO_SCALE) == 0) {
select_bias *= gz->scale_final;