Wed, 02 Jul 2025 23:21:17 +0200
resolve TODOs regarding input.h
a) mouse position must be integer, because it can be negative (though rarely)
b) we should not trade "access complexity" for space in the scancodes array
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * Copyright 2025 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "ascension/sprite.h" #include "ascension/context.h" #include "ascension/glcontext.h" #include "ascension/error.h" #include "ascension/mesh.h" #include "ascension/shader.h" #include "ascension/constants.h" typedef struct asc_sprite_shader_s { AscShaderProgram program; asc_uniform_loc tex; } AscSpriteShader; static AscShaderProgram *asc_sprite_shader_create(int rect) { AscShaderCodes codes; if (asc_shader_load_code_files((AscShaderCodeInfo){ .files.vtx = "sprite_vtx.glsl", .files.frag = "sprite_frag.glsl", .defines.frag = rect ? "#define USE_RECT" : NULL, }, &codes)) { asc_error("Loading sprite shader failed."); return NULL; } AscShaderProgram *shader = asc_shader_create(codes, sizeof(*shader)); if (asc_shader_invalid(shader)) { asc_shader_free_codes(codes); return shader; } asc_ptr_cast(AscSpriteShader, sprite_shader, shader); sprite_shader->tex = asc_shader_get_uniform_loc(shader, "tex"); asc_shader_free_codes(codes); asc_error_catch_all_gl(); return shader; } static void asc_sprite_destroy(AscSceneNode *node) { asc_ptr_cast(AscSprite, sprite, node); asc_mesh_destroy(&sprite->mesh); sprite->texture->refcount--; } static void asc_sprite_update(AscSceneNode *node) { asc_ptr_cast(AscSprite, sprite, node); // calculate texture parameters asc_vec2f uv_scale; if (sprite->texture_scale_mode == ASC_TEXTURE_SCALE_REPEAT) { uv_scale = asc_texture_calculate_uv_scale(sprite->texture, asc_vec2u_new(sprite->width, sprite->height), sprite->texture_scale); } else { uv_scale = sprite->texture_scale; } // update mesh asc_mesh_plane_2d(&sprite->mesh, .size = asc_vec2f_new(sprite->width, sprite->height), .uv_scale = uv_scale ); } void asc_sprite_draw(const AscCamera *camera, const AscSceneNode *node) { asc_cptr_cast(AscSprite, sprite, node); // Activate shader // TODO: scene should know which shader we are going to activate s.t. it can pre-sort nodes const AscShaderProgram *shader = asc_texture_is_uv_normalized(sprite->texture) ? asc_shader_lookup_or_create(ASC_SHADER_SPRITE_UV, asc_sprite_shader_create, 0) : asc_shader_lookup_or_create(ASC_SHADER_SPRITE_RECT, asc_sprite_shader_create, 1); if (asc_shader_use(shader, camera)) return; asc_cptr_cast(AscSpriteShader, sprite_shader, shader); // Upload model matrix asc_shader_upload_model_matrix(shader, node); // Bind texture asc_texture_bind(sprite->texture, sprite_shader->tex, 0); // Draw mesh asc_mesh_draw_triangle_strip(&sprite->mesh); } AscSceneNode *asc_sprite_create(struct asc_sprite_create_args args) { AscSprite *sprite = cxZallocDefault(sizeof(AscSprite)); // sprite parameters args.texture->refcount++; sprite->texture = args.texture; sprite->width = args.width; sprite->height = args.height; sprite->texture_scale_mode = args.texture_scale_mode; sprite->texture_scale.u = ASC_NONZERO_OR(1.f, args.texture_scale_x); sprite->texture_scale.v = ASC_NONZERO_OR(1.f, args.texture_scale_y); // basic node parameters AscSceneNode *node = (AscSceneNode *) sprite; asc_scene_node_name(node, args.name); node->render_group = args.opaque ? ASC_RENDER_GROUP_2D_OPAQUE : ASC_RENDER_GROUP_2D_BLEND; node->update_func = asc_sprite_update; node->destroy_func = asc_sprite_destroy; node->draw_func = asc_sprite_draw; node->position = asc_vec3f_new(args.x, args.y, ASC_SCENE_2D_DEPTH_OFFSET); node->scale = asc_vec3f_one; asc_node_update(node); return node; } void asc_sprite_set_size(AscSceneNode *node, unsigned width, unsigned height) { AscSprite *sprite = (AscSprite*) node; sprite->width = width; sprite->height = height; asc_node_update(node); }