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 2024 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/error.h" #include "ascension/context.h" #include "ascension/texture.h" #include "ascension/filesystem.h" #include <assert.h> #include <GL/glew.h> #include <SDL2/SDL_image.h> void asc_texture_bind(const AscTexture *tex, int uniform_location, int unit) { glActiveTexture(GL_TEXTURE0 + unit); GLenum error = glGetError(); if (error == GL_INVALID_ENUM) { GLint max_units; glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &max_units); asc_error("Tried to use more texture units than available (%u/%u).", unit, max_units); } glBindTexture(tex->target, tex->tex_id); glUniform1i(uniform_location, unit); asc_error_catch_all_gl(); } void asc_texture_from_surface(AscTexture *tex, const SDL_Surface *surface) { if (tex->tex_id == 0) { asc_error("Tried to use uninitialized texture (%"PRIxPTR ").", (uintptr_t) tex); return; } tex->width = surface->w; tex->height = surface->h; glBindTexture(tex->target,tex->tex_id); glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch / surface->format->BytesPerPixel); // Determine the format and internal format based on the SDL surface format GLint internal_format; GLenum format; switch (surface->format->format) { case SDL_PIXELFORMAT_RGB24: internal_format = GL_RGB8; format = GL_RGB; break; case SDL_PIXELFORMAT_BGR24: internal_format = GL_RGB8; format = GL_BGR; break; case SDL_PIXELFORMAT_RGBA32: case SDL_PIXELFORMAT_ABGR32: internal_format = GL_RGBA8; format = GL_RGBA; break; case SDL_PIXELFORMAT_ARGB32: case SDL_PIXELFORMAT_BGRA32: internal_format = GL_RGBA8; format = GL_BGRA; break; default: asc_error("Unsupported pixel format: %x", surface->format->format); return; } glTexImage2D(tex->target, 0, internal_format, surface->w, surface->h, 0, format, GL_UNSIGNED_BYTE, surface->pixels); // TODO: replace catch all with proper error handling for this single call asc_error_catch_all_gl(); } void asc_texture_from_file(AscTexture *tex, const char *name) { cxmutstr filepath = asc_filesystem_combine_paths(cx_strcast(asc_context.texture_path), cx_str(name)); asc_dprintf("Load texture from %" CX_PRIstr, CX_SFMT(filepath)); SDL_Surface *image = IMG_Load(filepath.ptr); cx_strfree(&filepath); if (image == NULL) { asc_error("Failed to load texture: %s", IMG_GetError()); return; } asc_texture_from_surface(tex, image); asc_dprintf("Free temporary surface %"PRIxPTR, (uintptr_t) image); SDL_FreeSurface(image); } void asc_texture_init( AscTexture *tex, unsigned count, enum asc_texture_target target, enum asc_texture_min_filter min_filter, enum asc_texture_mag_filter mag_filter ) { static const GLenum texture_targets[] = { GL_TEXTURE_RECTANGLE, GL_TEXTURE_2D, }; static const GLint texture_filters[] = { GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR }; assert(target < sizeof(texture_targets) / sizeof(GLenum)); assert(min_filter < sizeof(texture_filters) / sizeof(GLint)); assert(mag_filter < 2); // mag filter only supports nearest/linear GLuint textures[count]; glGenTextures(count, textures); for (unsigned i = 0; i < count; ++i) { memset(&tex[i], 0, sizeof(AscTexture)); tex[i].tex_id = textures[i]; tex[i].target = texture_targets[target]; glBindTexture(tex[i].target, tex[i].tex_id); glTexParameteri(tex[i].target, GL_TEXTURE_MIN_FILTER, texture_filters[min_filter]); glTexParameteri(tex[i].target, GL_TEXTURE_MAG_FILTER, texture_filters[mag_filter]); asc_dprintf("Initialized texture: %u", tex[i].tex_id); } // TODO: proper error handling for each gl call asc_error_catch_all_gl(); } void asc_texture_destroy(AscTexture *tex, unsigned count) { GLuint textures[count]; for (unsigned i = 0; i < count; ++i) { if (tex[i].refcount > 0) { asc_wprintf("Texture %u still in use by %u objects.", tex[i].tex_id, tex[i].refcount); } asc_dprintf("Destroy texture: %u", tex[i].tex_id); textures[i] = tex[i].tex_id; memset(&tex[i], 0, sizeof(AscTexture)); } glDeleteTextures(count, textures); } bool asc_texture_is_uv_normalized(const AscTexture *tex) { return tex->target != GL_TEXTURE_RECTANGLE; } asc_vec2f asc_texture_calculate_uv_scale(const AscTexture *tex, asc_vec2u surface_dimension, asc_vec2f factors) { if (surface_dimension.width == 0 || surface_dimension.height == 0) { asc_wprintf("Tried to calculate UV scale for texture %u with zero dimensions.", tex->tex_id); return factors; } asc_vec2f uv_scale = factors; // non-normalized coordinates for rectangle textures if (tex->target == GL_TEXTURE_RECTANGLE) { uv_scale.x *= (float) tex->width; uv_scale.y *= (float) tex->height; } // apply scaling if (surface_dimension.width != tex->width) { uv_scale.u *= (float) surface_dimension.width; uv_scale.u /= (float) tex->width; } if (surface_dimension.height != tex->height) { uv_scale.v *= (float) surface_dimension.height; uv_scale.v /= (float) tex->height; } return uv_scale; }