Serenity Operating System
1/*
2 * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
3 * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@serenityos.org>
4 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/Assertions.h>
10#include <LibGL/GLContext.h>
11
12namespace GL {
13
14static constexpr size_t matrix_stack_limit(GLenum matrix_mode)
15{
16 switch (matrix_mode) {
17 case GL_MODELVIEW:
18 return MODELVIEW_MATRIX_STACK_LIMIT;
19 case GL_PROJECTION:
20 return PROJECTION_MATRIX_STACK_LIMIT;
21 case GL_TEXTURE:
22 return TEXTURE_MATRIX_STACK_LIMIT;
23 }
24 VERIFY_NOT_REACHED();
25}
26
27void GLContext::gl_frustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)
28{
29 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_frustum, left, right, bottom, top, near_val, far_val);
30
31 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
32 RETURN_WITH_ERROR_IF(near_val < 0 || far_val < 0, GL_INVALID_VALUE);
33 RETURN_WITH_ERROR_IF(left == right || bottom == top || near_val == far_val, GL_INVALID_VALUE);
34
35 // Let's do some math!
36 auto a = static_cast<float>((right + left) / (right - left));
37 auto b = static_cast<float>((top + bottom) / (top - bottom));
38 auto c = static_cast<float>(-((far_val + near_val) / (far_val - near_val)));
39 auto d = static_cast<float>(-((2 * far_val * near_val) / (far_val - near_val)));
40
41 FloatMatrix4x4 frustum {
42 static_cast<float>(2 * near_val / (right - left)), 0, a, 0,
43 0, static_cast<float>(2 * near_val / (top - bottom)), b, 0,
44 0, 0, c, d,
45 0, 0, -1, 0
46 };
47 update_current_matrix(*m_current_matrix * frustum);
48}
49
50void GLContext::gl_load_identity()
51{
52 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_load_identity);
53 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
54
55 update_current_matrix(FloatMatrix4x4::identity());
56}
57
58void GLContext::gl_load_matrix(FloatMatrix4x4 const& matrix)
59{
60 APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(gl_load_matrix, matrix);
61 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
62
63 update_current_matrix(matrix);
64}
65
66void GLContext::gl_matrix_mode(GLenum mode)
67{
68 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_matrix_mode, mode);
69 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
70 RETURN_WITH_ERROR_IF(mode < GL_MODELVIEW || mode > GL_TEXTURE, GL_INVALID_ENUM);
71
72 m_current_matrix_mode = mode;
73 switch (mode) {
74 case GL_MODELVIEW:
75 m_current_matrix_stack = &m_model_view_matrix_stack;
76 break;
77 case GL_PROJECTION:
78 m_current_matrix_stack = &m_projection_matrix_stack;
79 break;
80 case GL_TEXTURE:
81 m_current_matrix_stack = &m_active_texture_unit->texture_matrix_stack();
82 break;
83 default:
84 VERIFY_NOT_REACHED();
85 }
86 m_current_matrix = &m_current_matrix_stack->last();
87}
88
89void GLContext::gl_mult_matrix(FloatMatrix4x4 const& matrix)
90{
91 APPEND_TO_CALL_LIST_WITH_ARG_AND_RETURN_IF_NEEDED(gl_mult_matrix, matrix);
92 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
93
94 update_current_matrix(*m_current_matrix * matrix);
95}
96
97void GLContext::gl_ortho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)
98{
99 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_ortho, left, right, bottom, top, near_val, far_val);
100
101 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
102 RETURN_WITH_ERROR_IF(left == right || bottom == top || near_val == far_val, GL_INVALID_VALUE);
103
104 auto rl = right - left;
105 auto tb = top - bottom;
106 auto fn = far_val - near_val;
107 auto tx = -(right + left) / rl;
108 auto ty = -(top + bottom) / tb;
109 auto tz = -(far_val + near_val) / fn;
110
111 FloatMatrix4x4 projection {
112 static_cast<float>(2 / rl), 0, 0, static_cast<float>(tx),
113 0, static_cast<float>(2 / tb), 0, static_cast<float>(ty),
114 0, 0, static_cast<float>(-2 / fn), static_cast<float>(tz),
115 0, 0, 0, 1
116 };
117 update_current_matrix(*m_current_matrix * projection);
118}
119
120void GLContext::gl_pop_matrix()
121{
122 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_pop_matrix);
123 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
124 RETURN_WITH_ERROR_IF(m_current_matrix_stack->size() <= 1, GL_STACK_UNDERFLOW);
125
126 m_current_matrix_stack->take_last();
127 m_current_matrix = &m_current_matrix_stack->last();
128}
129
130void GLContext::gl_push_matrix()
131{
132 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_push_matrix);
133 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
134 RETURN_WITH_ERROR_IF(m_current_matrix_stack->size() >= matrix_stack_limit(m_current_matrix_mode), GL_STACK_OVERFLOW);
135
136 m_current_matrix_stack->append(*m_current_matrix);
137 m_current_matrix = &m_current_matrix_stack->last();
138}
139
140void GLContext::gl_rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
141{
142 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_rotate, angle, x, y, z);
143 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
144
145 FloatVector3 axis = { x, y, z };
146 if (axis.length() > 0.f)
147 axis.normalize();
148 auto rotation_mat = Gfx::rotation_matrix(axis, angle * static_cast<float>(M_PI * 2 / 360));
149 update_current_matrix(*m_current_matrix * rotation_mat);
150}
151
152void GLContext::gl_scale(GLfloat x, GLfloat y, GLfloat z)
153{
154 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_scale, x, y, z);
155 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
156
157 auto scale_matrix = Gfx::scale_matrix(FloatVector3 { x, y, z });
158 update_current_matrix(*m_current_matrix * scale_matrix);
159}
160
161void GLContext::gl_translate(GLfloat x, GLfloat y, GLfloat z)
162{
163 APPEND_TO_CALL_LIST_AND_RETURN_IF_NEEDED(gl_translate, x, y, z);
164 RETURN_WITH_ERROR_IF(m_in_draw_state, GL_INVALID_OPERATION);
165
166 auto translation_matrix = Gfx::translation_matrix(FloatVector3 { x, y, z });
167 update_current_matrix(*m_current_matrix * translation_matrix);
168}
169
170}