Changeset View
Changeset View
Standalone View
Standalone View
source/blender/gpu/intern/gpu_matrix.c
| Show All 17 Lines | |||||
| */ | */ | ||||
| /** \file | /** \file | ||||
| * \ingroup gpu | * \ingroup gpu | ||||
| */ | */ | ||||
| #include "GPU_shader_interface.h" | #include "GPU_shader_interface.h" | ||||
| #include "gpu_context_private.h" | |||||
| #include "gpu_matrix_private.h" | |||||
| #define SUPPRESS_GENERIC_MATRIX_API | #define SUPPRESS_GENERIC_MATRIX_API | ||||
| #define USE_GPU_PY_MATRIX_API /* only so values are declared */ | #define USE_GPU_PY_MATRIX_API /* only so values are declared */ | ||||
| #include "GPU_matrix.h" | #include "GPU_matrix.h" | ||||
| #undef USE_GPU_PY_MATRIX_API | #undef USE_GPU_PY_MATRIX_API | ||||
| #include "BLI_math_matrix.h" | #include "BLI_math_matrix.h" | ||||
| #include "BLI_math_rotation.h" | #include "BLI_math_rotation.h" | ||||
| #include "BLI_math_vector.h" | #include "BLI_math_vector.h" | ||||
| #include "MEM_guardedalloc.h" | |||||
| #define DEBUG_MATRIX_BIND 0 | #define DEBUG_MATRIX_BIND 0 | ||||
| #define MATRIX_STACK_DEPTH 32 | #define MATRIX_STACK_DEPTH 32 | ||||
| typedef float Mat4[4][4]; | typedef float Mat4[4][4]; | ||||
| typedef float Mat3[3][3]; | typedef float Mat3[3][3]; | ||||
| typedef struct MatrixStack { | typedef struct MatrixStack { | ||||
| Mat4 stack[MATRIX_STACK_DEPTH]; | Mat4 stack[MATRIX_STACK_DEPTH]; | ||||
| uint top; | uint top; | ||||
| } MatrixStack; | } MatrixStack; | ||||
| typedef struct { | typedef struct GPUMatrixState { | ||||
| MatrixStack model_view_stack; | MatrixStack model_view_stack; | ||||
| MatrixStack projection_stack; | MatrixStack projection_stack; | ||||
| bool dirty; | bool dirty; | ||||
| /* TODO: cache of derived matrices (Normal, MVP, inverse MVP, etc) | /* TODO: cache of derived matrices (Normal, MVP, inverse MVP, etc) | ||||
| * generate as needed for shaders, invalidate when original matrices change | * generate as needed for shaders, invalidate when original matrices change | ||||
| * | * | ||||
| * TODO: separate Model from View transform? Batches/objects have model, | * TODO: separate Model from View transform? Batches/objects have model, | ||||
| * camera/eye has view & projection | * camera/eye has view & projection | ||||
| */ | */ | ||||
| } MatrixState; | } GPUMatrixState; | ||||
| #define ModelViewStack gpu_context_active_matrix_state_get()->model_view_stack | |||||
| #define ModelView ModelViewStack.stack[ModelViewStack.top] | |||||
| #define ProjectionStack gpu_context_active_matrix_state_get()->projection_stack | |||||
| #define Projection ProjectionStack.stack[ProjectionStack.top] | |||||
| GPUMatrixState *GPU_matrix_state_create(void) | |||||
| { | |||||
| #define MATRIX_4X4_IDENTITY \ | #define MATRIX_4X4_IDENTITY \ | ||||
| { \ | { \ | ||||
| {1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, \ | {1.0f, 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f, 0.0f}, \ | ||||
| { \ | { \ | ||||
| 0.0f, 0.0f, 0.0f, 1.0f \ | 0.0f, 0.0f, 0.0f, 1.0f \ | ||||
| } \ | } \ | ||||
| } | } | ||||
| static MatrixState state = { | GPUMatrixState *state = MEM_mallocN(sizeof(*state), __func__); | ||||
| .model_view_stack = {{MATRIX_4X4_IDENTITY}, 0}, | const MatrixStack identity_stack = {{MATRIX_4X4_IDENTITY}, 0}; | ||||
| .projection_stack = {{MATRIX_4X4_IDENTITY}, 0}, | |||||
| .dirty = true, | state->model_view_stack = state->projection_stack = identity_stack; | ||||
| }; | state->dirty = true; | ||||
| #undef MATRIX_4X4_IDENTITY | #undef MATRIX_4X4_IDENTITY | ||||
| #define ModelViewStack state.model_view_stack | return state; | ||||
| #define ModelView ModelViewStack.stack[ModelViewStack.top] | } | ||||
| #define ProjectionStack state.projection_stack | void GPU_matrix_state_discard(GPUMatrixState *state) | ||||
| #define Projection ProjectionStack.stack[ProjectionStack.top] | { | ||||
| MEM_freeN(state); | |||||
| } | |||||
| static void gpu_matrix_state_active_set_dirty(bool value) | |||||
| { | |||||
| GPUMatrixState *state = gpu_context_active_matrix_state_get(); | |||||
| state->dirty = value; | |||||
| } | |||||
| void GPU_matrix_reset(void) | void GPU_matrix_reset(void) | ||||
| { | { | ||||
| state.model_view_stack.top = 0; | GPUMatrixState *state = gpu_context_active_matrix_state_get(); | ||||
| state.projection_stack.top = 0; | state->model_view_stack.top = 0; | ||||
| state->projection_stack.top = 0; | |||||
| unit_m4(ModelView); | unit_m4(ModelView); | ||||
| unit_m4(Projection); | unit_m4(Projection); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| #ifdef WITH_GPU_SAFETY | #ifdef WITH_GPU_SAFETY | ||||
| /* Check if matrix is numerically good */ | /* Check if matrix is numerically good */ | ||||
| static void checkmat(cosnt float *m) | static void checkmat(cosnt float *m) | ||||
| { | { | ||||
| const int n = 16; | const int n = 16; | ||||
| Show All 20 Lines | void GPU_matrix_push(void) | ||||
| ModelViewStack.top++; | ModelViewStack.top++; | ||||
| copy_m4_m4(ModelView, ModelViewStack.stack[ModelViewStack.top - 1]); | copy_m4_m4(ModelView, ModelViewStack.stack[ModelViewStack.top - 1]); | ||||
| } | } | ||||
| void GPU_matrix_pop(void) | void GPU_matrix_pop(void) | ||||
| { | { | ||||
| BLI_assert(ModelViewStack.top > 0); | BLI_assert(ModelViewStack.top > 0); | ||||
| ModelViewStack.top--; | ModelViewStack.top--; | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_push_projection(void) | void GPU_matrix_push_projection(void) | ||||
| { | { | ||||
| BLI_assert(ProjectionStack.top + 1 < MATRIX_STACK_DEPTH); | BLI_assert(ProjectionStack.top + 1 < MATRIX_STACK_DEPTH); | ||||
| ProjectionStack.top++; | ProjectionStack.top++; | ||||
| copy_m4_m4(Projection, ProjectionStack.stack[ProjectionStack.top - 1]); | copy_m4_m4(Projection, ProjectionStack.stack[ProjectionStack.top - 1]); | ||||
| } | } | ||||
| void GPU_matrix_pop_projection(void) | void GPU_matrix_pop_projection(void) | ||||
| { | { | ||||
| BLI_assert(ProjectionStack.top > 0); | BLI_assert(ProjectionStack.top > 0); | ||||
| ProjectionStack.top--; | ProjectionStack.top--; | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_set(const float m[4][4]) | void GPU_matrix_set(const float m[4][4]) | ||||
| { | { | ||||
| copy_m4_m4(ModelView, m); | copy_m4_m4(ModelView, m); | ||||
| CHECKMAT(ModelView3D); | CHECKMAT(ModelView3D); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_identity_projection_set(void) | void GPU_matrix_identity_projection_set(void) | ||||
| { | { | ||||
| unit_m4(Projection); | unit_m4(Projection); | ||||
| CHECKMAT(Projection3D); | CHECKMAT(Projection3D); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_projection_set(const float m[4][4]) | void GPU_matrix_projection_set(const float m[4][4]) | ||||
| { | { | ||||
| copy_m4_m4(Projection, m); | copy_m4_m4(Projection, m); | ||||
| CHECKMAT(Projection3D); | CHECKMAT(Projection3D); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_identity_set(void) | void GPU_matrix_identity_set(void) | ||||
| { | { | ||||
| unit_m4(ModelView); | unit_m4(ModelView); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_translate_2f(float x, float y) | void GPU_matrix_translate_2f(float x, float y) | ||||
| { | { | ||||
| Mat4 m; | Mat4 m; | ||||
| unit_m4(m); | unit_m4(m); | ||||
| m[3][0] = x; | m[3][0] = x; | ||||
| m[3][1] = y; | m[3][1] = y; | ||||
| Show All 13 Lines | |||||
| #else /* above works well in early testing, below is generic version */ | #else /* above works well in early testing, below is generic version */ | ||||
| Mat4 m; | Mat4 m; | ||||
| unit_m4(m); | unit_m4(m); | ||||
| m[3][0] = x; | m[3][0] = x; | ||||
| m[3][1] = y; | m[3][1] = y; | ||||
| m[3][2] = z; | m[3][2] = z; | ||||
| GPU_matrix_mul(m); | GPU_matrix_mul(m); | ||||
| #endif | #endif | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_translate_3fv(const float vec[3]) | void GPU_matrix_translate_3fv(const float vec[3]) | ||||
| { | { | ||||
| GPU_matrix_translate_3f(vec[0], vec[1], vec[2]); | GPU_matrix_translate_3f(vec[0], vec[1], vec[2]); | ||||
| } | } | ||||
| void GPU_matrix_scale_1f(float factor) | void GPU_matrix_scale_1f(float factor) | ||||
| Show All 32 Lines | |||||
| { | { | ||||
| GPU_matrix_scale_3f(vec[0], vec[1], vec[2]); | GPU_matrix_scale_3f(vec[0], vec[1], vec[2]); | ||||
| } | } | ||||
| void GPU_matrix_mul(const float m[4][4]) | void GPU_matrix_mul(const float m[4][4]) | ||||
| { | { | ||||
| mul_m4_m4_post(ModelView, m); | mul_m4_m4_post(ModelView, m); | ||||
| CHECKMAT(ModelView); | CHECKMAT(ModelView); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_rotate_2d(float deg) | void GPU_matrix_rotate_2d(float deg) | ||||
| { | { | ||||
| /* essentially RotateAxis('Z') | /* essentially RotateAxis('Z') | ||||
| * TODO: simpler math for 2D case | * TODO: simpler math for 2D case | ||||
| */ | */ | ||||
| rotate_m4(ModelView, 'Z', DEG2RADF(deg)); | rotate_m4(ModelView, 'Z', DEG2RADF(deg)); | ||||
| Show All 12 Lines | void GPU_matrix_rotate_3fv(float deg, const float axis[3]) | ||||
| GPU_matrix_mul(m); | GPU_matrix_mul(m); | ||||
| } | } | ||||
| void GPU_matrix_rotate_axis(float deg, char axis) | void GPU_matrix_rotate_axis(float deg, char axis) | ||||
| { | { | ||||
| /* rotate_m4 works in place */ | /* rotate_m4 works in place */ | ||||
| rotate_m4(ModelView, axis, DEG2RADF(deg)); | rotate_m4(ModelView, axis, DEG2RADF(deg)); | ||||
| CHECKMAT(ModelView); | CHECKMAT(ModelView); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| static void mat4_ortho_set( | static void mat4_ortho_set( | ||||
| float m[4][4], float left, float right, float bottom, float top, float near, float far) | float m[4][4], float left, float right, float bottom, float top, float near, float far) | ||||
| { | { | ||||
| m[0][0] = 2.0f / (right - left); | m[0][0] = 2.0f / (right - left); | ||||
| m[1][0] = 0.0f; | m[1][0] = 0.0f; | ||||
| m[2][0] = 0.0f; | m[2][0] = 0.0f; | ||||
| Show All 9 Lines | static void mat4_ortho_set( | ||||
| m[2][2] = -2.0f / (far - near); | m[2][2] = -2.0f / (far - near); | ||||
| m[3][2] = -(far + near) / (far - near); | m[3][2] = -(far + near) / (far - near); | ||||
| m[0][3] = 0.0f; | m[0][3] = 0.0f; | ||||
| m[1][3] = 0.0f; | m[1][3] = 0.0f; | ||||
| m[2][3] = 0.0f; | m[2][3] = 0.0f; | ||||
| m[3][3] = 1.0f; | m[3][3] = 1.0f; | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| static void mat4_frustum_set( | static void mat4_frustum_set( | ||||
| float m[4][4], float left, float right, float bottom, float top, float near, float far) | float m[4][4], float left, float right, float bottom, float top, float near, float far) | ||||
| { | { | ||||
| m[0][0] = 2.0f * near / (right - left); | m[0][0] = 2.0f * near / (right - left); | ||||
| m[1][0] = 0.0f; | m[1][0] = 0.0f; | ||||
| m[2][0] = (right + left) / (right - left); | m[2][0] = (right + left) / (right - left); | ||||
| Show All 9 Lines | static void mat4_frustum_set( | ||||
| m[2][2] = -(far + near) / (far - near); | m[2][2] = -(far + near) / (far - near); | ||||
| m[3][2] = -2.0f * far * near / (far - near); | m[3][2] = -2.0f * far * near / (far - near); | ||||
| m[0][3] = 0.0f; | m[0][3] = 0.0f; | ||||
| m[1][3] = 0.0f; | m[1][3] = 0.0f; | ||||
| m[2][3] = -1.0f; | m[2][3] = -1.0f; | ||||
| m[3][3] = 0.0f; | m[3][3] = 0.0f; | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3]) | static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3]) | ||||
| { | { | ||||
| /* This function is loosely based on Mesa implementation. | /* This function is loosely based on Mesa implementation. | ||||
| * | * | ||||
| * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) | * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) | ||||
| * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. | * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. | ||||
| ▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | static void mat4_look_from_origin(float m[4][4], float lookdir[3], float camup[3]) | ||||
| m[2][2] = -lookdir[2]; | m[2][2] = -lookdir[2]; | ||||
| m[3][2] = 0.0f; | m[3][2] = 0.0f; | ||||
| m[0][3] = 0.0f; | m[0][3] = 0.0f; | ||||
| m[1][3] = 0.0f; | m[1][3] = 0.0f; | ||||
| m[2][3] = 0.0f; | m[2][3] = 0.0f; | ||||
| m[3][3] = 1.0f; | m[3][3] = 1.0f; | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far) | void GPU_matrix_ortho_set(float left, float right, float bottom, float top, float near, float far) | ||||
| { | { | ||||
| mat4_ortho_set(Projection, left, right, bottom, top, near, far); | mat4_ortho_set(Projection, left, right, bottom, top, near, far); | ||||
| CHECKMAT(Projection); | CHECKMAT(Projection); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top) | void GPU_matrix_ortho_2d_set(float left, float right, float bottom, float top) | ||||
| { | { | ||||
| Mat4 m; | Mat4 m; | ||||
| mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f); | mat4_ortho_set(m, left, right, bottom, top, -1.0f, 1.0f); | ||||
| CHECKMAT(Projection2D); | CHECKMAT(Projection2D); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_frustum_set( | void GPU_matrix_frustum_set( | ||||
| float left, float right, float bottom, float top, float near, float far) | float left, float right, float bottom, float top, float near, float far) | ||||
| { | { | ||||
| mat4_frustum_set(Projection, left, right, bottom, top, near, far); | mat4_frustum_set(Projection, left, right, bottom, top, near, far); | ||||
| CHECKMAT(Projection); | CHECKMAT(Projection); | ||||
| state.dirty = true; | gpu_matrix_state_active_set_dirty(true); | ||||
| } | } | ||||
| void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far) | void GPU_matrix_perspective_set(float fovy, float aspect, float near, float far) | ||||
| { | { | ||||
| float half_height = tanf(fovy * (float)(M_PI / 360.0)) * near; | float half_height = tanf(fovy * (float)(M_PI / 360.0)) * near; | ||||
| float half_width = half_height * aspect; | float half_width = half_height * aspect; | ||||
| GPU_matrix_frustum_set(-half_width, +half_width, -half_height, +half_height, near, far); | GPU_matrix_frustum_set(-half_width, +half_width, -half_height, +half_height, near, far); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 249 Lines • ▼ Show 20 Lines | #endif | ||||
| if (P_inv) { | if (P_inv) { | ||||
| Mat4 m; | Mat4 m; | ||||
| GPU_matrix_projection_get(m); | GPU_matrix_projection_get(m); | ||||
| invert_m4(m); | invert_m4(m); | ||||
| glUniformMatrix4fv(P_inv->location, 1, GL_FALSE, (const float *)m); | glUniformMatrix4fv(P_inv->location, 1, GL_FALSE, (const float *)m); | ||||
| } | } | ||||
| state.dirty = false; | gpu_matrix_state_active_set_dirty(false); | ||||
| } | } | ||||
| bool GPU_matrix_dirty_get(void) | bool GPU_matrix_dirty_get(void) | ||||
| { | { | ||||
| return state.dirty; | GPUMatrixState *state = gpu_context_active_matrix_state_get(); | ||||
| return state->dirty; | |||||
| } | } | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Python API Helpers | /** \name Python API Helpers | ||||
| * \{ */ | * \{ */ | ||||
| BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mismatch"); | BLI_STATIC_ASSERT(GPU_PY_MATRIX_STACK_LEN + 1 == MATRIX_STACK_DEPTH, "define mismatch"); | ||||
| /* Return int since caller is may subtract. */ | /* Return int since caller is may subtract. */ | ||||
| int GPU_matrix_stack_level_get_model_view(void) | int GPU_matrix_stack_level_get_model_view(void) | ||||
| { | { | ||||
| return (int)state.model_view_stack.top; | GPUMatrixState *state = gpu_context_active_matrix_state_get(); | ||||
| return (int)state->model_view_stack.top; | |||||
| } | } | ||||
| int GPU_matrix_stack_level_get_projection(void) | int GPU_matrix_stack_level_get_projection(void) | ||||
| { | { | ||||
| return (int)state.projection_stack.top; | GPUMatrixState *state = gpu_context_active_matrix_state_get(); | ||||
| return (int)state->projection_stack.top; | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||