SoftwareGLContext.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  1. /*
  2. * Copyright (c) 2021, Jesse Buhagiar <jooster669@gmail.com>
  3. * Copyright (c) 2021, Stephan Unverwerth <s.unverwerth@gmx.de>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "SoftwareGLContext.h"
  8. #include "GLStruct.h"
  9. #include <AK/Assertions.h>
  10. #include <AK/Debug.h>
  11. #include <AK/Format.h>
  12. #include <AK/QuickSort.h>
  13. #include <AK/Vector.h>
  14. #include <LibGfx/Vector4.h>
  15. #include <math.h>
  16. using AK::dbgln;
  17. namespace GL {
  18. static constexpr size_t NUM_CLIP_PLANES = 6;
  19. static FloatVector4 clip_planes[] = {
  20. { -1, 0, 0, 1 }, // Left Plane
  21. { 1, 0, 0, 1 }, // Right Plane
  22. { 0, 1, 0, 1 }, // Top Plane
  23. { 0, -1, 0, 1 }, // Bottom plane
  24. { 0, 0, 1, 1 }, // Near Plane
  25. { 0, 0, -1, 1 } // Far Plane
  26. };
  27. static FloatVector4 clip_plane_normals[] = {
  28. { 1, 0, 0, 1 }, // Left Plane
  29. { -1, 0, 0, 1 }, // Right Plane
  30. { 0, -1, 0, 1 }, // Top Plane
  31. { 0, 1, 0, 1 }, // Bottom plane
  32. { 0, 0, -1, 1 }, // Near Plane
  33. { 0, 0, 1, 1 } // Far Plane
  34. };
  35. enum ClippingPlane {
  36. LEFT = 0,
  37. RIGHT = 1,
  38. TOP = 2,
  39. BOTTOM = 3,
  40. NEAR = 4,
  41. FAR = 5
  42. };
  43. // FIXME: We should set this up when we create the context!
  44. static constexpr size_t MATRIX_STACK_LIMIT = 1024;
  45. // FIXME: Change this to accept a vertex!
  46. // Determines whether or not a vertex is inside the frustum for a given plane
  47. static bool vert_inside_plane(const FloatVector4& vec, ClippingPlane plane)
  48. {
  49. switch (plane) {
  50. case ClippingPlane::LEFT:
  51. return vec.x() > -vec.w();
  52. case ClippingPlane::RIGHT:
  53. return vec.x() < vec.w();
  54. case ClippingPlane::TOP:
  55. return vec.y() < vec.w();
  56. case ClippingPlane::BOTTOM:
  57. return vec.y() > -vec.w();
  58. case ClippingPlane::NEAR:
  59. return vec.z() > -vec.w();
  60. case ClippingPlane::FAR:
  61. return vec.z() < vec.w();
  62. }
  63. return false;
  64. }
  65. // FIXME: This needs to interpolate color/UV data as well!
  66. static FloatVector4 clip_intersection_point(const FloatVector4& vec, const FloatVector4& prev_vec, ClippingPlane plane_index)
  67. {
  68. // https://github.com/fogleman/fauxgl/blob/master/clipping.go#L20
  69. FloatVector4 u, w;
  70. FloatVector4 ret = prev_vec;
  71. FloatVector4 plane = clip_planes[plane_index];
  72. FloatVector4 plane_normal = clip_plane_normals[plane_index];
  73. u = vec;
  74. u -= prev_vec;
  75. w = prev_vec;
  76. w -= plane;
  77. float d = plane_normal.dot(u);
  78. float n = -plane_normal.dot(w);
  79. ret += (u * (n / d));
  80. return ret;
  81. }
  82. // https://groups.csail.mit.edu/graphics/classes/6.837/F04/lectures/07_Pipeline_II.pdf
  83. // This is a really rough implementation of the Sutherland-Hodgman algorithm in clip-space
  84. static void clip_triangle_against_frustum(Vector<FloatVector4>& in_vec)
  85. {
  86. Vector<FloatVector4> clipped_polygon = in_vec; // in_vec = subjectPolygon, clipped_polygon = outputList
  87. for (size_t i = 0; i < NUM_CLIP_PLANES; i++) // Test against each clip plane
  88. {
  89. ClippingPlane plane = static_cast<ClippingPlane>(i); // Hahaha, what the fuck
  90. in_vec = clipped_polygon;
  91. clipped_polygon.clear();
  92. // Prevent a crash from .at() undeflow
  93. if (in_vec.size() == 0)
  94. return;
  95. FloatVector4 prev_vec = in_vec.at(in_vec.size() - 1);
  96. for (size_t j = 0; j < in_vec.size(); j++) // Perform this for each vertex
  97. {
  98. const FloatVector4& vec = in_vec.at(j);
  99. if (vert_inside_plane(vec, plane)) {
  100. if (!vert_inside_plane(prev_vec, plane)) {
  101. FloatVector4 intersect = clip_intersection_point(prev_vec, vec, plane);
  102. clipped_polygon.append(intersect);
  103. }
  104. clipped_polygon.append(vec);
  105. } else if (vert_inside_plane(prev_vec, plane)) {
  106. FloatVector4 intersect = clip_intersection_point(prev_vec, vec, plane);
  107. clipped_polygon.append(intersect);
  108. }
  109. prev_vec = vec;
  110. }
  111. }
  112. }
  113. void SoftwareGLContext::gl_begin(GLenum mode)
  114. {
  115. if (m_in_draw_state) {
  116. m_error = GL_INVALID_OPERATION;
  117. return;
  118. }
  119. if (mode < GL_TRIANGLES || mode > GL_POLYGON) {
  120. m_error = GL_INVALID_ENUM;
  121. return;
  122. }
  123. m_current_draw_mode = mode;
  124. m_in_draw_state = true; // Certain commands will now generate an error
  125. m_error = GL_NO_ERROR;
  126. }
  127. void SoftwareGLContext::gl_clear(GLbitfield mask)
  128. {
  129. if (m_in_draw_state) {
  130. m_error = GL_INVALID_OPERATION;
  131. return;
  132. }
  133. if (mask & GL_COLOR_BUFFER_BIT) {
  134. uint8_t r = static_cast<uint8_t>(floor(m_clear_color.x() * 255.0f));
  135. uint8_t g = static_cast<uint8_t>(floor(m_clear_color.y() * 255.0f));
  136. uint8_t b = static_cast<uint8_t>(floor(m_clear_color.z() * 255.0f));
  137. uint64_t color = r << 16 | g << 8 | b;
  138. (void)(color);
  139. m_error = GL_NO_ERROR;
  140. } else {
  141. m_error = GL_INVALID_ENUM;
  142. }
  143. }
  144. void SoftwareGLContext::gl_clear_color(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
  145. {
  146. if (m_in_draw_state) {
  147. m_error = GL_INVALID_OPERATION;
  148. return;
  149. }
  150. m_clear_color = { red, green, blue, alpha };
  151. m_error = GL_NO_ERROR;
  152. }
  153. void SoftwareGLContext::gl_color(GLdouble r, GLdouble g, GLdouble b, GLdouble a)
  154. {
  155. m_current_vertex_color = { (float)r, (float)g, (float)b, (float)a };
  156. m_error = GL_NO_ERROR;
  157. }
  158. void SoftwareGLContext::gl_end()
  159. {
  160. // At this point, the user has effectively specified that they are done with defining the geometry
  161. // of what they want to draw. We now need to do a few things (https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview):
  162. //
  163. // 1. Transform all of the vertices in the current vertex list into eye space by mulitplying the model-view matrix
  164. // 2. Transform all of the vertices from eye space into clip space by multiplying by the projection matrix
  165. // 3. If culling is enabled, we cull the desired faces (https://learnopengl.com/Advanced-OpenGL/Face-culling)
  166. // 4. Each element of the vertex is then divided by w to bring the positions into NDC (Normalized Device Coordinates)
  167. // 5. The vertices are sorted (for the rasteriser, how are we doing this? 3Dfx did this top to bottom in terms of vertex y co-ordinates)
  168. // 6. The vertices are then sent off to the rasteriser and drawn to the screen
  169. // FIXME: Don't assume screen dimensions
  170. float scr_width = 640.0f;
  171. float scr_height = 480.0f;
  172. // Make sure we had a `glBegin` before this call...
  173. if (!m_in_draw_state) {
  174. m_error = GL_INVALID_OPERATION;
  175. return;
  176. }
  177. // Let's construct some triangles
  178. if (m_current_draw_mode == GL_TRIANGLES) {
  179. GLTriangle triangle;
  180. for (size_t i = 0; i < vertex_list.size(); i += 3) {
  181. triangle.vertices[0] = vertex_list.at(i);
  182. triangle.vertices[1] = vertex_list.at(i + 1);
  183. triangle.vertices[2] = vertex_list.at(i + 2);
  184. triangle_list.append(triangle);
  185. }
  186. } else if (m_current_draw_mode == GL_QUADS) {
  187. // We need to construct two triangles to form the quad
  188. GLTriangle triangle;
  189. VERIFY(vertex_list.size() % 4 == 0);
  190. for (size_t i = 0; i < vertex_list.size(); i += 4) {
  191. // Triangle 1
  192. triangle.vertices[0] = vertex_list.at(i);
  193. triangle.vertices[1] = vertex_list.at(i + 1);
  194. triangle.vertices[2] = vertex_list.at(i + 2);
  195. triangle_list.append(triangle);
  196. // Triangle 2
  197. triangle.vertices[0] = vertex_list.at(i + 2);
  198. triangle.vertices[1] = vertex_list.at(i + 3);
  199. triangle.vertices[2] = vertex_list.at(i);
  200. triangle_list.append(triangle);
  201. }
  202. } else if (m_current_draw_mode == GL_TRIANGLE_FAN) {
  203. GLTriangle triangle;
  204. triangle.vertices[0] = vertex_list.at(0); // Root vertex is always the vertex defined first
  205. for (size_t i = 1; i < vertex_list.size() - 1; i++) // This is technically `n-2` triangles. We start at index 1
  206. {
  207. triangle.vertices[1] = vertex_list.at(i);
  208. triangle.vertices[2] = vertex_list.at(i + 1);
  209. triangle_list.append(triangle);
  210. }
  211. } else if (m_current_draw_mode == GL_TRIANGLE_STRIP) {
  212. GLTriangle triangle;
  213. for (size_t i = 0; i < vertex_list.size() - 2; i++) {
  214. triangle.vertices[0] = vertex_list.at(i);
  215. triangle.vertices[1] = vertex_list.at(i + 1);
  216. triangle.vertices[2] = vertex_list.at(i + 2);
  217. triangle_list.append(triangle);
  218. }
  219. } else {
  220. m_error = GL_INVALID_ENUM;
  221. return;
  222. }
  223. // Now let's transform each triangle and send that to the GPU
  224. for (size_t i = 0; i < triangle_list.size(); i++) {
  225. GLTriangle& triangle = triangle_list.at(i);
  226. GLVertex& vertexa = triangle.vertices[0];
  227. GLVertex& vertexb = triangle.vertices[1];
  228. GLVertex& vertexc = triangle.vertices[2];
  229. FloatVector4 veca({ vertexa.x, vertexa.y, vertexa.z, 1.0f });
  230. FloatVector4 vecb({ vertexb.x, vertexb.y, vertexb.z, 1.0f });
  231. FloatVector4 vecc({ vertexc.x, vertexc.y, vertexc.z, 1.0f });
  232. // First multiply the vertex by the MODELVIEW matrix and then the PROJECTION matrix
  233. veca = m_model_view_matrix * veca;
  234. veca = m_projection_matrix * veca;
  235. vecb = m_model_view_matrix * vecb;
  236. vecb = m_projection_matrix * vecb;
  237. vecc = m_model_view_matrix * vecc;
  238. vecc = m_projection_matrix * vecc;
  239. // At this point, we're in clip space
  240. // Here's where we do the clipping. This is a really crude implementation of the
  241. // https://learnopengl.com/Getting-started/Coordinate-Systems
  242. // "Note that if only a part of a primitive e.g. a triangle is outside the clipping volume OpenGL
  243. // will reconstruct the triangle as one or more triangles to fit inside the clipping range. "
  244. //
  245. // ALL VERTICES ARE DEFINED IN A CLOCKWISE ORDER
  246. // Okay, let's do some face culling first
  247. Vector<FloatVector4> vecs;
  248. Vector<GLVertex> verts;
  249. vecs.append(veca);
  250. vecs.append(vecb);
  251. vecs.append(vecc);
  252. clip_triangle_against_frustum(vecs);
  253. // TODO: Copy color and UV information too!
  254. for (size_t vec_idx = 0; vec_idx < vecs.size(); vec_idx++) {
  255. FloatVector4& vec = vecs.at(vec_idx);
  256. GLVertex vertex;
  257. // Perform the perspective divide
  258. if (vec.w() != 0.0f) {
  259. vec.set_x(vec.x() / vec.w());
  260. vec.set_y(vec.y() / vec.w());
  261. vec.set_z(vec.z() / vec.w());
  262. }
  263. vertex.x = vec.x();
  264. vertex.y = vec.y();
  265. vertex.z = vec.z();
  266. vertex.w = vec.w();
  267. // FIXME: This is to suppress any -Wunused errors
  268. vertex.u = 0.0f;
  269. vertex.v = 0.0f;
  270. if (vec_idx == 0) {
  271. vertex.r = vertexa.r;
  272. vertex.g = vertexa.g;
  273. vertex.b = vertexa.b;
  274. vertex.a = vertexa.a;
  275. } else if (vec_idx == 1) {
  276. vertex.r = vertexb.r;
  277. vertex.g = vertexb.g;
  278. vertex.b = vertexb.b;
  279. vertex.a = vertexb.a;
  280. } else {
  281. vertex.r = vertexc.r;
  282. vertex.g = vertexc.g;
  283. vertex.b = vertexc.b;
  284. vertex.a = vertexc.a;
  285. }
  286. vertex.x = (vec.x() + 1.0f) * (scr_width / 2.0f) + 0.0f; // TODO: 0.0f should be something!?
  287. vertex.y = scr_height - ((vec.y() + 1.0f) * (scr_height / 2.0f) + 0.0f);
  288. vertex.z = vec.z();
  289. verts.append(vertex);
  290. }
  291. if (verts.size() == 0) {
  292. continue;
  293. } else if (verts.size() == 3) {
  294. GLTriangle tri;
  295. tri.vertices[0] = verts.at(0);
  296. tri.vertices[1] = verts.at(1);
  297. tri.vertices[2] = verts.at(2);
  298. processed_triangles.append(tri);
  299. } else if (verts.size() == 4) {
  300. GLTriangle tri1;
  301. GLTriangle tri2;
  302. tri1.vertices[0] = verts.at(0);
  303. tri1.vertices[1] = verts.at(1);
  304. tri1.vertices[2] = verts.at(2);
  305. processed_triangles.append(tri1);
  306. tri2.vertices[0] = verts.at(0);
  307. tri2.vertices[1] = verts.at(2);
  308. tri2.vertices[2] = verts.at(3);
  309. processed_triangles.append(tri2);
  310. }
  311. }
  312. for (size_t i = 0; i < processed_triangles.size(); i++) {
  313. Vector<GLVertex> sort_vert_list;
  314. GLTriangle& triangle = processed_triangles.at(i);
  315. // Now we sort the vertices by their y values. A is the vertex that has the least y value,
  316. // B is the middle and C is the bottom.
  317. // These are sorted in groups of 3
  318. sort_vert_list.append(triangle.vertices[0]);
  319. sort_vert_list.append(triangle.vertices[1]);
  320. sort_vert_list.append(triangle.vertices[2]);
  321. AK::quick_sort(sort_vert_list.begin(), sort_vert_list.end(), [](auto& a, auto& b) { return a.y < b.y; });
  322. triangle.vertices[0] = sort_vert_list.at(0);
  323. triangle.vertices[1] = sort_vert_list.at(1);
  324. triangle.vertices[2] = sort_vert_list.at(2);
  325. // Let's calculate the (signed) area of the triangle
  326. // https://cp-algorithms.com/geometry/oriented-triangle-area.html
  327. float dxAB = triangle.vertices[0].x - triangle.vertices[1].x; // A.x - B.x
  328. float dxBC = triangle.vertices[1].x - triangle.vertices[2].x; // B.X - C.x
  329. float dyAB = triangle.vertices[0].y - triangle.vertices[1].y;
  330. float dyBC = triangle.vertices[1].y - triangle.vertices[2].y;
  331. float area = (dxAB * dyBC) - (dxBC * dyAB);
  332. if (area == 0.0f)
  333. continue;
  334. int32_t vertexAx = triangle.vertices[0].x;
  335. int32_t vertexAy = triangle.vertices[0].y;
  336. int32_t vertexBx = triangle.vertices[1].x;
  337. int32_t vertexBy = triangle.vertices[1].y;
  338. int32_t vertexCx = triangle.vertices[2].x;
  339. int32_t vertexCy = triangle.vertices[2].y;
  340. (void)(vertexAx);
  341. (void)(vertexAy);
  342. (void)(vertexBx);
  343. (void)(vertexBy);
  344. (void)(vertexCx);
  345. (void)(vertexCy);
  346. if (m_cull_faces) {
  347. bool is_front = (m_front_face == GL_CCW ? area > 0 : area < 0);
  348. if (is_front && (m_culled_sides == GL_FRONT || m_culled_sides == GL_FRONT_AND_BACK))
  349. continue;
  350. if (!is_front && (m_culled_sides == GL_BACK || m_culled_sides == GL_FRONT_AND_BACK))
  351. continue;
  352. }
  353. }
  354. triangle_list.clear();
  355. processed_triangles.clear();
  356. vertex_list.clear();
  357. m_in_draw_state = false;
  358. m_error = GL_NO_ERROR;
  359. }
  360. void SoftwareGLContext::gl_frustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)
  361. {
  362. if (m_in_draw_state) {
  363. m_error = GL_INVALID_OPERATION;
  364. return;
  365. }
  366. // Let's do some math!
  367. // FIXME: Are we losing too much precision by doing this?
  368. float a = static_cast<float>((right + left) / (right - left));
  369. float b = static_cast<float>((top + bottom) / (top - bottom));
  370. float c = static_cast<float>(-((far_val + near_val) / (far_val - near_val)));
  371. float d = static_cast<float>(-((2 * (far_val * near_val)) / (far_val - near_val)));
  372. FloatMatrix4x4 frustum {
  373. ((2 * (float)near_val) / ((float)right - (float)left)), 0, a, 0,
  374. 0, ((2 * (float)near_val) / ((float)top - (float)bottom)), b, 0,
  375. 0, 0, c, d,
  376. 0, 0, -1, 0
  377. };
  378. if (m_current_matrix_mode == GL_PROJECTION) {
  379. m_projection_matrix = m_projection_matrix * frustum;
  380. } else if (m_current_matrix_mode == GL_MODELVIEW) {
  381. dbgln_if(GL_DEBUG, "glFrustum(): frustum created with curr_matrix_mode == GL_MODELVIEW!!!");
  382. m_projection_matrix = m_model_view_matrix * frustum;
  383. }
  384. m_error = GL_NO_ERROR;
  385. }
  386. void SoftwareGLContext::gl_ortho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val)
  387. {
  388. if (m_in_draw_state) {
  389. m_error = GL_INVALID_OPERATION;
  390. return;
  391. }
  392. if (left == right || bottom == top || near_val == far_val) {
  393. m_error = GL_INVALID_VALUE;
  394. return;
  395. }
  396. auto rl = right - left;
  397. auto tb = top - bottom;
  398. auto fn = far_val - near_val;
  399. auto tx = -(right + left) / rl;
  400. auto ty = -(top + bottom) / tb;
  401. auto tz = -(far_val + near_val) / fn;
  402. FloatMatrix4x4 projection {
  403. static_cast<float>(2 / rl), 0, 0, static_cast<float>(tx),
  404. 0, static_cast<float>(2 / tb), 0, static_cast<float>(ty),
  405. 0, 0, static_cast<float>(-2 / fn), static_cast<float>(tz),
  406. 0, 0, 0, 1
  407. };
  408. if (m_current_matrix_mode == GL_PROJECTION) {
  409. m_projection_matrix = m_projection_matrix * projection;
  410. } else if (m_current_matrix_mode == GL_MODELVIEW) {
  411. m_projection_matrix = m_model_view_matrix * projection;
  412. }
  413. m_error = GL_NO_ERROR;
  414. }
  415. GLenum SoftwareGLContext::gl_get_error()
  416. {
  417. if (m_in_draw_state) {
  418. return GL_INVALID_OPERATION;
  419. }
  420. return m_error;
  421. }
  422. GLubyte* SoftwareGLContext::gl_get_string(GLenum name)
  423. {
  424. if (m_in_draw_state) {
  425. m_error = GL_INVALID_OPERATION;
  426. return nullptr;
  427. }
  428. switch (name) {
  429. case GL_VENDOR:
  430. return reinterpret_cast<GLubyte*>(const_cast<char*>("The SerenityOS Developers"));
  431. case GL_RENDERER:
  432. return reinterpret_cast<GLubyte*>(const_cast<char*>("SerenityOS OpenGL"));
  433. case GL_VERSION:
  434. return reinterpret_cast<GLubyte*>(const_cast<char*>("OpenGL 1.2 SerenityOS"));
  435. default:
  436. dbgln_if(GL_DEBUG, "glGetString(): Unknown enum name!");
  437. break;
  438. }
  439. m_error = GL_INVALID_ENUM;
  440. return nullptr;
  441. }
  442. void SoftwareGLContext::gl_load_identity()
  443. {
  444. if (m_in_draw_state) {
  445. m_error = GL_INVALID_OPERATION;
  446. return;
  447. }
  448. if (m_current_matrix_mode == GL_PROJECTION)
  449. m_projection_matrix = FloatMatrix4x4::identity();
  450. else if (m_current_matrix_mode == GL_MODELVIEW)
  451. m_model_view_matrix = FloatMatrix4x4::identity();
  452. else
  453. VERIFY_NOT_REACHED();
  454. m_error = GL_NO_ERROR;
  455. }
  456. void SoftwareGLContext::gl_load_matrix(const FloatMatrix4x4& matrix)
  457. {
  458. if (m_in_draw_state) {
  459. m_error = GL_INVALID_OPERATION;
  460. return;
  461. }
  462. if (m_current_matrix_mode == GL_PROJECTION)
  463. m_projection_matrix = matrix;
  464. else if (m_current_matrix_mode == GL_MODELVIEW)
  465. m_model_view_matrix = matrix;
  466. else
  467. VERIFY_NOT_REACHED();
  468. m_error = GL_NO_ERROR;
  469. }
  470. void SoftwareGLContext::gl_matrix_mode(GLenum mode)
  471. {
  472. if (m_in_draw_state) {
  473. m_error = GL_INVALID_OPERATION;
  474. return;
  475. }
  476. if (mode < GL_MODELVIEW || mode > GL_PROJECTION) {
  477. m_error = GL_INVALID_ENUM;
  478. return;
  479. }
  480. m_current_matrix_mode = mode;
  481. m_error = GL_NO_ERROR;
  482. }
  483. void SoftwareGLContext::gl_push_matrix()
  484. {
  485. if (m_in_draw_state) {
  486. m_error = GL_INVALID_OPERATION;
  487. return;
  488. }
  489. dbgln_if(GL_DEBUG, "glPushMatrix(): Pushing matrix to the matrix stack (matrix_mode {})", m_current_matrix_mode);
  490. switch (m_current_matrix_mode) {
  491. case GL_PROJECTION:
  492. if (m_projection_matrix_stack.size() >= MATRIX_STACK_LIMIT) {
  493. m_error = GL_STACK_OVERFLOW;
  494. return;
  495. }
  496. m_projection_matrix_stack.append(m_projection_matrix);
  497. break;
  498. case GL_MODELVIEW:
  499. if (m_model_view_matrix_stack.size() >= MATRIX_STACK_LIMIT) {
  500. m_error = GL_STACK_OVERFLOW;
  501. return;
  502. }
  503. m_model_view_matrix_stack.append(m_model_view_matrix);
  504. break;
  505. default:
  506. dbgln_if(GL_DEBUG, "glPushMatrix(): Attempt to push matrix with invalid matrix mode {})", m_current_matrix_mode);
  507. return;
  508. }
  509. m_error = GL_NO_ERROR;
  510. }
  511. void SoftwareGLContext::gl_pop_matrix()
  512. {
  513. if (m_in_draw_state) {
  514. m_error = GL_INVALID_OPERATION;
  515. return;
  516. }
  517. dbgln_if(GL_DEBUG, "glPopMatrix(): Popping matrix from matrix stack (matrix_mode = {})", m_current_matrix_mode);
  518. // FIXME: Make sure stack::top() doesn't cause any nasty issues if it's empty (that could result in a lockup/hang)
  519. switch (m_current_matrix_mode) {
  520. case GL_PROJECTION:
  521. if (m_projection_matrix_stack.size() == 0) {
  522. m_error = GL_STACK_UNDERFLOW;
  523. return;
  524. }
  525. m_projection_matrix = m_projection_matrix_stack.take_last();
  526. break;
  527. case GL_MODELVIEW:
  528. if (m_model_view_matrix_stack.size() == 0) {
  529. m_error = GL_STACK_UNDERFLOW;
  530. return;
  531. }
  532. m_model_view_matrix = m_model_view_matrix_stack.take_last();
  533. break;
  534. default:
  535. dbgln_if(GL_DEBUG, "glPopMatrix(): Attempt to pop matrix with invalid matrix mode, {}", m_current_matrix_mode);
  536. return;
  537. }
  538. m_error = GL_NO_ERROR;
  539. }
  540. void SoftwareGLContext::gl_rotate(GLdouble angle, GLdouble x, GLdouble y, GLdouble z)
  541. {
  542. if (m_in_draw_state) {
  543. m_error = GL_INVALID_OPERATION;
  544. return;
  545. }
  546. FloatVector3 axis = { (float)x, (float)y, (float)z };
  547. axis.normalize();
  548. auto rotation_mat = FloatMatrix4x4::rotate(axis, angle);
  549. if (m_current_matrix_mode == GL_MODELVIEW)
  550. m_model_view_matrix = m_model_view_matrix * rotation_mat;
  551. else if (m_current_matrix_mode == GL_PROJECTION)
  552. m_projection_matrix = m_projection_matrix * rotation_mat;
  553. m_error = GL_NO_ERROR;
  554. }
  555. void SoftwareGLContext::gl_scale(GLdouble x, GLdouble y, GLdouble z)
  556. {
  557. if (m_in_draw_state) {
  558. m_error = GL_INVALID_OPERATION;
  559. return;
  560. }
  561. if (m_current_matrix_mode == GL_MODELVIEW) {
  562. m_model_view_matrix = m_model_view_matrix * FloatMatrix4x4::scale({ static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) });
  563. } else if (m_current_matrix_mode == GL_PROJECTION) {
  564. m_projection_matrix = m_projection_matrix * FloatMatrix4x4::scale({ static_cast<float>(x), static_cast<float>(y), static_cast<float>(z) });
  565. }
  566. m_error = GL_NO_ERROR;
  567. }
  568. void SoftwareGLContext::gl_translate(GLdouble x, GLdouble y, GLdouble z)
  569. {
  570. if (m_in_draw_state) {
  571. m_error = GL_INVALID_OPERATION;
  572. return;
  573. }
  574. if (m_current_matrix_mode == GL_MODELVIEW) {
  575. m_model_view_matrix = m_model_view_matrix * FloatMatrix4x4::translate({ (float)x, (float)y, (float)z });
  576. } else if (m_current_matrix_mode == GL_PROJECTION) {
  577. m_projection_matrix = m_projection_matrix * FloatMatrix4x4::translate({ (float)x, (float)y, (float)z });
  578. }
  579. m_error = GL_NO_ERROR;
  580. }
  581. void SoftwareGLContext::gl_vertex(GLdouble x, GLdouble y, GLdouble z, GLdouble w)
  582. {
  583. GLVertex vertex;
  584. vertex.x = x;
  585. vertex.y = y;
  586. vertex.z = z;
  587. vertex.w = w;
  588. vertex.r = m_current_vertex_color.x();
  589. vertex.g = m_current_vertex_color.y();
  590. vertex.b = m_current_vertex_color.z();
  591. vertex.a = m_current_vertex_color.w();
  592. // FIXME: This is to suppress any -Wunused errors
  593. vertex.w = 0.0f;
  594. vertex.u = 0.0f;
  595. vertex.v = 0.0f;
  596. vertex_list.append(vertex);
  597. m_error = GL_NO_ERROR;
  598. }
  599. void SoftwareGLContext::gl_viewport(GLint x, GLint y, GLsizei width, GLsizei height)
  600. {
  601. if (m_in_draw_state) {
  602. m_error = GL_INVALID_OPERATION;
  603. return;
  604. }
  605. (void)(x);
  606. (void)(y);
  607. (void)(width);
  608. (void)(height);
  609. m_error = GL_NO_ERROR;
  610. }
  611. void SoftwareGLContext::gl_enable(GLenum capability)
  612. {
  613. if (m_in_draw_state) {
  614. m_error = GL_INVALID_OPERATION;
  615. return;
  616. }
  617. switch (capability) {
  618. case GL_CULL_FACE:
  619. m_cull_faces = true;
  620. break;
  621. default:
  622. m_error = GL_INVALID_ENUM;
  623. break;
  624. }
  625. }
  626. void SoftwareGLContext::gl_disable(GLenum capability)
  627. {
  628. if (m_in_draw_state) {
  629. m_error = GL_INVALID_OPERATION;
  630. return;
  631. }
  632. switch (capability) {
  633. case GL_CULL_FACE:
  634. m_cull_faces = false;
  635. break;
  636. default:
  637. m_error = GL_INVALID_ENUM;
  638. break;
  639. }
  640. }
  641. void SoftwareGLContext::gl_front_face(GLenum face)
  642. {
  643. if (face < GL_CW || face > GL_CCW) {
  644. m_error = GL_INVALID_ENUM;
  645. return;
  646. }
  647. m_front_face = face;
  648. }
  649. void SoftwareGLContext::gl_cull_face(GLenum cull_mode)
  650. {
  651. if (cull_mode < GL_FRONT || cull_mode > GL_FRONT_AND_BACK) {
  652. m_error = GL_INVALID_ENUM;
  653. return;
  654. }
  655. m_culled_sides = cull_mode;
  656. }
  657. }