LCOV - code coverage report
Current view: top level - blaze - blaze.c (source / functions) Hit Total Coverage
Test: blaze.gcda.info Lines: 479 501 95.6 %
Date: 2019-02-18 14:19:23 Functions: 47 48 97.9 %

          Line data    Source code
       1             : #include "blaze.h"
       2             : #include "./deps/SOIL/SOIL.h"
       3             : #include "./glad/include/glad/glad.h"
       4             : 
       5             : #include <math.h>
       6             : #include <stdio.h>
       7             : #include <stdlib.h>
       8             : #include <string.h>
       9             : 
      10             : #define calloc_one(s) calloc(1, s)
      11             : #define BUFFER_COUNT 2
      12             : #define HAS_FLAG(batch, flag) ((batch->flags & flag) == flag)
      13             : 
      14             : #define return_success(result) \
      15             :         do                         \
      16             :         {                          \
      17             :                 __lastError = NULL;    \
      18             :                 return result;         \
      19             :         } while (0);
      20             : #define success() return_success(BLZ_TRUE)
      21             : #define fail(msg)          \
      22             :         do                     \
      23             :         {                      \
      24             :                 __lastError = msg; \
      25             :                 return BLZ_FALSE;  \
      26             :         } while (0);
      27             : #define fail_cmp(val, cmp, msg) \
      28             :         do                          \
      29             :         {                           \
      30             :                 if (val == cmp)         \
      31             :                 {                       \
      32             :                         fail(msg);          \
      33             :                 }                       \
      34             :         } while (0);
      35             : #define fail_if_null(val, msg) fail_cmp(val, NULL, msg)
      36             : #define fail_if_false(val, msg) fail_cmp(val, BLZ_FALSE, msg)
      37             : #define null_if_false(val, msg) \
      38             :         do                          \
      39             :         {                           \
      40             :                 if (val == BLZ_FALSE)   \
      41             :                 {                       \
      42             :                         __lastError = msg;  \
      43             :                         return NULL;        \
      44             :                 }                       \
      45             :         } while (0);
      46             : #define check_alloc(p) fail_if_null(p, "Could not allocate memory")
      47             : #define validate(expr)                                         \
      48             :         do                                                         \
      49             :         {                                                          \
      50             :                 if (!(expr))                                           \
      51             :                 {                                                      \
      52             :                         fail("Invalid parameter value, should be " #expr); \
      53             :                 }                                                      \
      54             :         } while (0);
      55             : #define null_if_invalid(expr)                                          \
      56             :         do                                                                 \
      57             :         {                                                                  \
      58             :                 if (!(expr))                                                   \
      59             :                 {                                                              \
      60             :                         __lastError = "Invalid parameter value, should be " #expr; \
      61             :                         return NULL;                                               \
      62             :                 }                                                              \
      63             :         } while (0);
      64             : 
      65             : /* Public constants */
      66             : const struct BLZ_BlendFunc BLEND_NORMAL = {GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA};
      67             : const struct BLZ_BlendFunc BLEND_ADDITIVE = {GL_ONE, GL_ONE};
      68             : const struct BLZ_BlendFunc BLEND_MULTIPLY = {GL_DST_COLOR, GL_ZERO};
      69             : 
      70             : /* Internal values */
      71             : struct Buffer
      72             : {
      73             :         GLuint vao, vbo, ebo;
      74             : };
      75             : 
      76             : struct BLZ_StaticBatch
      77             : {
      78             :         int sprite_count;
      79             :         int max_sprite_count;
      80             :         unsigned char is_uploaded;
      81             :         struct BLZ_Vertex *vertices;
      82             :         struct Buffer buffer;
      83             :         const struct BLZ_Texture *texture;
      84             : };
      85             : 
      86             : struct BLZ_SpriteBatch
      87             : {
      88             :         int max_buckets;
      89             :         int max_sprites_per_bucket;
      90             :         unsigned char buffer_index;
      91             :         unsigned char frameskip;
      92             :         enum BLZ_InitFlags flags;
      93             :         struct SpriteBucket *sprite_buckets;
      94             : };
      95             : 
      96             : struct BLZ_Shader
      97             : {
      98             :         GLuint program;
      99             :         GLint mvp_param;
     100             : };
     101             : 
     102             : struct SpriteBucket
     103             : {
     104             :         GLuint texture;
     105             :         int sprite_count;
     106             :         struct BLZ_Vertex *vertices;
     107             :         struct Buffer buffer[BUFFER_COUNT];
     108             : };
     109             : 
     110             : static char *__lastError = NULL;
     111             : 
     112             : #define Z_NEAR 0.9f
     113             : #define Z_FAR 2.1f
     114             : #define LAYER_DEPTH(depth) 1.0f + depth
     115             : 
     116             : static GLfloat orthoMatrix[16] =
     117             :         {0, 0, 0, 0,
     118             :          0, 0, 0, 0,
     119             :          0, 0, 1, 0,
     120             :          -1, 1, 0, 1};
     121             : 
     122             : static GLchar vertexSource[] =
     123             :         "#version 130\n"
     124             :         "uniform mat4 u_mvpMatrix;"
     125             :         "in vec2 in_Position;"
     126             :         "in vec2 in_Texcoord;"
     127             :         "in vec4 in_Color;"
     128             :         "out vec4 ex_Color;"
     129             :         "out vec2 ex_Texcoord;"
     130             :         "void main() {"
     131             :         "  ex_Color = in_Color;"
     132             :         "  ex_Texcoord = in_Texcoord;"
     133             :         "  gl_Position = u_mvpMatrix * vec4(in_Position, 1, 1);"
     134             :         "}";
     135             : 
     136             : static GLchar fragmentSource[] =
     137             :         "#version 130\n"
     138             :         "in vec4 ex_Color;"
     139             :         "in vec2 ex_Texcoord;"
     140             :         "out vec4 outColor;"
     141             :         "uniform sampler2D tex;"
     142             :         "void main() {"
     143             :         "  outColor = texture(tex, ex_Texcoord) * ex_Color;"
     144             :         "}";
     145             : 
     146             : static BLZ_Shader *SHADER_DEFAULT;
     147             : static BLZ_Shader *SHADER_CURRENT;
     148             : static struct Buffer immediateBuf;
     149             : static GLuint tex0_override = 0;
     150             : 
     151             : static const int VERT_SIZE = sizeof(struct BLZ_Vertex);
     152             : 
     153             : /* TODO: Optimization: Reuse same VAO for all batches to minimize state changes */
     154          56 : static struct Buffer create_buffer(int max_sprites, GLenum usage)
     155             : {
     156          56 :         int INDICES_SIZE = max_sprites * 6 * sizeof(GLushort);
     157             :         int i;
     158             :         struct Buffer result;
     159          56 :         GLushort *indices = malloc(INDICES_SIZE);
     160             :         GLuint vao, vbo, ebo;
     161          56 :         glGenVertexArrays(1, &vao);
     162          56 :         glBindVertexArray(vao);
     163          56 :         glGenBuffers(1, &vbo);
     164          56 :         glBindBuffer(GL_ARRAY_BUFFER, vbo);
     165          56 :         glBufferData(GL_ARRAY_BUFFER, VERT_SIZE * 4 * max_sprites,
     166             :                                  NULL, usage);
     167             :         /* x|y */
     168          56 :         glEnableVertexAttribArray(0);
     169          56 :         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, VERT_SIZE, (void *)0);
     170             :         /* u|v */
     171          56 :         glEnableVertexAttribArray(1);
     172          56 :         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, VERT_SIZE, (void *)8);
     173             :         /* r|g|b|a */
     174          56 :         glEnableVertexAttribArray(2);
     175          56 :         glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, VERT_SIZE, (void *)16);
     176             :         /* indices */
     177          56 :         glGenBuffers(1, &ebo);
     178          56 :         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
     179        3768 :         for (i = 0; i < max_sprites; i++)
     180             :         {
     181        3712 :                 *(indices + (i * 6)) = (GLushort)(i * 4);
     182        3712 :                 *(indices + (i * 6) + 1) = (GLushort)(i * 4 + 1);
     183        3712 :                 *(indices + (i * 6) + 2) = (GLushort)(i * 4 + 2);
     184        3712 :                 *(indices + (i * 6) + 3) = (GLushort)(i * 4 + 2);
     185        3712 :                 *(indices + (i * 6) + 4) = (GLushort)(i * 4 + 1);
     186        3712 :                 *(indices + (i * 6) + 5) = (GLushort)(i * 4 + 3);
     187             :         }
     188          56 :         glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_SIZE, indices, GL_STATIC_DRAW);
     189          56 :         free(indices);
     190          56 :         glBindVertexArray(0);
     191          56 :         result.vao = vao;
     192          56 :         result.vbo = vbo;
     193          56 :         return result;
     194             : }
     195             : 
     196          51 : static void free_buffer(struct Buffer buffer)
     197             : {
     198          51 :         glDeleteVertexArrays(1, &buffer.vao);
     199          51 :         glDeleteBuffers(1, &buffer.vbo);
     200          51 : }
     201             : 
     202             : /* Public API */
     203           0 : char* BLZ_GetLastError()
     204             : {
     205           0 :         return __lastError;
     206             : }
     207             : 
     208           8 : int BLZ_Load(glGetProcAddress loader)
     209             : {
     210           8 :         int result = gladLoadGLLoader((GLADloadproc)loader);
     211           8 :         fail_if_false(result, "Could not load the OpenGL library");
     212           8 :         SHADER_DEFAULT = BLZ_CompileShader(vertexSource, fragmentSource);
     213           8 :         fail_if_false(SHADER_DEFAULT, "Could not compile default shader");
     214           8 :         fail_if_false(BLZ_UseShader(SHADER_DEFAULT), "Could not use default shader");
     215           8 :         immediateBuf = create_buffer(1, GL_STREAM_DRAW);
     216           8 :         glDisable(GL_DEPTH_TEST);
     217           8 :         glDepthMask(GL_FALSE);
     218           8 :         glEnable(GL_BLEND);
     219           8 :         success();
     220             : }
     221             : 
     222           1 : int BLZ_GetMaxTextureSlots()
     223             : {
     224             :         GLint result;
     225           1 :         glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &result);
     226           1 :         return result;
     227             : }
     228             : 
     229           4 : int BLZ_BindTexture(struct BLZ_Texture *texture, int slot)
     230             : {
     231           4 :         GLuint id = texture == NULL ? 0 : texture->id;
     232           4 :         glActiveTexture(GL_TEXTURE0 + slot);
     233           4 :         glBindTexture(GL_TEXTURE_2D, id);
     234           4 :         if (slot == 0)
     235             :         {
     236           2 :                 tex0_override = id;
     237             :         }
     238           4 :         success();
     239             : }
     240             : 
     241           2 : int BLZ_SetTextureFiltering(
     242             :         struct BLZ_Texture *texture,
     243             :         enum BLZ_TextureFilter minification,
     244             :         enum BLZ_TextureFilter magnification)
     245             : {
     246           2 :         glBindTexture(GL_TEXTURE_2D, texture->id);
     247           2 :         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minification);
     248           2 :         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magnification);
     249           2 :         glBindTexture(GL_TEXTURE_2D, 0);
     250           2 :         success();
     251             : }
     252             : 
     253           1 : int BLZ_SetTextureWrap(
     254             :         struct BLZ_Texture *texture,
     255             :         enum BLZ_TextureWrap x,
     256             :         enum BLZ_TextureWrap y)
     257             : {
     258           1 :         glBindTexture(GL_TEXTURE_2D, texture->id);
     259           1 :         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, x);
     260           1 :         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, y);
     261           1 :         glBindTexture(GL_TEXTURE_2D, 0);
     262           1 :         success();
     263             : }
     264             : 
     265          51 : static void bind_tex0(GLuint tex)
     266             : {
     267          51 :         if (tex0_override == 0)
     268             :         {
     269          46 :                 glActiveTexture(GL_TEXTURE0);
     270          46 :                 glBindTexture(GL_TEXTURE_2D, tex);
     271             :         }
     272          51 : }
     273             : 
     274           3 : int BLZ_GetOptions(const struct BLZ_SpriteBatch *batch,
     275             :                                    int *max_buckets, int *max_sprites_per_bucket,
     276             :                                    enum BLZ_InitFlags *flags)
     277             : {
     278           3 :         validate(batch != NULL);
     279           3 :         if (batch->max_buckets <= 0 || batch->max_sprites_per_bucket <= 0)
     280             :         {
     281           1 :                 fail("Not initialized");
     282             :         }
     283           2 :         *max_buckets = batch->max_buckets;
     284           2 :         *max_sprites_per_bucket = batch->max_sprites_per_bucket;
     285           2 :         *flags = batch->flags;
     286           2 :         success();
     287             : }
     288             : 
     289           6 : int BLZ_SetViewport(int w, int h)
     290             : {
     291           6 :         validate(w > 0);
     292           6 :         validate(h > 0);
     293           6 :         orthoMatrix[0] = 2.0f / (GLfloat)w;
     294           6 :         orthoMatrix[5] = -2.0f / (GLfloat)h;
     295           6 :         success();
     296             : }
     297             : 
     298           6 : void BLZ_SetClearColor(struct BLZ_Vector4 color)
     299             : {
     300           6 :         glClearColor(color.x, color.y, color.z, color.w);
     301           6 : }
     302             : 
     303          20 : void BLZ_SetBlendMode(const struct BLZ_BlendFunc func)
     304             : {
     305          20 :         glBlendFunc(func.source, func.destination);
     306          20 : }
     307             : 
     308          31 : void BLZ_Clear()
     309             : {
     310          31 :         glClear(GL_COLOR_BUFFER_BIT);
     311          31 : }
     312             : 
     313          21 : static GLuint compile_shader(GLenum type, const char *src)
     314             : {
     315             :         int compiled;
     316             :         int log_length;
     317             :         char *log_string;
     318          21 :         GLuint shader = glCreateShader(type);
     319          21 :         glShaderSource(shader, 1, (const GLchar **)&src, 0);
     320          21 :         glCompileShader(shader);
     321          21 :         glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
     322          21 :         if (!compiled)
     323             :         {
     324           1 :                 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
     325           1 :                 log_string = malloc(log_length);
     326           1 :                 glGetShaderInfoLog(shader, log_length, &log_length, log_string);
     327           1 :                 printf("Error compiling shader: %s\n", log_string);
     328           1 :                 free(log_string);
     329           1 :                 return 0;
     330             :         }
     331          20 :         return shader;
     332             : }
     333             : 
     334          12 : GLint BLZ_GetUniformLocation(const struct BLZ_Shader *shader, const char *name)
     335             : {
     336          12 :         if (shader == NULL || name == NULL)
     337             :         {
     338           0 :                 return -1;
     339             :         }
     340          12 :         return glGetUniformLocation(shader->program, (const GLchar *)name);
     341             : }
     342             : 
     343          11 : BLZ_Shader *BLZ_CompileShader(const char *vert, const char *frag)
     344             : {
     345             :         struct BLZ_Shader *shader;
     346             :         GLuint program, vertex_shader, fragment_shader;
     347             :         int is_linked, log_length;
     348             :         char *log_string;
     349          11 :         validate(vert != NULL);
     350          11 :         validate(frag != NULL);
     351          11 :         shader = malloc(sizeof(struct BLZ_Shader));
     352          11 :         vertex_shader = compile_shader(GL_VERTEX_SHADER, vert);
     353          11 :         if (!vertex_shader)
     354             :         {
     355           1 :                 return NULL;
     356             :         }
     357          10 :         fragment_shader = compile_shader(GL_FRAGMENT_SHADER, frag);
     358          10 :         if (!fragment_shader)
     359             :         {
     360           0 :                 return NULL;
     361             :         }
     362          10 :         program = glCreateProgram();
     363          10 :         glAttachShader(program, vertex_shader);
     364          10 :         glAttachShader(program, fragment_shader);
     365          10 :         glBindAttribLocation(program, 0, "in_Position");
     366          10 :         glBindAttribLocation(program, 1, "in_Texcoord");
     367          10 :         glBindAttribLocation(program, 2, "in_Color");
     368          10 :         glLinkProgram(program);
     369          10 :         glGetProgramiv(program, GL_LINK_STATUS, &is_linked);
     370          10 :         if (!is_linked)
     371             :         {
     372           0 :                 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
     373           0 :                 log_string = malloc(log_length);
     374           0 :                 glGetProgramInfoLog(program, log_length, &log_length, log_string);
     375           0 :                 printf("Error linking shader: %s\n", log_string);
     376           0 :                 free(log_string);
     377           0 :                 return NULL;
     378             :         }
     379          10 :         shader->program = program;
     380          10 :         shader->mvp_param = BLZ_GetUniformLocation(shader, "u_mvpMatrix");
     381          10 :         return shader;
     382             : }
     383             : 
     384          10 : int BLZ_UseShader(struct BLZ_Shader *program)
     385             : {
     386             :         GLenum result;
     387          10 :         validate(program != NULL);
     388             :         /* clear previous errors to make sure we're reading the actual one */
     389          10 :         while (glGetError() != GL_NO_ERROR)
     390             :         {
     391             :         };
     392          10 :         glUseProgram(program->program);
     393          10 :         result = glGetError();
     394          10 :         if (result == GL_NO_ERROR)
     395             :         {
     396          10 :                 SHADER_CURRENT = program;
     397          10 :                 success();
     398             :         }
     399           0 :         printf("glUseProgram: error %d\n", result);
     400           0 :         fail("Could not use shader program");
     401             : }
     402             : 
     403           2 : int BLZ_FreeShader(BLZ_Shader *program)
     404             : {
     405           2 :         validate(program != NULL);
     406           2 :         glDeleteShader(program->program);
     407           2 :         free(program);
     408           2 :         success();
     409             : }
     410             : 
     411           1 : BLZ_Shader *BLZ_GetDefaultShader()
     412             : {
     413           1 :         return SHADER_DEFAULT;
     414             : }
     415             : 
     416           6 : int BLZ_FreeBatch(struct BLZ_SpriteBatch *batch)
     417             : {
     418             :         int i;
     419             :         struct SpriteBucket cur;
     420           6 :         if (batch->sprite_buckets != NULL)
     421             :         {
     422          29 :                 for (i = 0; i < batch->max_buckets; i++)
     423             :                 {
     424          23 :                         cur = *(batch->sprite_buckets + i);
     425          23 :                         free_buffer(cur.buffer[0]);
     426          23 :                         if (!HAS_FLAG(batch, NO_BUFFERING))
     427             :                         {
     428          13 :                                 free_buffer(cur.buffer[1]);
     429          13 :                                 free_buffer(cur.buffer[2]);
     430             :                         }
     431             :                 }
     432           6 :                 free(batch->sprite_buckets);
     433             :         }
     434           6 :         free(batch);
     435           6 :         success();
     436             : }
     437             : 
     438           7 : struct BLZ_SpriteBatch *BLZ_CreateBatch(
     439             :         int max_buckets, int max_sprites_per_bucket, enum BLZ_InitFlags flags)
     440             : {
     441             :         int i;
     442             :         struct BLZ_Vertex *vertices;
     443             :         struct SpriteBucket *cur;
     444           7 :         struct BLZ_SpriteBatch *batch = malloc(sizeof(BLZ_SpriteBatch));
     445           7 :         null_if_invalid(max_buckets > 0);
     446           6 :         null_if_invalid(max_sprites_per_bucket > 0);
     447           6 :         batch->max_sprites_per_bucket = max_sprites_per_bucket;
     448           6 :         batch->max_buckets = max_buckets;
     449           6 :         batch->flags = flags;
     450           6 :         batch->buffer_index = 0;
     451           6 :         if (!HAS_FLAG(batch, NO_BUFFERING))
     452             :         {
     453           5 :                 batch->frameskip = 1;
     454             :         }
     455           6 :         batch->sprite_buckets = calloc(batch->max_buckets, sizeof(struct SpriteBucket));
     456           6 :         check_alloc(batch->sprite_buckets);
     457          29 :         for (i = 0; i < batch->max_buckets; i++)
     458             :         {
     459          23 :                 cur = (batch->sprite_buckets + i);
     460          23 :                 vertices = malloc(batch->max_sprites_per_bucket * 4 * sizeof(struct BLZ_Vertex));
     461          23 :                 check_alloc(vertices);
     462          23 :                 cur->vertices = vertices;
     463          23 :                 cur->buffer[0] = create_buffer(max_sprites_per_bucket, GL_STREAM_DRAW);
     464          23 :                 cur->buffer[1] = create_buffer(max_sprites_per_bucket, GL_STREAM_DRAW);
     465             :         }
     466           6 :         return batch;
     467             : }
     468             : 
     469          46 : static inline void set_mvp_matrix(const GLfloat *matrix)
     470             : {
     471          46 :         if (SHADER_CURRENT->mvp_param > -1)
     472             :         {
     473          46 :                 BLZ_UniformMatrix4fv(SHADER_CURRENT->mvp_param, 1, GL_FALSE, matrix);
     474             :         }
     475          46 : }
     476             : 
     477             : static struct BLZ_SpriteBatch *__lastBatch;
     478             : static struct SpriteBucket *__lastBucket;
     479             : static GLuint __lastTexture;
     480          20 : static int flush(struct BLZ_SpriteBatch *batch)
     481             : {
     482             :         unsigned char to_draw, to_fill;
     483             :         struct SpriteBucket bucket;
     484             :         struct SpriteBucket *bucket_ptr;
     485             :         int i, buf_size;
     486          20 :         set_mvp_matrix((const GLfloat *)&orthoMatrix);
     487          20 :         if (HAS_FLAG(batch, NO_BUFFERING) || batch->frameskip)
     488             :         {
     489           4 :                 to_draw = to_fill = 0;
     490             :         }
     491             :         else
     492             :         {
     493          16 :                 to_draw = batch->buffer_index,
     494          16 :                 to_fill = batch->buffer_index + 1;
     495          16 :                 if (to_fill >= BUFFER_COUNT)
     496             :                 {
     497           8 :                         to_fill -= BUFFER_COUNT;
     498             :                 }
     499             :         }
     500          45 :         for (i = 0; i < batch->max_buckets; i++)
     501             :         {
     502          40 :                 bucket_ptr = (batch->sprite_buckets + i);
     503          40 :                 bucket = *bucket_ptr;
     504          40 :                 buf_size = bucket.sprite_count * 4 * sizeof(struct BLZ_Vertex);
     505          40 :                 if (buf_size == 0 || bucket.texture == 0)
     506             :                 {
     507             :                         /* we've reached the end of the batch */
     508             :                         break;
     509             :                 }
     510             :                 /* fill the buffer */
     511          25 :                 glBindBuffer(GL_ARRAY_BUFFER, bucket.buffer[to_fill].vbo);
     512          25 :                 glBufferData(GL_ARRAY_BUFFER, buf_size, bucket.vertices, GL_STREAM_DRAW);
     513          25 :                 glBindBuffer(GL_ARRAY_BUFFER, 0);
     514             :                 /* bind our texture and the VAO and draw it */
     515          25 :                 bind_tex0(bucket.texture);
     516          25 :                 glBindVertexArray(bucket.buffer[to_draw].vao);
     517          25 :                 glDrawElements(GL_TRIANGLES, bucket.sprite_count * 6, GL_UNSIGNED_SHORT, (void *)0);
     518          25 :                 bucket_ptr->sprite_count = 0;
     519          25 :                 bucket_ptr->texture = 0;
     520             :         }
     521          20 :         __lastBatch = NULL;
     522          20 :         __lastBucket = NULL;
     523          20 :         __lastTexture = 0;
     524          20 :         success();
     525             : }
     526             : 
     527          20 : int BLZ_Present(struct BLZ_SpriteBatch *batch)
     528             : {
     529          20 :         fail_if_false(flush(batch), "Could not flush the sprite batch");
     530          20 :         if (!HAS_FLAG(batch, NO_BUFFERING) && batch->frameskip == 0)
     531             :         {
     532          16 :                 batch->buffer_index++;
     533          16 :                 if (batch->buffer_index >= BUFFER_COUNT)
     534             :                 {
     535           8 :                         batch->buffer_index -= BUFFER_COUNT;
     536             :                 }
     537             :         }
     538          20 :         if (batch->frameskip > 0)
     539             :         {
     540           4 :                 batch->frameskip--;
     541             :         }
     542          20 :         success();
     543             : }
     544             : 
     545             : #define set_vertex(index, field, val)     \
     546             :         do                                    \
     547             :         {                                     \
     548             :                 quad.vertices[index].field = val; \
     549             :         } while (0);
     550             : 
     551             : #define set_all_vertices(field, val) \
     552             :         do                               \
     553             :         {                                \
     554             :                 set_vertex(0, field, val);   \
     555             :                 set_vertex(1, field, val);   \
     556             :                 set_vertex(2, field, val);   \
     557             :                 set_vertex(3, field, val);   \
     558             :         } while (0);
     559             : 
     560             : #define swap(tmp, one, two) \
     561             :         do                      \
     562             :         {                       \
     563             :                 tmp = one;          \
     564             :                 one = two;          \
     565             :                 two = tmp;          \
     566             :         } while (0);
     567             : 
     568         224 : static struct BLZ_SpriteQuad transform_position_fastpath(
     569             :         const struct BLZ_Texture *texture,
     570             :         const struct BLZ_Vector2 position,
     571             :         const struct BLZ_Vector4 color)
     572             : {
     573         224 :         GLfloat x = position.x;
     574         224 :         GLfloat y = position.y;
     575         224 :         GLfloat w = (GLfloat)texture->width;
     576         224 :         GLfloat h = (GLfloat)texture->height;
     577             :         struct BLZ_SpriteQuad quad;
     578             : 
     579             :         /* set the vertex values */
     580         224 :         set_vertex(0, x, x);
     581         224 :         set_vertex(0, y, y);
     582         224 :         set_vertex(0, u, 0);
     583         224 :         set_vertex(0, v, 0);
     584         224 :         set_vertex(2, x, x + w);
     585         224 :         set_vertex(2, y, y);
     586         224 :         set_vertex(2, u, 1);
     587         224 :         set_vertex(2, v, 0);
     588         224 :         set_vertex(3, x, x + w);
     589         224 :         set_vertex(3, y, y + h);
     590         224 :         set_vertex(3, u, 1);
     591         224 :         set_vertex(3, v, 1);
     592         224 :         set_vertex(1, x, x);
     593         224 :         set_vertex(1, y, y + h);
     594         224 :         set_vertex(1, u, 0);
     595         224 :         set_vertex(1, v, 1);
     596             : 
     597         224 :         set_all_vertices(r, color.x);
     598         224 :         set_all_vertices(g, color.y);
     599         224 :         set_all_vertices(b, color.z);
     600         224 :         set_all_vertices(a, color.w);
     601         224 :         return quad;
     602             : }
     603             : 
     604         456 : static struct BLZ_SpriteQuad transform_full(
     605             :         const struct BLZ_Texture *texture,
     606             :         const struct BLZ_Vector2 position,
     607             :         const struct BLZ_Rectangle *srcRectangle,
     608             :         float rotation,
     609             :         const struct BLZ_Vector2 *origin,
     610             :         const struct BLZ_Vector2 *scale,
     611             :         const struct BLZ_Vector4 color,
     612             :         enum BLZ_SpriteFlip effects)
     613             : {
     614             :         /* position: top-left, top-right, bottom-left, bottom-right */
     615             :         struct BLZ_Vector2 p_tl, p_tr, p_bl, p_br;
     616             :         /* same for texture coordinates */
     617             :         struct BLZ_Vector2 t_tl, t_tr, t_bl, t_br;
     618             :         struct BLZ_SpriteQuad quad;
     619             :         GLfloat dx, dy;
     620         456 :         GLfloat x = position.x;
     621         456 :         GLfloat y = position.y;
     622         456 :         GLfloat tw = (GLfloat)texture->width;
     623         456 :         GLfloat th = (GLfloat)texture->height;
     624         456 :         int w = srcRectangle == NULL ? tw : srcRectangle->w;
     625         456 :         int h = srcRectangle == NULL ? th : srcRectangle->h;
     626         456 :         GLfloat _sin = rotation == 0.0f ? 0.0f : sin(rotation);
     627         456 :         GLfloat _cos = rotation == 0.0f ? 1.0f : cos(rotation);
     628         456 :         GLfloat u1 = srcRectangle == NULL ? 0 : srcRectangle->x / tw;
     629         456 :         GLfloat v1 = srcRectangle == NULL ? 0 : srcRectangle->y / th;
     630         456 :         GLfloat u2 = srcRectangle == NULL ? 1 : u1 + (srcRectangle->w / tw);
     631         456 :         GLfloat v2 = srcRectangle == NULL ? 1 : v1 + (srcRectangle->h / th);
     632             :         GLfloat tmp;
     633         456 :         if (scale != NULL)
     634             :         {
     635         144 :                 w *= scale->x;
     636         144 :                 h *= scale->y;
     637             :         }
     638         456 :         if (origin == NULL)
     639             :         {
     640         312 :                 dx = 0;
     641         312 :                 dy = 0;
     642             :         }
     643             :         else
     644             :         {
     645         144 :                 dx = -origin->x;
     646         144 :                 dy = -origin->y;
     647             :         }
     648         456 :         p_tl.x = x + dx * _cos - dy * _sin;
     649         456 :         p_tl.y = y + dx * _sin + dy * _cos;
     650         456 :         p_tr.x = x + (dx + w) * _cos - dy * _sin;
     651         456 :         p_tr.y = y + (dx + w) * _sin + dy * _cos;
     652         456 :         p_bl.x = x + dx * _cos - (dy + h) * _sin;
     653         456 :         p_bl.y = y + dx * _sin + (dy + h) * _cos;
     654         456 :         p_br.x = x + (dx + w) * _cos - (dy + h) * _sin;
     655         456 :         p_br.y = y + (dx + w) * _sin + (dy + h) * _cos;
     656             : 
     657             :         /* calculate texture coordinates */
     658         456 :         switch (effects)
     659             :         {
     660             :         case FLIP_H:
     661          12 :                 swap(tmp, u1, u2);
     662          12 :                 break;
     663             :         case FLIP_V:
     664          12 :                 swap(tmp, v1, v2);
     665          12 :                 break;
     666             :         case BOTH:
     667          12 :                 swap(tmp, u1, u2);
     668          12 :                 swap(tmp, v1, v2);
     669          12 :                 break;
     670             :         default:
     671         420 :                 break;
     672             :         }
     673         456 :         t_tl.x = u1;
     674         456 :         t_tl.y = v1;
     675         456 :         t_tr.x = u2;
     676         456 :         t_tr.y = v1;
     677         456 :         t_bl.x = u1;
     678         456 :         t_bl.y = v2;
     679         456 :         t_br.x = u2;
     680         456 :         t_br.y = v2;
     681             :         /* set the vertex values */
     682         456 :         set_vertex(0, x, p_tl.x);
     683         456 :         set_vertex(0, y, p_tl.y);
     684         456 :         set_vertex(0, u, t_tl.x);
     685         456 :         set_vertex(0, v, t_tl.y);
     686         456 :         set_vertex(2, x, p_tr.x);
     687         456 :         set_vertex(2, y, p_tr.y);
     688         456 :         set_vertex(2, u, t_tr.x);
     689         456 :         set_vertex(2, v, t_tr.y);
     690         456 :         set_vertex(3, x, p_br.x);
     691         456 :         set_vertex(3, y, p_br.y);
     692         456 :         set_vertex(3, u, t_br.x);
     693         456 :         set_vertex(3, v, t_br.y);
     694         456 :         set_vertex(1, x, p_bl.x);
     695         456 :         set_vertex(1, y, p_bl.y);
     696         456 :         set_vertex(1, u, t_bl.x);
     697         456 :         set_vertex(1, v, t_bl.y);
     698             : 
     699         456 :         set_all_vertices(r, color.x);
     700         456 :         set_all_vertices(g, color.y);
     701         456 :         set_all_vertices(b, color.z);
     702         456 :         set_all_vertices(a, color.w);
     703         456 :         return quad;
     704             : }
     705             : 
     706         680 : inline static struct BLZ_SpriteQuad transform(
     707             :         const struct BLZ_Texture *texture,
     708             :         const struct BLZ_Vector2 position,
     709             :         const struct BLZ_Rectangle *srcRectangle,
     710             :         float rotation,
     711             :         const struct BLZ_Vector2 *origin,
     712             :         const struct BLZ_Vector2 *scale,
     713             :         const struct BLZ_Vector4 color,
     714             :         enum BLZ_SpriteFlip effects)
     715             : {
     716         680 :         if (srcRectangle == NULL && rotation == 0.0f && origin == NULL &&
     717         260 :                 scale == NULL && effects == NONE)
     718             :         {
     719         224 :                 return transform_position_fastpath(texture, position, color);
     720             :         }
     721             :         else
     722             :         {
     723         456 :                 return transform_full(texture, position, srcRectangle, rotation,
     724             :                                                           origin, scale, color, effects);
     725             :         }
     726             : }
     727             : 
     728         565 : int BLZ_Draw(
     729             :         struct BLZ_SpriteBatch *batch,
     730             :         const struct BLZ_Texture *texture,
     731             :         const struct BLZ_Vector2 position,
     732             :         const struct BLZ_Rectangle *srcRectangle,
     733             :         float rotation,
     734             :         const struct BLZ_Vector2 *origin,
     735             :         const struct BLZ_Vector2 *scale,
     736             :         const struct BLZ_Vector4 color,
     737             :         enum BLZ_SpriteFlip effects)
     738             : {
     739         565 :         struct BLZ_SpriteQuad quad = transform(
     740             :                 texture,
     741             :                 position,
     742             :                 srcRectangle,
     743             :                 rotation,
     744             :                 origin,
     745             :                 scale,
     746             :                 color,
     747             :                 effects);
     748         565 :         return BLZ_LowerDraw(batch, texture->id, &quad);
     749             : }
     750             : 
     751         565 : int BLZ_LowerDraw(
     752             :         struct BLZ_SpriteBatch *batch,
     753             :         GLuint texture, const struct BLZ_SpriteQuad *quad)
     754             : {
     755         565 :         struct SpriteBucket *bucket = NULL;
     756         565 :         int i = 0;
     757             :         size_t offset;
     758         565 :         if (__lastBatch != batch)
     759             :         {
     760          20 :                 __lastBatch = NULL;
     761          20 :                 __lastBucket = NULL;
     762          20 :                 __lastTexture = 0;
     763             :         }
     764         565 :         if (__lastTexture > 0 && texture == __lastTexture)
     765             :         {
     766         540 :                 if (__lastBucket != NULL && __lastBucket->sprite_count < batch->max_sprites_per_bucket)
     767             :                 {
     768         540 :                         bucket = __lastBucket;
     769             :                 }
     770             :         }
     771         565 :         if (bucket == NULL)
     772             :         {
     773          30 :                 for (i = 0; i < batch->max_buckets; i++)
     774             :                 {
     775          30 :                         bucket = (batch->sprite_buckets + i);
     776          30 :                         if (bucket->texture == texture &&
     777           0 :                                 bucket->sprite_count >= batch->max_sprites_per_bucket)
     778             :                         {
     779             :                                 /* this bucket is already filled, skip it */
     780           0 :                                 continue;
     781             :                         }
     782          30 :                         if (bucket->texture == texture || bucket->texture == 0)
     783             :                         {
     784             :                                 /* we found existing not-full bucket or an empty one */
     785             :                                 break;
     786             :                         }
     787             :                 }
     788             :         }
     789        1130 :         if (bucket->sprite_count >= batch->max_sprites_per_bucket ||
     790        1105 :                 (bucket->texture != 0 && bucket->texture != texture))
     791             :         {
     792             :                 /* we ran out of limits */
     793           0 :                 fail("Sprite limit reached - increase limits in BLZ_CreateBatch(...)");
     794             :         }
     795         565 :         offset = bucket->sprite_count * 4;
     796             :         /* set the vertex data */
     797         565 :         memcpy((bucket->vertices + offset), quad, sizeof(struct BLZ_SpriteQuad));
     798         565 :         bucket->sprite_count++;
     799         565 :         bucket->texture = texture;
     800         565 :         __lastBatch = batch;
     801         565 :         __lastBucket = bucket;
     802         565 :         __lastTexture = texture;
     803         565 :         success();
     804             : }
     805             : 
     806             : /* Static drawing */
     807           2 : static void upload_static_vertices(struct BLZ_StaticBatch *batch)
     808             : {
     809           2 :         glBindBuffer(GL_ARRAY_BUFFER, batch->buffer.vbo);
     810           4 :         glBufferData(GL_ARRAY_BUFFER,
     811           2 :                                  batch->sprite_count * 4 * sizeof(struct BLZ_Vertex),
     812           2 :                                  batch->vertices, GL_STATIC_DRAW);
     813           2 :         glBindBuffer(GL_ARRAY_BUFFER, 0);
     814           2 :         batch->is_uploaded = BLZ_TRUE;
     815           2 : }
     816             : 
     817           2 : struct BLZ_StaticBatch *BLZ_CreateStatic(
     818             :         const struct BLZ_Texture *texture, int max_sprite_count)
     819             : {
     820           2 :         struct BLZ_StaticBatch *result = malloc(sizeof(struct BLZ_StaticBatch));
     821           2 :         result->texture = texture;
     822           2 :         result->buffer = create_buffer(max_sprite_count, GL_STATIC_DRAW);
     823           2 :         result->is_uploaded = BLZ_FALSE;
     824           2 :         result->sprite_count = 0;
     825           2 :         result->max_sprite_count = max_sprite_count;
     826           2 :         result->vertices = malloc(max_sprite_count * 4 * sizeof(struct BLZ_Vertex));
     827           2 :         return result;
     828             : }
     829             : 
     830           2 : int BLZ_GetOptionsStatic(
     831             :         const struct BLZ_StaticBatch *batch,
     832             :         int *max_sprite_count)
     833             : {
     834           2 :         validate(batch != NULL);
     835           2 :         *max_sprite_count = batch->max_sprite_count;
     836           2 :         success();
     837             : }
     838             : 
     839           2 : int BLZ_FreeBatchStatic(
     840             :         struct BLZ_StaticBatch *batch)
     841             : {
     842           2 :         if (batch == NULL)
     843             :         {
     844           0 :                 success();
     845             :         }
     846           2 :         free(batch->vertices);
     847           2 :         free_buffer(batch->buffer);
     848           2 :         free(batch);
     849           2 :         success();
     850             : }
     851             : 
     852         104 : int BLZ_DrawStatic(
     853             :         struct BLZ_StaticBatch *batch,
     854             :         const struct BLZ_Vector2 position,
     855             :         const struct BLZ_Rectangle *srcRectangle,
     856             :         float rotation,
     857             :         const struct BLZ_Vector2 *origin,
     858             :         const struct BLZ_Vector2 *scale,
     859             :         const struct BLZ_Vector4 color,
     860             :         enum BLZ_SpriteFlip effects)
     861             : {
     862         104 :         struct BLZ_SpriteQuad quad = transform(
     863             :                 batch->texture,
     864             :                 position,
     865             :                 srcRectangle,
     866             :                 rotation,
     867             :                 origin,
     868             :                 scale,
     869             :                 color,
     870             :                 effects);
     871         104 :         return BLZ_LowerDrawStatic(batch, &quad);
     872             : }
     873             : 
     874         104 : int BLZ_LowerDrawStatic(
     875             :         struct BLZ_StaticBatch *batch,
     876             :         const struct BLZ_SpriteQuad *quad)
     877             : {
     878         104 :         if (batch->is_uploaded)
     879             :         {
     880           0 :                 fail("Can't push more sprites to already uploaded static batch");
     881             :         }
     882         104 :         if (batch->sprite_count >= batch->max_sprite_count)
     883             :         {
     884           0 :                 fail("Sprite limit reached - increase limits in BLZ_CreateStatic(...)");
     885             :         }
     886             :         /* set the vertex data */
     887         104 :         memcpy((batch->vertices + batch->sprite_count * 4), quad, sizeof(struct BLZ_SpriteQuad));
     888         104 :         batch->sprite_count++;
     889         104 :         success();
     890             : }
     891             : 
     892             : static GLfloat identityMatrix[16] = {
     893             :         1, 0, 0, 0,
     894             :         0, 1, 0, 0,
     895             :         0, 0, 1, 0,
     896             :         0, 0, 0, 1};
     897             : 
     898             : #define O(y, x) (y + (x << 2))
     899          10 : static void mult_4x4_matrix(const GLfloat *restrict src1, const GLfloat *restrict src2, GLfloat *restrict dest)
     900             : {
     901          10 :         *(dest + O(0, 0)) = (*(src1 + O(0, 0)) * *(src2 + O(0, 0))) + (*(src1 + O(0, 1)) * *(src2 + O(1, 0))) + (*(src1 + O(0, 2)) * *(src2 + O(2, 0))) + (*(src1 + O(0, 3)) * *(src2 + O(3, 0)));
     902          10 :         *(dest + O(0, 1)) = (*(src1 + O(0, 0)) * *(src2 + O(0, 1))) + (*(src1 + O(0, 1)) * *(src2 + O(1, 1))) + (*(src1 + O(0, 2)) * *(src2 + O(2, 1))) + (*(src1 + O(0, 3)) * *(src2 + O(3, 1)));
     903          10 :         *(dest + O(0, 2)) = (*(src1 + O(0, 0)) * *(src2 + O(0, 2))) + (*(src1 + O(0, 1)) * *(src2 + O(1, 2))) + (*(src1 + O(0, 2)) * *(src2 + O(2, 2))) + (*(src1 + O(0, 3)) * *(src2 + O(3, 2)));
     904          10 :         *(dest + O(0, 3)) = (*(src1 + O(0, 0)) * *(src2 + O(0, 3))) + (*(src1 + O(0, 1)) * *(src2 + O(1, 3))) + (*(src1 + O(0, 2)) * *(src2 + O(2, 3))) + (*(src1 + O(0, 3)) * *(src2 + O(3, 3)));
     905          10 :         *(dest + O(1, 0)) = (*(src1 + O(1, 0)) * *(src2 + O(0, 0))) + (*(src1 + O(1, 1)) * *(src2 + O(1, 0))) + (*(src1 + O(1, 2)) * *(src2 + O(2, 0))) + (*(src1 + O(1, 3)) * *(src2 + O(3, 0)));
     906          10 :         *(dest + O(1, 1)) = (*(src1 + O(1, 0)) * *(src2 + O(0, 1))) + (*(src1 + O(1, 1)) * *(src2 + O(1, 1))) + (*(src1 + O(1, 2)) * *(src2 + O(2, 1))) + (*(src1 + O(1, 3)) * *(src2 + O(3, 1)));
     907          10 :         *(dest + O(1, 2)) = (*(src1 + O(1, 0)) * *(src2 + O(0, 2))) + (*(src1 + O(1, 1)) * *(src2 + O(1, 2))) + (*(src1 + O(1, 2)) * *(src2 + O(2, 2))) + (*(src1 + O(1, 3)) * *(src2 + O(3, 2)));
     908          10 :         *(dest + O(1, 3)) = (*(src1 + O(1, 0)) * *(src2 + O(0, 3))) + (*(src1 + O(1, 1)) * *(src2 + O(1, 3))) + (*(src1 + O(1, 2)) * *(src2 + O(2, 3))) + (*(src1 + O(1, 3)) * *(src2 + O(3, 3)));
     909          10 :         *(dest + O(2, 0)) = (*(src1 + O(2, 0)) * *(src2 + O(0, 0))) + (*(src1 + O(2, 1)) * *(src2 + O(1, 0))) + (*(src1 + O(2, 2)) * *(src2 + O(2, 0))) + (*(src1 + O(2, 3)) * *(src2 + O(3, 0)));
     910          10 :         *(dest + O(2, 1)) = (*(src1 + O(2, 0)) * *(src2 + O(0, 1))) + (*(src1 + O(2, 1)) * *(src2 + O(1, 1))) + (*(src1 + O(2, 2)) * *(src2 + O(2, 1))) + (*(src1 + O(2, 3)) * *(src2 + O(3, 1)));
     911          10 :         *(dest + O(2, 2)) = (*(src1 + O(2, 0)) * *(src2 + O(0, 2))) + (*(src1 + O(2, 1)) * *(src2 + O(1, 2))) + (*(src1 + O(2, 2)) * *(src2 + O(2, 2))) + (*(src1 + O(2, 3)) * *(src2 + O(3, 2)));
     912          10 :         *(dest + O(2, 3)) = (*(src1 + O(2, 0)) * *(src2 + O(0, 3))) + (*(src1 + O(2, 1)) * *(src2 + O(1, 3))) + (*(src1 + O(2, 2)) * *(src2 + O(2, 3))) + (*(src1 + O(2, 3)) * *(src2 + O(3, 3)));
     913          10 :         *(dest + O(3, 0)) = (*(src1 + O(3, 0)) * *(src2 + O(0, 0))) + (*(src1 + O(3, 1)) * *(src2 + O(1, 0))) + (*(src1 + O(3, 2)) * *(src2 + O(2, 0))) + (*(src1 + O(3, 3)) * *(src2 + O(3, 0)));
     914          10 :         *(dest + O(3, 1)) = (*(src1 + O(3, 0)) * *(src2 + O(0, 1))) + (*(src1 + O(3, 1)) * *(src2 + O(1, 1))) + (*(src1 + O(3, 2)) * *(src2 + O(2, 1))) + (*(src1 + O(3, 3)) * *(src2 + O(3, 1)));
     915          10 :         *(dest + O(3, 2)) = (*(src1 + O(3, 0)) * *(src2 + O(0, 2))) + (*(src1 + O(3, 1)) * *(src2 + O(1, 2))) + (*(src1 + O(3, 2)) * *(src2 + O(2, 2))) + (*(src1 + O(3, 3)) * *(src2 + O(3, 2)));
     916          10 :         *(dest + O(3, 3)) = (*(src1 + O(3, 0)) * *(src2 + O(0, 3))) + (*(src1 + O(3, 1)) * *(src2 + O(1, 3))) + (*(src1 + O(3, 2)) * *(src2 + O(2, 3))) + (*(src1 + O(3, 3)) * *(src2 + O(3, 3)));
     917          10 : }
     918             : 
     919          10 : int BLZ_PresentStatic(
     920             :         struct BLZ_StaticBatch *batch,
     921             :         const GLfloat *transformMatrix4x4)
     922             : {
     923          10 :         const GLfloat *transform = transformMatrix4x4 != NULL ? transformMatrix4x4 : (GLfloat *)&identityMatrix;
     924             : 
     925             :         GLfloat mvpMatrix[16];
     926          10 :         if (!batch->is_uploaded)
     927             :         {
     928           2 :                 upload_static_vertices(batch);
     929             :         }
     930          10 :         if (SHADER_CURRENT->mvp_param > -1)
     931             :         {
     932          10 :                 mult_4x4_matrix(transform, (GLfloat *)&orthoMatrix, (GLfloat *)&mvpMatrix);
     933          10 :                 set_mvp_matrix((const GLfloat *)&mvpMatrix);
     934             :         }
     935          10 :         bind_tex0(batch->texture->id);
     936          10 :         glBindVertexArray(batch->buffer.vao);
     937          10 :         glDrawElements(GL_TRIANGLES, batch->sprite_count * 6, GL_UNSIGNED_SHORT, (void *)0);
     938          10 :         success();
     939             : }
     940             : 
     941             : /* Immediate drawing */
     942          11 : int BLZ_DrawImmediate(
     943             :         const struct BLZ_Texture *texture,
     944             :         const struct BLZ_Vector2 position,
     945             :         const struct BLZ_Rectangle *srcRectangle,
     946             :         float rotation,
     947             :         const struct BLZ_Vector2 *origin,
     948             :         const struct BLZ_Vector2 *scale,
     949             :         const struct BLZ_Vector4 color,
     950             :         enum BLZ_SpriteFlip effects)
     951             : {
     952          11 :         struct BLZ_SpriteQuad quad = transform(
     953             :                 texture,
     954             :                 position,
     955             :                 srcRectangle,
     956             :                 rotation,
     957             :                 origin,
     958             :                 scale,
     959             :                 color,
     960             :                 effects);
     961          11 :         return BLZ_LowerDrawImmediate(texture->id, &quad);
     962             : }
     963             : 
     964             : const int SIZE_OF_ONE_QUAD = sizeof(struct BLZ_SpriteQuad);
     965          16 : int BLZ_LowerDrawImmediate(
     966             :         GLuint texture,
     967             :         const struct BLZ_SpriteQuad *quad)
     968             : {
     969          16 :         glBindBuffer(GL_ARRAY_BUFFER, immediateBuf.vbo);
     970          16 :         glBufferData(GL_ARRAY_BUFFER, SIZE_OF_ONE_QUAD, quad, GL_STREAM_DRAW);
     971          16 :         glBindBuffer(GL_ARRAY_BUFFER, 0);
     972          16 :         glBindVertexArray(immediateBuf.vao);
     973          16 :         set_mvp_matrix((const GLfloat *)&orthoMatrix);
     974          16 :         bind_tex0(texture);
     975          16 :         glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, (void *)0);
     976          16 :         success();
     977             : }
     978             : 
     979             : /* Textures */
     980          12 : static void fill_texture_info(struct BLZ_Texture *texture)
     981             : {
     982          12 :         glBindTexture(GL_TEXTURE_2D, texture->id);
     983          12 :         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &texture->width);
     984          12 :         glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &texture->height);
     985          12 :         glBindTexture(GL_TEXTURE_2D, 0);
     986          12 : }
     987             : 
     988          12 : struct BLZ_Texture *BLZ_LoadTextureFromFile(
     989             :         const char *filename,
     990             :         enum BLZ_ImageChannels channels,
     991             :         unsigned int texture_id,
     992             :         enum BLZ_ImageFlags flags)
     993             : {
     994             :         struct BLZ_Texture *texture;
     995          12 :         unsigned int id = SOIL_load_OGL_texture(
     996             :                 filename,
     997             :                 channels,
     998             :                 texture_id,
     999             :                 flags);
    1000          12 :         const char *last_result = SOIL_last_result();
    1001          12 :         if (!id)
    1002             :         {
    1003           1 :                 printf("Error: %s\n", last_result);
    1004           1 :                 return NULL;
    1005             :         }
    1006          11 :         texture = malloc(sizeof(struct BLZ_Texture));
    1007          11 :         texture->id = id;
    1008          11 :         fill_texture_info(texture);
    1009          11 :         return texture;
    1010             : }
    1011             : 
    1012           2 : struct BLZ_Texture *BLZ_LoadTextureFromMemory(
    1013             :         const unsigned char *const buffer,
    1014             :         int buffer_length,
    1015             :         enum BLZ_ImageChannels force_channels,
    1016             :         unsigned int texture_id,
    1017             :         enum BLZ_ImageFlags flags)
    1018             : {
    1019             :         struct BLZ_Texture *texture;
    1020             :         int width, height, channels;
    1021           2 :         unsigned char *data = SOIL_load_image_from_memory(
    1022             :                 buffer, buffer_length,
    1023             :                 &width, &height, &channels,
    1024             :                 force_channels);
    1025           2 :         const char *last_result = SOIL_last_result();
    1026           2 :         if (data == NULL)
    1027             :         {
    1028           1 :                 printf("Error: %s\n", last_result);
    1029           1 :                 return NULL;
    1030             :         }
    1031           1 :         texture = malloc(sizeof(struct BLZ_Texture));
    1032           1 :         texture->id = SOIL_create_OGL_texture(
    1033             :                 data, width, height, channels, texture_id, flags);
    1034           1 :         fill_texture_info(texture);
    1035           1 :         return texture;
    1036             : }
    1037             : 
    1038           8 : int BLZ_FreeTexture(struct BLZ_Texture *texture)
    1039             : {
    1040           8 :         if (texture == NULL)
    1041             :         {
    1042           0 :                 success();
    1043             :         }
    1044           8 :         glDeleteTextures(1, &texture->id);
    1045           8 :         free(texture);
    1046           8 :         success();
    1047             : }
    1048             : 
    1049           6 : int BLZ_SaveScreenshot(
    1050             :         const char *filename,
    1051             :         enum BLZ_SaveImageFormat format,
    1052             :         int x, int y,
    1053             :         int width, int height)
    1054             : {
    1055           6 :         return SOIL_save_screenshot(
    1056             :                 filename,
    1057             :                 format, x, y, width, height);
    1058             : }
    1059             : 
    1060             : /* Render targets */
    1061             : static const GLenum DRAW_BUFFERS[1] = {GL_COLOR_ATTACHMENT0};
    1062           1 : struct BLZ_RenderTarget *BLZ_CreateRenderTarget(int width, int height)
    1063             : {
    1064             :         GLuint framebuffer, texture;
    1065             :         struct BLZ_RenderTarget *result;
    1066           1 :         glGenFramebuffers(1, &framebuffer);
    1067           1 :         glGenTextures(1, &texture);
    1068           1 :         null_if_false(framebuffer, "Could not create framebuffer");
    1069           1 :         null_if_false(texture, "Could not create texture for framebuffer");
    1070           1 :         result = malloc(sizeof(struct BLZ_RenderTarget));
    1071           1 :         result->id = framebuffer;
    1072           1 :         result->texture.id = texture;
    1073           1 :         result->texture.width = width;
    1074           1 :         result->texture.height = height;
    1075           1 :         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    1076           1 :         glBindTexture(GL_TEXTURE_2D, texture);
    1077           1 :         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
    1078             :                                  GL_UNSIGNED_BYTE, NULL);
    1079           1 :         BLZ_SetTextureFiltering(&result->texture, NEAREST, NEAREST);
    1080           1 :         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
    1081             :                                                    texture, 0);
    1082           1 :         glDrawBuffers(1, DRAW_BUFFERS);
    1083           1 :         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    1084             :         {
    1085           0 :                 free(result);
    1086           0 :                 fail("The specified framebuffer is not complete");
    1087             :         }
    1088           1 :         glBindFramebuffer(GL_FRAMEBUFFER, 0);
    1089           1 :         return result;
    1090             : }
    1091             : 
    1092           2 : int BLZ_BindRenderTarget(struct BLZ_RenderTarget *target)
    1093             : {
    1094           2 :         glBindFramebuffer(GL_FRAMEBUFFER, target == NULL ? 0 : target->id);
    1095           2 :         success();
    1096             : }
    1097             : 
    1098           1 : int BLZ_FreeRenderTarget(struct BLZ_RenderTarget *target)
    1099             : {
    1100           1 :         if (target == NULL) {
    1101           0 :                 success();
    1102             :         }
    1103           1 :         glDeleteTextures(1, &target->texture.id);
    1104           1 :         glDeleteFramebuffers(1, &target->id);
    1105           1 :         free(target);
    1106           1 :         success();
    1107             : }
    1108             : 
    1109             : /* glUniform shims */
    1110             : /* LCOV_EXCL_START */
    1111             : #define PARAM1(type) type v0
    1112             : #define PARAM2(type) PARAM1(type), type v1
    1113             : #define PARAM3(type) PARAM2(type), type v2
    1114             : #define PARAM4(type) PARAM3(type), type v3
    1115             : #define PASS_PARAM1 v0
    1116             : #define PASS_PARAM2 PASS_PARAM1, v1
    1117             : #define PASS_PARAM3 PASS_PARAM2, v2
    1118             : #define PASS_PARAM4 PASS_PARAM3, v3
    1119             : 
    1120             : #define UNIFORM_VEC(postfix, type, n)                            \
    1121             :         void BLZ_Uniform##n##postfix(GLint location, PARAM##n(type)) \
    1122             :         {                                                            \
    1123             :                 glUniform##n##postfix(location, PASS_PARAM##n);          \
    1124             :         }
    1125             : 
    1126             : #define UNIFORM_MAT(size)                                             \
    1127             :         void BLZ_UniformMatrix##size##fv(                                 \
    1128             :                 GLint location,                                               \
    1129             :                 GLsizei count,                                                \
    1130             :                 GLboolean transpose,                                          \
    1131             :                 const GLfloat *value)                                         \
    1132             :         {                                                                 \
    1133             :                 glUniformMatrix##size##fv(location, count, transpose, value); \
    1134             :         }
    1135             : 
    1136             : UNIFORM_VEC(f, GLfloat, 1)
    1137             : UNIFORM_VEC(f, GLfloat, 2)
    1138             : UNIFORM_VEC(f, GLfloat, 3)
    1139             : UNIFORM_VEC(f, GLfloat, 4)
    1140             : UNIFORM_VEC(i, GLint, 1)
    1141             : UNIFORM_VEC(i, GLint, 2)
    1142             : UNIFORM_VEC(i, GLint, 3)
    1143             : UNIFORM_VEC(i, GLint, 4)
    1144             : UNIFORM_VEC(ui, GLuint, 1)
    1145             : UNIFORM_VEC(ui, GLuint, 2)
    1146             : UNIFORM_VEC(ui, GLuint, 3)
    1147             : UNIFORM_VEC(ui, GLuint, 4)
    1148             : UNIFORM_MAT(2)
    1149             : UNIFORM_MAT(3)
    1150             : UNIFORM_MAT(4)
    1151             : UNIFORM_MAT(2x3)
    1152             : UNIFORM_MAT(3x2)
    1153             : UNIFORM_MAT(2x4)
    1154             : UNIFORM_MAT(4x2)
    1155             : UNIFORM_MAT(3x4)
    1156             : UNIFORM_MAT(4x3)
    1157             : /* LCOV_EXCL_STOP */

Generated by: LCOV version 1.11