void tealeaf_context_update_shader(context_2d *ctx, unsigned int shader_type, bool force) { int width = ctx->backing_width; int height = ctx->backing_height; tealeaf_shader *shader = &global_shaders[shader_type]; matrix_4x4 m; matrix_3x3 *proj; if (shader->last_width != width || shader->last_height != height || force) { //Need to copy the 3x3 projection matrix into a 4x4 matrix since that //is the form used in the shader. Note that in the future the shaders //could be changed to use 3x3 matrices rather than 4x4 proj = &ctx->proj_matrix; m.m00 = proj->m00; m.m01 = proj->m01; m.m02 = 0; m.m03 = proj->m02; m.m10 = proj->m10; m.m11 = proj->m11; m.m12 = 0; m.m13 = proj->m12; m.m20 = 0; m.m21 = 0; m.m22 = 1; m.m23 = 0; m.m30 = proj->m20; m.m31 = proj->m21; m.m32 = 0; m.m33 = proj->m22; GLTRACE(glUseProgram(shader->program)); GLTRACE(glUniformMatrix4fv(shader->proj_matrix, 1, false, (float *) &m)); shader->last_width = width; shader->last_height = height; GLTRACE(glUseProgram(global_shaders[current_shader].program)); } }
/** * @name enable_scissor * @brief enables the use of glScissors using proprties from the given context * @param ctx - (context_2d *) * @retval NONE */ void enable_scissor(context_2d *ctx) { rect_2d *bounds = GET_CLIPPING_BOUNDS(ctx); if (rect_2d_equals(&last_scissor_rect, bounds)) { return; } draw_textures_flush(); last_scissor_rect.x = bounds->x; last_scissor_rect.y = bounds->y; last_scissor_rect.width = bounds->width; last_scissor_rect.height = bounds->height; GLTRACE(glScissor((int) bounds->x, (int) bounds->y, (int) bounds->width, (int) bounds->height)); GLTRACE(glEnable(GL_SCISSOR_TEST)); }
/** * @name tealeaf_canvas_bind_texture_buffer * @brief binds the given context's texture backing to gl to draw to * @param ctx - (context_2d *) pointer to the context to bind * @retval NONE */ void tealeaf_canvas_bind_texture_buffer(context_2d *ctx) { texture_2d *tex = texture_manager_get_texture(texture_manager_get(), ctx->url); if (!tex) { return; } GLTRACE(glBindTexture(GL_TEXTURE_2D, tex->name)); GLTRACE(glFinish()); GLTRACE(glBindFramebuffer(GL_FRAMEBUFFER, canvas.offscreen_framebuffer)); GLTRACE(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex->name, 0)); canvas.framebuffer_width = tex->originalWidth; canvas.framebuffer_height = tex->originalHeight; canvas.framebuffer_offset_bottom = tex->height - tex->originalHeight; }
/** * @name tealeaf_context_update_viewport * @brief updates the gl viewport using the context's properties * @param ctx - (context_2d *) context to update the viewport by * @param force - (bool) force an updating of shaders * @retval NONE */ void tealeaf_context_update_viewport(context_2d *ctx, bool force) { tealeaf_context_update_shader(ctx, DRAWING_SHADER, force); tealeaf_context_update_shader(ctx, PRIMARY_SHADER, force); tealeaf_context_update_shader(ctx, FILL_RECT_SHADER, force); tealeaf_context_update_shader(ctx, LINEAR_ADD_SHADER, force); GLTRACE(glViewport(0, 0, ctx->backing_width, ctx->backing_height)); }
/** * @name tealeaf_canvas_resize * @brief resize's the onscreen canvas * @param w - (int) width to resize to * @param h - (int) height to resize to * @retval NONE */ void tealeaf_canvas_resize(int w, int h) { LOG("{canvas} Resizing screen to (%d, %d)", w, h); context_2d *ctx = canvas.onscreen_ctx; context_2d_resize(ctx, w, h); if (canvas.active_ctx == canvas.onscreen_ctx) { tealeaf_canvas_bind_render_buffer(ctx); tealeaf_context_update_viewport(ctx, true); context_2d_clear(ctx); } GLTRACE(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GLTRACE(glEnable(GL_BLEND)); config_set_screen_width(w); config_set_screen_height(h); canvas.should_resize = true; }
/** * @name context_2d_fillRect * @brief fills a rectangle on the given context using given options * @param ctx - (context_2d *) context to fill a rectangle on * @param rect - (const rect_2d *) rect to be filled * @param color - (const rgba *) color to fill with * @param composite_op - deprecated * @retval NONE */ void context_2d_fillRect(context_2d *ctx, const rect_2d *rect, const rgba *color) { if (use_single_shader) { return; } draw_textures_flush(); context_2d_bind(ctx); tealeaf_shaders_bind(FILL_RECT_SHADER); apply_composite_operation(ctx->globalCompositeOperation[ctx->mvp]); rect_2d_vertices in, out; rect_2d_to_rect_2d_vertices(rect, &in); matrix_3x3_multiply_m_r_r(GET_MODEL_VIEW_MATRIX(ctx), &in, &out); float alpha = color->a * ctx->globalAlpha[ctx->mvp]; // TODO: will pre-multiplied alpha cause a loss-of-precision in color for filling rectangles? GLTRACE(glUniform4f(global_shaders[FILL_RECT_SHADER].draw_color, alpha * color->r, alpha * color->g, alpha * color->b, alpha)); GLTRACE(glVertexAttribPointer(global_shaders[FILL_RECT_SHADER].vertex_coords, 2, GL_FLOAT, GL_FALSE, 0, &out)); GLTRACE(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); tealeaf_shaders_bind(PRIMARY_SHADER); }
/** * @name context_2d_clearRect * @brief clears the given rect on the given context * @param ctx - (context_2d *) context to clear a rect from * @param rect - (const rect_2d *) rect to clear from the context * @retval NONE */ void context_2d_clearRect(context_2d *ctx, const rect_2d *rect) { draw_textures_flush(); context_2d_bind(ctx); tealeaf_shaders_bind(PRIMARY_SHADER); // Draw a rectangle using triangle strip: // (0,1)-(2,3)-(4,5) and (2,3)-(4,5)-(6,7) // // With coordinates: // 4,5 - 6,7 // | \ | // 0,1 - 2,3 GLfloat v[8]; matrix_3x3_multiply(GET_MODEL_VIEW_MATRIX(ctx), rect, (float *)&v[4], (float *)&v[5], (float *)&v[6], (float *)&v[7], (float *)&v[2], (float *)&v[3], (float *)&v[0], (float *)&v[1]); GLTRACE(glBlendFunc(GL_ONE, GL_ZERO)); GLTRACE(glUniform4f(global_shaders[PRIMARY_SHADER].draw_color, 0, 0, 0, 0)); // set color to 0 GLTRACE(glVertexAttribPointer(global_shaders[PRIMARY_SHADER].vertex_coords, 2, GL_FLOAT, GL_FALSE, 0, v)); GLTRACE(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); GLTRACE(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); }
/** * @name tealeaf_canvas_init * @brief initilizes the tealeaf canvas object * @param framebuffer_name - (int) gl id of the onscreen framebuffer * @retval NONE */ void tealeaf_canvas_init(int framebuffer_name) { LOG("{canvas} Initializing Canvas"); int width = config_get_screen_width(); int height = config_get_screen_height(); GLuint offscreen_buffer_name; GLTRACE(glGenFramebuffers(1, &offscreen_buffer_name)); canvas.offscreen_framebuffer = offscreen_buffer_name; canvas.view_framebuffer = framebuffer_name; canvas.onscreen_ctx = context_2d_init(&canvas, "onscreen", -1, true); canvas.onscreen_ctx->width = width; canvas.onscreen_ctx->height = height; canvas.active_ctx = 0; // TODO: should_resize is not respected on iOS tealeaf_canvas_context_2d_bind(canvas.onscreen_ctx); }
/** * @name context_2d_draw_point_sprites * @brief Draws pointsprites using the given options (in batch along a line) * @param ctx - (context_2d *) context to draw to * @param url - (const char *) name of the texture to draw from * @param point_size - (float) point sprite size * @param step_size - (float) step size to take between drawn pointsprites * @param color - (rgba *) color to draw with * @param x1 - (float) starting x-coordinate to draw along * @param y1 - (float) starting y-coordinate to draw along * @param x2 - (float) ending x-coordinate to draw along * @param y2 - (float) ending y-coordinate to draw along * @retval NONE */ void context_2d_draw_point_sprites(context_2d *ctx, const char *url, float point_size, float step_size, rgba *color, float x1, float y1, float x2, float y2) { draw_textures_flush(); context_2d_bind(ctx); texture_2d *tex = texture_manager_load_texture(texture_manager_get(), url); // If texture is not finished loading, if (!tex || !tex->loaded) { return; } static GLfloat *vertex_buffer = NULL; static unsigned int vertex_max = 64; tealeaf_shaders_bind(DRAWING_SHADER); matrix_3x3_multiply_m_f_f_f_f(GET_MODEL_VIEW_MATRIX(ctx), x1, y1, &x1, &y1); matrix_3x3_multiply_m_f_f_f_f(GET_MODEL_VIEW_MATRIX(ctx), x2, y2, &x2, &y2); // Allocate vertex array buffer if (vertex_buffer == NULL) { vertex_buffer = malloc(vertex_max * 2 * sizeof(GLfloat)); } // Add points to the buffer so there are drawing points every X pixels unsigned int count = ceilf(sqrtf((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) / step_size); if (count < 1) { count = 1; } unsigned int vertex_count = 0; unsigned int i; for (i = 0; i < count; ++i) { if (vertex_count == vertex_max) { vertex_max = 2 * vertex_max; vertex_buffer = realloc(vertex_buffer, vertex_max * 2 * sizeof(GLfloat)); } vertex_buffer[2 * vertex_count + 0] = x1 + (x2 - x1) * ((GLfloat)i / (GLfloat)count); vertex_buffer[2 * vertex_count + 1] = y1 + (y2 - y1) * ((GLfloat)i / (GLfloat)count); vertex_count += 1; } GLTRACE(glActiveTexture(GL_TEXTURE0)); GLTRACE(glBindTexture(GL_TEXTURE_2D, tex->name)); GLTRACE(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GLTRACE(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); GLTRACE(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); GLTRACE(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLTRACE(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); // Render the vertex array GLTRACE(glUniform1f(global_shaders[DRAWING_SHADER].point_size, point_size)); GLTRACE(glVertexAttribPointer(global_shaders[DRAWING_SHADER].vertex_coords, 2, GL_FLOAT, GL_FALSE, 0, (float *) vertex_buffer)); float alpha = color->a * ctx->globalAlpha[ctx->mvp]; GLTRACE(glUniform4f(global_shaders[DRAWING_SHADER].draw_color, alpha * color->r, alpha * color->g, alpha * color->b, alpha)); GLTRACE(glDrawArrays(GL_POINTS, 0, vertex_count)); tealeaf_shaders_bind(PRIMARY_SHADER); }
/** * @name disable_scissor * @brief disables the use of glScissors * @param ctx - deprecated * @retval NONE */ void disable_scissor(context_2d *ctx) { draw_textures_flush(); last_scissor_rect.x = last_scissor_rect.y = 0; last_scissor_rect.width = last_scissor_rect.height = -1; GLTRACE(glDisable(GL_SCISSOR_TEST)); }
/** * @name tealeaf_canvas_bind_render_buffer * @brief bind's the render buffer and set's it's height / width to the given context's props * @param ctx - (context_2d *) pointer to the context to use the width / height from * @retval NONE */ void tealeaf_canvas_bind_render_buffer(context_2d *ctx) { GLTRACE(glBindFramebuffer(GL_FRAMEBUFFER, canvas.view_framebuffer)); canvas.framebuffer_width = ctx->width; canvas.framebuffer_height = ctx->height; canvas.framebuffer_offset_bottom = 0; }