196 node->destroy_func = asc_rectangle_destroy; |
196 node->destroy_func = asc_rectangle_destroy; |
197 node->draw_func = asc_rectangle_draw; |
197 node->draw_func = asc_rectangle_draw; |
198 asc_node_update(node); |
198 asc_node_update(node); |
199 return node; |
199 return node; |
200 } |
200 } |
|
201 |
|
202 static void asc_ellipsis_destroy(AscSceneNode *node) { |
|
203 asc_ptr_cast(AscEllipsis, ellipsis, node); |
|
204 asc_mesh_destroy(&ellipsis->mesh); |
|
205 } |
|
206 |
|
207 static void asc_ellipsis_update(AscSceneNode *node) { |
|
208 asc_ptr_cast(AscEllipsis, ellipsis, node); |
|
209 asc_vec2f size = asc_vec2f_scale(ellipsis->radii, 2); |
|
210 asc_mesh_plane_2d(&ellipsis->mesh, .size = size, .uv_scale = size); |
|
211 } |
|
212 |
|
213 |
|
214 typedef struct asc_ellipsis_shader_s { |
|
215 AscShaderProgram program; |
|
216 asc_uniform_loc color; |
|
217 asc_uniform_loc border_color; |
|
218 asc_uniform_loc radii; |
|
219 asc_uniform_loc thickness; |
|
220 } AscEllipsisShader; |
|
221 |
|
222 #define ASC_ELLIPSIS_SHADER_FLAG_FILL 1 |
|
223 #define ASC_ELLIPSIS_SHADER_FLAG_BORDER 2 |
|
224 |
|
225 static AscShaderProgram *asc_ellipsis_shader_create(int flags) { |
|
226 AscShaderCodes codes; |
|
227 const char * const defines[] = { |
|
228 "#define FILL\n", |
|
229 "#define BORDER\n" |
|
230 }; |
|
231 if (asc_shader_load_code_files((AscShaderCodeInfo){ |
|
232 .files.vtx = "sprite_vtx.glsl", |
|
233 .files.frag = "ellipsis_frag.glsl", |
|
234 .defines.frag_list = defines, |
|
235 .defines.frag_list_select = flags |
|
236 }, &codes)) { |
|
237 asc_error("Loading ellipsis shader failed."); |
|
238 return NULL; |
|
239 } |
|
240 AscShaderProgram *shader = asc_shader_create(codes, sizeof(AscRectangleShader)); |
|
241 if (asc_shader_invalid(shader)) { |
|
242 asc_shader_free_codes(codes); |
|
243 return shader; |
|
244 } |
|
245 asc_ptr_cast(AscEllipsisShader, ellipsis_shader, shader); |
|
246 ellipsis_shader->radii = asc_shader_get_uniform_loc(shader, "radii"); |
|
247 if (asc_test_flag(flags, ASC_ELLIPSIS_SHADER_FLAG_FILL)) { |
|
248 ellipsis_shader->color = asc_shader_get_uniform_loc(shader, "color"); |
|
249 } else { |
|
250 ellipsis_shader->color = -1; |
|
251 } |
|
252 if (asc_test_flag(flags, ASC_ELLIPSIS_SHADER_FLAG_BORDER)) { |
|
253 ellipsis_shader->thickness = asc_shader_get_uniform_loc(shader, "thickness"); |
|
254 ellipsis_shader->border_color = asc_shader_get_uniform_loc(shader, "border_color"); |
|
255 } else { |
|
256 ellipsis_shader->thickness = -1; |
|
257 ellipsis_shader->border_color = -1; |
|
258 } |
|
259 asc_shader_free_codes(codes); |
|
260 |
|
261 asc_error_catch_all_gl(); |
|
262 |
|
263 return shader; |
|
264 } |
|
265 |
|
266 static void asc_ellipsis_draw(const AscCamera *camera, const AscSceneNode *node) { |
|
267 asc_cptr_cast(AscEllipsis, ellipsis, node); |
|
268 |
|
269 const bool filled = ellipsis->filled; |
|
270 const bool border = ellipsis->thickness > 0; |
|
271 |
|
272 // Compute shader flags |
|
273 int shader_flags = 0; |
|
274 if (filled) shader_flags |= ASC_ELLIPSIS_SHADER_FLAG_FILL; |
|
275 if (border) shader_flags |= ASC_ELLIPSIS_SHADER_FLAG_BORDER; |
|
276 |
|
277 // Lookup table for the shader ID |
|
278 const int shader_ids[] = { |
|
279 -1, // unused |
|
280 ASC_SHADER_ELLIPSIS_FILL, |
|
281 ASC_SHADER_ELLIPSIS_DRAW, |
|
282 ASC_SHADER_ELLIPSIS_FILL_BORDER, |
|
283 }; |
|
284 |
|
285 // Look up and activate shader |
|
286 const AscShaderProgram *shader = asc_shader_lookup_or_create( |
|
287 shader_ids[shader_flags], asc_ellipsis_shader_create, shader_flags); |
|
288 if (asc_shader_use(shader, camera)) return; |
|
289 asc_cptr_cast(AscEllipsisShader, ellipsis_shader, shader); |
|
290 |
|
291 // Upload uniforms |
|
292 asc_shader_upload_model_matrix(shader, node); |
|
293 |
|
294 if (filled) { |
|
295 asc_shader_upload_col4f(ellipsis_shader->color, ellipsis->color); |
|
296 } |
|
297 asc_shader_upload_vec2f(ellipsis_shader->radii, ellipsis->radii); |
|
298 |
|
299 if (border) { |
|
300 asc_shader_upload_float(ellipsis_shader->thickness, ellipsis->thickness); |
|
301 asc_shader_upload_col4f(ellipsis_shader->border_color, ellipsis->border_color); |
|
302 } |
|
303 |
|
304 // Draw mesh |
|
305 asc_mesh_draw_triangle_strip(&ellipsis->mesh); |
|
306 } |
|
307 |
|
308 AscSceneNode *asc_ellipsis_create(struct asc_ellipsis_create_args args) { |
|
309 AscEllipsis *ellipsis = cxZallocDefault(sizeof(AscEllipsis)); |
|
310 |
|
311 if (args.bounds.size.width + args.bounds.size.height > 0) { |
|
312 ellipsis->node.position.x = (float) args.bounds.pos.x; |
|
313 ellipsis->node.position.y = (float) args.bounds.pos.y; |
|
314 ellipsis->radii.x = (float) args.bounds.size.width / 2.f; |
|
315 ellipsis->radii.y = (float) args.bounds.size.height / 2.f; |
|
316 } else { |
|
317 const unsigned cx = ASC_NONZERO_OR(args.x, args.center.x); |
|
318 const unsigned cy = ASC_NONZERO_OR(args.y, args.center.y); |
|
319 const unsigned rx = ASC_NONZERO_OR(args.radius, args.radius_x); |
|
320 const unsigned ry = ASC_NONZERO_OR(args.radius, args.radius_y); |
|
321 ellipsis->node.position.x = (float) (cx-rx); |
|
322 ellipsis->node.position.y = (float) (cy-ry); |
|
323 ellipsis->radii.x = (float) rx; |
|
324 ellipsis->radii.y = (float) ry; |
|
325 } |
|
326 |
|
327 ellipsis->color = asc_col_itof(asc_context.ink); |
|
328 ellipsis->border_color = asc_col_itof(args.border_color); |
|
329 ellipsis->filled = args.filled; |
|
330 if (!args.filled && args.thickness == 0) { |
|
331 // when we do not fill the ellipsis, we need a border |
|
332 ellipsis->thickness = 1; |
|
333 } else { |
|
334 ellipsis->thickness = args.thickness; |
|
335 } |
|
336 if (!args.filled && asc_col4_test_zero(args.border_color)) { |
|
337 // convenience fallback: |
|
338 // when we are drawing an outline but have no explicit border color, |
|
339 // use the active ink |
|
340 ellipsis->border_color = ellipsis->color; |
|
341 } |
|
342 |
|
343 AscSceneNode *node = &ellipsis->node; |
|
344 node->position.z = ASC_SCENE_2D_DEPTH_OFFSET; |
|
345 node->scale = asc_vec3f_one; |
|
346 node->render_group = asc_context.ink.alpha < 255 |
|
347 ? ASC_RENDER_GROUP_2D_BLEND |
|
348 : ASC_RENDER_GROUP_2D_OPAQUE; |
|
349 node->update_func = asc_ellipsis_update; |
|
350 node->destroy_func = asc_ellipsis_destroy; |
|
351 node->draw_func = asc_ellipsis_draw; |
|
352 asc_node_update(node); |
|
353 return node; |
|
354 } |