1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2012 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup gpu
22  */
23 
24 #include "gpu_context_private.hh"
25 #include "gpu_matrix_private.h"
26 
27 #define SUPPRESS_GENERIC_MATRIX_API
28 #define USE_GPU_PY_MATRIX_API /* only so values are declared */
29 #include "GPU_matrix.h"
30 #undef USE_GPU_PY_MATRIX_API
31 
32 #include "BLI_math_matrix.h"
33 #include "BLI_math_rotation.h"
34 #include "BLI_math_vector.h"
35 
36 #include "MEM_guardedalloc.h"
37 
38 using namespace blender::gpu;
39 
40 #define MATRIX_STACK_DEPTH 32
41 
42 typedef float Mat4[4][4];
43 typedef float Mat3[3][3];
44 
45 typedef struct MatrixStack {
46   Mat4 stack[MATRIX_STACK_DEPTH];
47   uint top;
48 } MatrixStack;
49 
50 typedef struct GPUMatrixState {
51   MatrixStack model_view_stack;
52   MatrixStack projection_stack;
53 
54   bool dirty;
55 
56   /* TODO: cache of derived matrices (Normal, MVP, inverse MVP, etc)
57    * generate as needed for shaders, invalidate when original matrices change
58    *
59    * TODO: separate Model from View transform? Batches/objects have model,
60    * camera/eye has view & projection
61    */
62 } GPUMatrixState;
63 
64 #define ModelViewStack Context::get()->matrix_state->model_view_stack
65 #define ModelView ModelViewStack.stack[ModelViewStack.top]
66 
67 #define ProjectionStack Context::get()->matrix_state->projection_stack
68 #define Projection ProjectionStack.stack[ProjectionStack.top]
69 
GPU_matrix_state_create(void)70 GPUMatrixState *GPU_matrix_state_create(void)
71 {
72 #define MATRIX_4X4_IDENTITY \
73   { \
74     {1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, \
75     { \
76       0.0f, 0.0f, 0.0f, 1.0f \
77     } \
78   }
79 
80   GPUMatrixState *state = (GPUMatrixState *)MEM_mallocN(sizeof(*state), __func__);
81   const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0};
82 
83   state->model_view_stack = state->projection_stack = identity_stack;
84   state->dirty = true;
85 
86 #undef MATRIX_4X4_IDENTITY
87 
88   return state;
89 }
90 
GPU_matrix_state_discard(GPUMatrixState * state)91 void GPU_matrix_state_discard(GPUMatrixState *state)
92 {
93   MEM_freeN(state);
94 }
95 
gpu_matrix_state_active_set_dirty(bool value)96 static void gpu_matrix_state_active_set_dirty(bool value)
97 {
98   GPUMatrixState *state = Context::get()->matrix_state;
99   state->dirty = value;
100 }
101 
GPU_matrix_reset(void)102 void GPU_matrix_reset(void)
103 {
104   GPUMatrixState *state = Context::get()->matrix_state;
105   state->model_view_stack.top = 0;
106   state->projection_stack.top = 0;
107   unit_m4(ModelView);
108   unit_m4(Projection);
109   gpu_matrix_state_active_set_dirty(true);
110 }
111 
112 #ifdef WITH_GPU_SAFETY
113 
114 /* Check if matrix is numerically good */
checkmat(cosnt float * m)115 static void checkmat(cosnt float *m)
116 {
117   const int n = 16;
118   for (int i = 0; i < n; i++) {
119 #  if _MSC_VER
120     BLI_assert(_finite(m[i]));
121 #  else
122     BLI_assert(!std::isinf(m[i]));
123 #  endif
124   }
125 }
126 
127 #  define CHECKMAT(m) checkmat((const float *)m)
128 
129 #else
130 
131 #  define CHECKMAT(m)
132 
133 #endif
134 
GPU_matrix_push(void)135 void GPU_matrix_push(void)
136 {
137   BLI_assert(ModelViewStack.top + 1 < MATRIX_STACK_DEPTH);
138   ModelViewStack.top++;
139   copy_m4_m4(ModelView, ModelViewStack.stack[ModelViewStack.top - 1]);
140 }
141 
GPU_matrix_pop(void)142 void GPU_matrix_pop(void)
143 {
144   BLI_assert(ModelViewStack.top > 0);
145   ModelViewStack.top--;
146   gpu_matrix_state_active_set_dirty(true);
147 }
148 
GPU_matrix_push_projection(void)149 void GPU_matrix_push_projection(void)
150 {
151   BLI_assert(ProjectionStack.top + 1 < MATRIX_STACK_DEPTH);
152   ProjectionStack.top++;
153   copy_m4_m4(Projection, ProjectionStack.stack[ProjectionStack.top - 1]);
154 }
155 
GPU_matrix_pop_projection(void)156 void GPU_matrix_pop_projection(void)
157 {
158   BLI_assert(ProjectionStack.top > 0);
159   ProjectionStack.top--;
160   gpu_matrix_state_active_set_dirty(true);
161 }
162 
GPU_matrix_set(const float m[4][4])163 void GPU_matrix_set(const float m[4][4])
164 {
165   copy_m4_m4(ModelView, m);
166   CHECKMAT(ModelView3D);
167   gpu_matrix_state_active_set_dirty(true);
168 }
169 
GPU_matrix_identity_projection_set(void)170 void GPU_matrix_identity_projection_set(void)
171 {
172   unit_m4(Projection);
173   CHECKMAT(Projection3D);
174   gpu_matrix_state_active_set_dirty(true);
175 }
176 
GPU_matrix_projection_set(const float m[4][4])177 void GPU_matrix_projection_set(const float m[4][4])
178 {
179   copy_m4_m4(Projection, m);
180   CHECKMAT(Projection3D);
181   gpu_matrix_state_active_set_dirty(true);
182 }
183 
GPU_matrix_identity_set(void)184 void GPU_matrix_identity_set(void)
185 {
186   unit_m4(ModelView);
187   gpu_matrix_state_active_set_dirty(true);
188 }
189 
GPU_matrix_translate_2f(float x,float y)190 void GPU_matrix_translate_2f(float x, float y)
191 {
192   Mat4 m;
193   unit_m4(m);
194   m[3][0] = x;
195   m[3][1] = y;
196   GPU_matrix_mul(m);
197 }
198 
GPU_matrix_translate_2fv(const float vec[2])199 void GPU_matrix_translate_2fv(const float vec[2])
200 {
201   GPU_matrix_translate_2f(vec[0], vec[1]);
202 }
203 
GPU_matrix_translate_3f(float x,float y,float z)204 void GPU_matrix_translate_3f(float x, float y, float z)
205 {
206 #if 1
207   translate_m4(ModelView, x, y, z);
208   CHECKMAT(ModelView);
209 #else /* above works well in early testing, below is generic version */
210   Mat4 m;
211   unit_m4(m);
212   m[3][0] = x;
213   m[3][1] = y;
214   m[3][2] = z;
215   GPU_matrix_mul(m);
216 #endif
217   gpu_matrix_state_active_set_dirty(true);
218 }
219 
GPU_matrix_translate_3fv(const float vec[3])220 void GPU_matrix_translate_3fv(const float vec[3])
221 {
222   GPU_matrix_translate_3f(vec[0], vec[1], vec[2]);
223 }
224 
GPU_matrix_scale_1f(float factor)225 void GPU_matrix_scale_1f(float factor)
226 {
227   Mat4 m;
228   scale_m4_fl(m, factor);
229   GPU_matrix_mul(m);
230 }
231 
GPU_matrix_scale_2f(float x,float y)232 void GPU_matrix_scale_2f(float x, float y)
233 {
234   Mat4 m = {{0.0f}};
235   m[0][0] = x;
236   m[1][1] = y;
237   m[2][2] = 1.0f;
238   m[3][3] = 1.0f;
239   GPU_matrix_mul(m);
240 }
241 
GPU_matrix_scale_2fv(const float vec[2])242 void GPU_matrix_scale_2fv(const float vec[2])
243 {
244   GPU_matrix_scale_2f(vec[0], vec[1]);
245 }
246 
GPU_matrix_scale_3f(float x,float y,float z)247 void GPU_matrix_scale_3f(float x, float y, float z)
248 {
249   Mat4 m = {{0.0f}};
250   m[0][0] = x;
251   m[1][1] = y;
252   m[2][2] = z;
253   m[3][3] = 1.0f;
254   GPU_matrix_mul(m);
255 }
256 
GPU_matrix_scale_3fv(const float vec[3])257 void GPU_matrix_scale_3fv(const float vec[3])
258 {
259   GPU_matrix_scale_3f(vec[0], vec[1], vec[2]);
260 }
261 
GPU_matrix_mul(const float m[4][4])262 void GPU_matrix_mul(const float m[4][4])
263 {
264   mul_m4_m4_post(ModelView, m);
265   CHECKMAT(ModelView);
266   gpu_matrix_state_active_set_dirty(true);
267 }
268 
GPU_matrix_rotate_2d(float deg)269 void GPU_matrix_rotate_2d(float deg)
270 {
271   /* essentially RotateAxis('Z')
272    * TODO: simpler math for 2D case
273    */
274   rotate_m4(ModelView, 'Z', DEG2RADF(deg));
275 }
276 
GPU_matrix_rotate_3f(float deg,float x,float y,float z)277 void GPU_matrix_rotate_3f(float deg, float x, float y, float z)
278 {
279   const float axis[3] = {x, y, z};
280   GPU_matrix_rotate_3fv(deg, axis);
281 }
282 
GPU_matrix_rotate_3fv(float deg,const float axis[3])283 void GPU_matrix_rotate_3fv(float deg, const float axis[3])
284 {
285   Mat4 m;
286   axis_angle_to_mat4(m, axis, DEG2RADF(deg));
287   GPU_matrix_mul(m);
288 }
289 
GPU_matrix_rotate_axis(float deg,char axis)290 void GPU_matrix_rotate_axis(float deg, char axis)
291 {
292   /* rotate_m4 works in place */
293   rotate_m4(ModelView, axis, DEG2RADF(deg));
294   CHECKMAT(ModelView);
295   gpu_matrix_state_active_set_dirty(true);
296 }
297 
mat4_ortho_set(float m[4][4],float left,float right,float bottom,float top,float near,float far)298 static void mat4_ortho_set(
299     float m[4][4], float left, float right, float bottom, float top, float near, float far)
300 {
301   m[0][0] = 2.0f / (right - left);
302   m[1][0] = 0.0f;
303   m[2][0] = 0.0f;
304   m[3][0] = -(right + left) / (right - left);
305 
306   m[0][1] = 0.0f;
307   m[1][1] = 2.0f / (top - bottom);
308   m[2][1] = 0.0f;
309   m[3][1] = -(top + bottom) / (top - bottom);
310 
311   m[0][2] = 0.0f;
312   m[1][2] = 0.0f;
313   m[2][2] = -2.0f / (far - near);
314   m[3][2] = -(far + near) / (far - near);
315 
316   m[0][3] = 0.0f;
317   m[1][3] = 0.0f;
318   m[2][3] = 0.0f;
319   m[3][3] = 1.0f;
320 
321   gpu_matrix_state_active_set_dirty(true);
322 }
323 
mat4_frustum_set(float m[4][4],float left,float right,float bottom,float top,float near,float far)324 static void mat4_frustum_set(
325     float m[4][4], float left, float right, float bottom, float top, float near, float far)
326 {
327   m[0][0] = 2.0f * near / (right - left);
328   m[1][0] = 0.0f;
329   m[2][0] = (right + left) / (right - left);
330   m[3][0] = 0.0f;
331 
332   m[0][1] = 0.0f;
333   m[1][1] = 2.0f * near / (top - bottom);
334   m[2][1] = (top + bottom) / (top - bottom);
335   m[3][1] = 0.0f;
336 
337   m[0][2] = 0.0f;
338   m[1][2] = 0.0f;
339   m[2][2] = -(far + near) / (far - near);
340   m[3][2] = -2.0f * far * near / (far - near);
341 
342   m[0][3] = 0.0f;
343   m[1][3] = 0.0f;
344   m[2][3] = -1.0f;
345   m[3][3] = 0.0f;
346 
347   gpu_matrix_state_active_set_dirty(true);
348 }
349 
mat4_look_from_origin(float m[4][4],float lookdir[3],float camup[3])350 static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3])
351 {
352   /* This function is loosely based on Mesa implementation.
353    *
354    * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008)
355    * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved.
356    *
357    * Permission is hereby granted, free of charge, to any person obtaining a
358    * copy of this software and associated documentation files (the "Software"),
359    * to deal in the Software without restriction, including without limitation
360    * the rights to use, copy, modify, merge, publish, distribute, sublicense,
361    * and/or sell copies of the Software, and to permit persons to whom the
362    * Software is furnished to do so, subject to the following conditions:
363    *
364    * The above copyright notice including the dates of first publication and
365    * either this permission notice or a reference to
366    * http://oss.sgi.com/projects/FreeB/
367    * shall be included in all copies or substantial portions of the Software.
368    *
369    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
370    * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
371    * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
372    * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
373    * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
374    * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
375    * SOFTWARE.
376    *
377    * Except as contained in this notice, the name of Silicon Graphics, Inc.
378    * shall not be used in advertising or otherwise to promote the sale, use or
379    * other dealings in this Software without prior written authorization from
380    * Silicon Graphics, Inc.
381    */
382   float side[3];
383 
384   normalize_v3(lookdir);
385 
386   cross_v3_v3v3(side, lookdir, camup);
387 
388   normalize_v3(side);
389 
390   cross_v3_v3v3(camup, side, lookdir);
391 
392   m[0][0] = side[0];
393   m[1][0] = side[1];
394   m[2][0] = side[2];
395   m[3][0] = 0.0f;
396 
397   m[0][1] = camup[0];
398   m[1][1] = camup[1];
399   m[2][1] = camup[2];
400   m[3][1] = 0.0f;
401 
402   m[0][2] = -lookdir[0];
403   m[1][2] = -lookdir[1];
404   m[2][2] = -lookdir[2];
405   m[3][2] = 0.0f;
406 
407   m[0][3] = 0.0f;
408   m[1][3] = 0.0f;
409   m[2][3] = 0.0f;
410   m[3][3] = 1.0f;
411 
412   gpu_matrix_state_active_set_dirty(true);
413 }
414 
GPU_matrix_ortho_set(float left,float right,float bottom,float top,float near,float far)415 void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far)
416 {
417   mat4_ortho_set(Projection, left, right, bottom, top, near, far);
418   CHECKMAT(Projection);
419   gpu_matrix_state_active_set_dirty(true);
420 }
421 
GPU_matrix_ortho_set_z(float near,float far)422 void GPU_matrix_ortho_set_z(float near, float far)
423 {
424   CHECKMAT(Projection);
425   Projection[2][2] = -2.0f / (far - near);
426   Projection[3][2] = -(far + near) / (far - near);
427   gpu_matrix_state_active_set_dirty(true);
428 }
429 
GPU_matrix_ortho_2d_set(float left,float right,float bottom,float top)430 void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top)
431 {
432   Mat4 m;
433   mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f);
434   CHECKMAT(Projection2D);
435   gpu_matrix_state_active_set_dirty(true);
436 }
437 
GPU_matrix_frustum_set(float left,float right,float bottom,float top,float near,float far)438 void GPU_matrix_frustum_set(
439     float left, float right, float bottom, float top, float near, float far)
440 {
441   mat4_frustum_set(Projection, left, right, bottom, top, near, far);
442   CHECKMAT(Projection);
443   gpu_matrix_state_active_set_dirty(true);
444 }
445 
GPU_matrix_perspective_set(float fovy,float aspect,float near,float far)446 void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far)
447 {
448   float half_height = tanf(fovy * (float)(M_PI / 360.0)) * near;
449   float half_width = half_height * aspect;
450   GPU_matrix_frustum_set(-half_width, +half_width, -half_height, +half_height, near, far);
451 }
452 
GPU_matrix_look_at(float eyeX,float eyeY,float eyeZ,float centerX,float centerY,float centerZ,float upX,float upY,float upZ)453 void GPU_matrix_look_at(float eyeX,
454                         float eyeY,
455                         float eyeZ,
456                         float centerX,
457                         float centerY,
458                         float centerZ,
459                         float upX,
460                         float upY,
461                         float upZ)
462 {
463   Mat4 cm;
464   float lookdir[3];
465   float camup[3] = {upX, upY, upZ};
466 
467   lookdir[0] = centerX - eyeX;
468   lookdir[1] = centerY - eyeY;
469   lookdir[2] = centerZ - eyeZ;
470 
471   mat4_look_from_origin(cm, lookdir, camup);
472 
473   GPU_matrix_mul(cm);
474   GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ);
475 }
476 
GPU_matrix_project(const float world[3],const float model[4][4],const float proj[4][4],const int view[4],float win[3])477 void GPU_matrix_project(const float world[3],
478                         const float model[4][4],
479                         const float proj[4][4],
480                         const int view[4],
481                         float win[3])
482 {
483   float v[4];
484 
485   mul_v4_m4v3(v, model, world);
486   mul_m4_v4(proj, v);
487 
488   if (v[3] != 0.0f) {
489     mul_v3_fl(v, 1.0f / v[3]);
490   }
491 
492   win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f;
493   win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f;
494   win[2] = (v[2] + 1) * 0.5f;
495 }
496 
497 /**
498  * The same result could be obtained as follows:
499  *
500  * \code{.c}
501  * float projinv[4][4];
502  * invert_m4_m4(projinv, projmat);
503  * co[0] = 2 * co[0] - 1;
504  * co[1] = 2 * co[1] - 1;
505  * co[2] = 2 * co[2] - 1;
506  * mul_project_m4_v3(projinv, co);
507  * \endcode
508  *
509  * But that solution loses much precision.
510  * Therefore, get the same result without inverting the matrix.
511  */
gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(const struct GPUMatrixUnproject_Precalc * precalc,float co[3])512 static void gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(
513     const struct GPUMatrixUnproject_Precalc *precalc, float co[3])
514 {
515   /* 'precalc->dims' is the result of 'projmat_dimensions(proj, ...)'. */
516   co[0] = precalc->dims.xmin + co[0] * (precalc->dims.xmax - precalc->dims.xmin);
517   co[1] = precalc->dims.ymin + co[1] * (precalc->dims.ymax - precalc->dims.ymin);
518 
519   if (precalc->is_persp) {
520     co[2] = precalc->dims.zmax * precalc->dims.zmin /
521             (precalc->dims.zmax + co[2] * (precalc->dims.zmin - precalc->dims.zmax));
522     co[0] *= co[2];
523     co[1] *= co[2];
524   }
525   else {
526     co[2] = precalc->dims.zmin + co[2] * (precalc->dims.zmax - precalc->dims.zmin);
527   }
528   co[2] *= -1;
529 }
530 
GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc * precalc,const float model[4][4],const float proj[4][4],const int view[4])531 bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc,
532                                   const float model[4][4],
533                                   const float proj[4][4],
534                                   const int view[4])
535 {
536   precalc->is_persp = proj[3][3] == 0.0f;
537   projmat_dimensions_db(proj,
538                         &precalc->dims.xmin,
539                         &precalc->dims.xmax,
540                         &precalc->dims.ymin,
541                         &precalc->dims.ymax,
542                         &precalc->dims.zmin,
543                         &precalc->dims.zmax);
544   if (std::isinf(precalc->dims.zmax)) {
545     /* We cannot retrieve the actual value of the clip_end.
546      * Use `FLT_MAX` to avoid nans. */
547     precalc->dims.zmax = FLT_MAX;
548   }
549   for (int i = 0; i < 4; i++) {
550     precalc->view[i] = (float)view[i];
551   }
552   if (!invert_m4_m4(precalc->model_inverted, model)) {
553     unit_m4(precalc->model_inverted);
554     return false;
555   }
556   return true;
557 }
558 
GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc * precalc,const float win[3],float r_world[3])559 void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc,
560                                        const float win[3],
561                                        float r_world[3])
562 {
563   float in[3] = {
564       (win[0] - precalc->view[0]) / precalc->view[2],
565       (win[1] - precalc->view[1]) / precalc->view[3],
566       win[2],
567   };
568   gpu_mul_invert_projmat_m4_unmapped_v3_with_precalc(precalc, in);
569   mul_v3_m4v3(r_world, precalc->model_inverted, in);
570 }
571 
GPU_matrix_unproject(const float win[3],const float model[4][4],const float proj[4][4],const int view[4],float r_world[3])572 bool GPU_matrix_unproject(const float win[3],
573                           const float model[4][4],
574                           const float proj[4][4],
575                           const int view[4],
576                           float r_world[3])
577 {
578   struct GPUMatrixUnproject_Precalc precalc;
579   if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) {
580     zero_v3(r_world);
581     return false;
582   }
583   GPU_matrix_unproject_with_precalc(&precalc, win, r_world);
584   return true;
585 }
586 
GPU_matrix_model_view_get(float m[4][4])587 const float (*GPU_matrix_model_view_get(float m[4][4]))[4]
588 {
589   if (m) {
590     copy_m4_m4(m, ModelView);
591     return m;
592   }
593 
594   return ModelView;
595 }
596 
597 const float (*GPU_matrix_projection_get(float m[4][4]))[4]
__anon0378d72e0202null598 {
599   if (m) {
600     copy_m4_m4(m, Projection);
601     return m;
602   }
603 
604   return Projection;
605 }
606 
607 const float (*GPU_matrix_model_view_projection_get(float m[4][4]))[4]
__anon0378d72e0302null608 {
609   if (m == NULL) {
610     static Mat4 temp;
611     m = temp;
612   }
613 
614   mul_m4_m4m4(m, Projection, ModelView);
615   return m;
616 }
617 
618 const float (*GPU_matrix_normal_get(float m[3][3]))[3]
__anon0378d72e0402null619 {
620   if (m == NULL) {
621     static Mat3 temp3;
622     m = temp3;
623   }
624 
625   copy_m3_m4(m, (const float(*)[4])GPU_matrix_model_view_get(NULL));
626 
627   invert_m3(m);
628   transpose_m3(m);
629 
630   return m;
631 }
632 
633 const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3]
__anon0378d72e0502null634 {
635   if (m == NULL) {
636     static Mat3 temp3;
637     m = temp3;
638   }
639 
640   GPU_matrix_normal_get(m);
641   invert_m3(m);
642 
643   return m;
644 }
645 
646 void GPU_matrix_bind(GPUShader *shader)
647 {
648   /* set uniform values to matrix stack values
649    * call this before a draw call if desired matrices are dirty
650    * call glUseProgram before this, as glUniform expects program to be bound
651    */
652   int32_t MV = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW);
653   int32_t P = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION);
654   int32_t MVP = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MVP);
655 
656   int32_t N = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_NORMAL);
657   int32_t MV_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV);
658   int32_t P_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION_INV);
659 
660   if (MV != -1) {
661     GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL));
662   }
663   if (P != -1) {
664     GPU_shader_uniform_vector(shader, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL));
665   }
666   if (MVP != -1) {
667     GPU_shader_uniform_vector(
668         shader, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL));
669   }
670   if (N != -1) {
671     GPU_shader_uniform_vector(shader, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL));
672   }
673   if (MV_inv != -1) {
674     Mat4 m;
675     GPU_matrix_model_view_get(m);
676     invert_m4(m);
677     GPU_shader_uniform_vector(shader, MV_inv, 16, 1, (const float *)m);
678   }
679   if (P_inv != -1) {
680     Mat4 m;
681     GPU_matrix_projection_get(m);
682     invert_m4(m);
683     GPU_shader_uniform_vector(shader, P_inv, 16, 1, (const float *)m);
684   }
685 
686   gpu_matrix_state_active_set_dirty(false);
687 }
688 
GPU_matrix_dirty_get(void)689 bool GPU_matrix_dirty_get(void)
690 {
691   GPUMatrixState *state = Context::get()->matrix_state;
692   return state->dirty;
693 }
694 
695 /* -------------------------------------------------------------------- */
696 /** \name Python API Helpers
697  * \{ */
698 BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mismatch");
699 
700 /* Return int since caller is may subtract. */
701 
GPU_matrix_stack_level_get_model_view(void)702 int GPU_matrix_stack_level_get_model_view(void)
703 {
704   GPUMatrixState *state = Context::get()->matrix_state;
705   return (int)state->model_view_stack.top;
706 }
707 
GPU_matrix_stack_level_get_projection(void)708 int GPU_matrix_stack_level_get_projection(void)
709 {
710   GPUMatrixState *state = Context::get()->matrix_state;
711   return (int)state->projection_stack.top;
712 }
713 
714 /** \} */
715 
716 /* -------------------------------------------------------------------- */
717 /** \name Polygon Offset Hack
718  *
719  * Workaround the fact that polygon-offset is implementation dependent.
720  * We modify the projection matrix \a winmat in order to change the final depth a tiny amount.
721  * \{ */
722 
GPU_polygon_offset_calc(const float (* winmat)[4],float viewdist,float dist)723 float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float dist)
724 {
725   /* Seems like we have a factor of 2 more offset than 2.79 for some reason. Correct for this. */
726   dist *= 0.5f;
727 
728   if (winmat[3][3] > 0.5f) {
729 #if 1
730     return 0.00001f * dist * viewdist;  // ortho tweaking
731 #else
732     static float depth_fac = 0.0f;
733     if (depth_fac == 0.0f) {
734       /* Hardcode for 24 bit precision. */
735       int depthbits = 24;
736       depth_fac = 1.0f / (float)((1 << depthbits) - 1);
737     }
738     offs = (-1.0 / winmat[2][2]) * dist * depth_fac;
739 
740     UNUSED_VARS(viewdist);
741 #endif
742   }
743 
744   /* This adjustment effectively results in reducing the Z value by 0.25%.
745    *
746    * winmat[4][3] actually evaluates to `-2 * far * near / (far - near)`,
747    * is very close to -0.2 with default clip range,
748    * and is used as the coefficient multiplied by `w / z`,
749    * thus controlling the z dependent part of the depth value.
750    */
751   return winmat[3][2] * -0.0025f * dist;
752 }
753 
754 /**
755  * \note \a viewdist is only for ortho at the moment.
756  */
GPU_polygon_offset(float viewdist,float dist)757 void GPU_polygon_offset(float viewdist, float dist)
758 {
759   static float winmat[4][4], offset = 0.0f;
760 
761   if (dist != 0.0f) {
762     /* hack below is to mimic polygon offset */
763     GPU_matrix_projection_get(winmat);
764 
765     /* dist is from camera to center point */
766 
767     float offs = GPU_polygon_offset_calc(winmat, viewdist, dist);
768 
769     winmat[3][2] -= offs;
770     offset += offs;
771   }
772   else {
773     winmat[3][2] += offset;
774     offset = 0.0;
775   }
776 
777   GPU_matrix_projection_set(winmat);
778 }
779 
780 /** \} */
781