src/drawing.c
1#include "alba.h"
2#include "internal.h"
3
4#include <math.h>
5#include <stdio.h>
6#include <stdlib.h>
7
8#define PI 3.14159265358979323846
9#define MAX_ERROR 0.25
10
11void draw_triangles_indexed_draw_call(
12 DrawCall* draw_call,
13 const uint32_t num_vertices,
14 const AlbaVector* vertices,
15 const AlbaAttribute* attributes,
16 const uint32_t num_indices,
17 uint32_t* indices
18)
19{
20 const uint32_t offset = draw_call->new_vertices.length;
21
22 alba_array_extend(&draw_call->new_vertices, num_vertices, vertices);
23 alba_array_extend(&draw_call->new_attributes, num_vertices, attributes);
24
25 if (offset > 0)
26 {
27 // Fix indices
28 for (uint32_t i = 0; i < num_indices; i++)
29 {
30 indices[i] += offset;
31 }
32 }
33 alba_array_extend(&draw_call->new_indices, num_indices, indices);
34
35 draw_call->buffers_dirty = 1;
36}
37
38void alba_draw_triangles_indexed(
39 AlbaWindow* window,
40 const uint32_t num_vertices,
41 const AlbaVector* vertices,
42 const AlbaAttribute* attributes,
43 const uint32_t num_indices,
44 uint32_t* indices
45)
46{
47 draw_triangles_indexed_draw_call(
48 alba_array_last(&window->draw_calls),
49 num_vertices, vertices, attributes,
50 num_indices, indices
51 );
52}
53
54void alba_draw_triangles(
55 AlbaWindow* window,
56 const uint32_t num_vertices,
57 const AlbaVector* vertices,
58 const AlbaAttribute* attributes
59)
60{
61 uint32_t* indices = malloc(num_vertices * sizeof(uint32_t));
62 if (indices == NULL)
63 {
64 printf(
65 "error: unable to allocate %lu bytes for triangle indices\n",
66 num_vertices * sizeof(uint32_t)
67 );
68 return;
69 }
70
71 for (uint32_t i = 0; i < num_vertices; i++)
72 {
73 indices[i] = i;
74 }
75
76 alba_draw_triangles_indexed(window, num_vertices, vertices, attributes, num_vertices, indices);
77
78 free(indices);
79}
80
81void alba_draw_triangle(AlbaWindow* window, const AlbaVector vertices[3], const AlbaColor color)
82{
83 const AlbaAttribute attributes[] = {
84 color, NO_TEXTURE,
85 color, NO_TEXTURE,
86 color, NO_TEXTURE,
87 };
88 // Does not try to consider winding order
89 uint32_t indices[] = {0, 1, 2};
90 alba_draw_triangles_indexed(window, 3, vertices, attributes, 3, indices);
91}
92
93void alba_draw_quad(AlbaWindow* window, const AlbaVector vertices[4], const AlbaColor color)
94{
95 const AlbaAttribute attributes[] = {
96 color, NO_TEXTURE,
97 color, NO_TEXTURE,
98 color, NO_TEXTURE,
99 color, NO_TEXTURE,
100 };
101 // Does not try to consider winding order
102 uint32_t indices[] = {0, 1, 2, 0, 2, 3};
103 alba_draw_triangles_indexed(window, 4, vertices, attributes, 6, indices);
104}
105
106void alba_draw_rect_aa(
107 AlbaWindow* window,
108 const float x0, const float y0, const float x1, const float y1,
109 const AlbaColor color
110)
111{
112 if (x0 > x1)
113 {
114 fprintf(stderr, "error: invalid rect coordinates, x0 > x1: %f > %f\n", x0, x1);
115 exit(1);
116 }
117 if (y0 > y1)
118 {
119 fprintf(stderr, "error: invalid rect coordinates, y0 > y1: %f > %f\n", y0, y1);
120 exit(1);
121 }
122
123 const AlbaVector vertices[] = {
124 x0, y0,
125 x0, y1,
126 x1, y1,
127 x1, y0,
128 };
129 alba_draw_quad(window, vertices, color);
130}
131
132void alba_generate_regular_polygon(
133 AlbaArray* array, const uint32_t num_sides,
134 const float x, const float y, const float r)
135{
136 alba_array_reserve(array, array->length + num_sides);
137
138 const float step_angle = 2 * PI / num_sides;
139 for (uint32_t i = 0; i < num_sides; i++)
140 {
141 alba_array_append(
142 array,
143 &(AlbaVector){
144 x + r * sin(step_angle * i),
145 y - r * cos(step_angle * i)
146 }
147 );
148 }
149}
150
151void alba_generate_circle(AlbaArray* array, const float x, const float y, const float r)
152{
153 const uint32_t num_sides = round(PI / acos(1 - MAX_ERROR / r));
154 alba_generate_regular_polygon(array, num_sides, x, y, r);
155}
156
157void alba_generate_rounded_rect_aa(
158 AlbaArray* array,
159 const float x0, const float y0, const float x1, const float y1,
160 float r
161)
162{
163 // Always a multiple of 4
164 const uint32_t num_sides = round((PI / acos(1 - MAX_ERROR / r)) / 4) * 4;
165 const uint32_t num_sides_per_corner = num_sides / 4;
166
167 const float step_angle = 2 * PI / num_sides;
168
169 if (x1 - x0 < r) r = (x1 - x0) / 2;
170 if (y1 - y0 < r) r = (y1 - y0) / 2;
171
172 alba_array_reserve(array, array->length + num_sides);
173
174 uint32_t i = 0;
175 for (; i < num_sides_per_corner + 1; i++)
176 {
177 alba_array_append(
178 array, &(AlbaVector){
179 x0 + r - r * sin(step_angle * i),
180 y0 + r - r * cos(step_angle * i)
181 });
182 }
183 i -= 1;
184
185 for (; i < 2 * num_sides_per_corner + 1; i++)
186 {
187 alba_array_append(
188 array, &(AlbaVector){
189 x0 + r - r * sin(step_angle * i),
190 y1 - r - r * cos(step_angle * i)
191 });
192 }
193 i -= 1;
194
195 for (; i < 3 * num_sides_per_corner + 1; i++)
196 {
197 alba_array_append(
198 array, &(AlbaVector){
199 x1 - r - r * sin(step_angle * i),
200 y1 - r - r * cos(step_angle * i)
201 });
202 }
203 i -= 1;
204
205 for (; i < 4 * num_sides_per_corner + 1; i++)
206 {
207 alba_array_append(
208 array, &(AlbaVector){
209 x1 - r - r * sin(step_angle * i),
210 y0 + r - r * cos(step_angle * i)
211 });
212 }
213}
214
215void alba_draw_concave_polygon(AlbaWindow* window, const float x, const float y, const AlbaArray* contour,
216 const AlbaColor color)
217{
218 const uint32_t num_sides = contour->length;
219 const uint32_t num_triangles = num_sides - 2;
220
221 AlbaArray attributes = alba_create_array(sizeof(AlbaAttribute), num_sides);
222 for (uint32_t i = 0; i < num_sides; i++)
223 {
224 alba_array_append(&attributes, &(AlbaAttribute){color, NO_TEXTURE});
225 }
226
227 uint32_t* indices = malloc(num_triangles * 3 * sizeof(uint32_t));
228 for (uint32_t i = 0; i < num_triangles; i++)
229 {
230 indices[i * 3] = 0;
231 indices[i * 3 + 1] = i + 1;
232 indices[i * 3 + 2] = i + 2;
233 }
234
235 alba_draw_triangles_indexed(
236 window,
237 num_sides, contour->data, attributes.data,
238 num_triangles * 3, indices
239 );
240
241 free(indices);
242}
243
244void alba_draw_regular_polygon(
245 AlbaWindow* window, const uint32_t num_sides,
246 const float x, const float y, const float r,
247 const AlbaColor color
248)
249{
250 if (num_sides <= 2)
251 {
252 fprintf(stderr, "error: invalid number of sides in call to draw_regular_polygon: %d. "
253 "A polygon must have at least 3 sides\n", num_sides);
254 exit(1);
255 }
256
257 AlbaArray polygon = alba_create_array(sizeof(AlbaVector), 0);
258 alba_generate_regular_polygon(&polygon, num_sides, x, y, r);
259 alba_draw_concave_polygon(window, x, y, &polygon, color);
260 alba_array_release(&polygon);
261}
262
263void alba_draw_circle(AlbaWindow* window, const float x, const float y, const float r, const AlbaColor color)
264{
265 AlbaArray circle = alba_create_array(sizeof(AlbaVector), 0);
266 alba_generate_circle(&circle, x, y, r);
267 alba_draw_concave_polygon(window, x, y, &circle, color);
268 alba_array_release(&circle);
269}
270
271
272void alba_draw_rounded_rect_aa(
273 AlbaWindow* window,
274 const float x0, const float y0, const float x1, const float y1,
275 const float r, const AlbaColor color
276)
277{
278 if (r == 0)
279 {
280 alba_draw_rect_aa(window, x0, y0, x1, y1, color);
281 return;
282 }
283
284 AlbaArray contour = alba_create_array(sizeof(AlbaVector), 0);
285 alba_generate_rounded_rect_aa(&contour, x0, y0, x1, y1, r);
286 alba_draw_concave_polygon(window, (x0 + x1) / 2, (y0 + y1) / 2, &contour, color);
287}