src/2d.c

changeset 173
bd57fe3f6360
parent 169
6e6717d9c776
equal deleted inserted replaced
172:8178eee19656 173:bd57fe3f6360
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 }

mercurial