#include #include #include #include Renderer::Renderer(SDL_Window *window) : m_Window(window), m_Canvas{} { } bool Renderer::Init() { GLenum err = glewInit(); if (err) { printf("glewInit() error\n"); return false; } int32_t window_width; int32_t window_height; if (!SDL_GetWindowSize(m_Window, &window_width, &window_height)) { printf("Error: SDL_GetWindowSize: %s\n", SDL_GetError()); return false; } m_Canvas.rshift = 0; m_Canvas.gshift = 8; m_Canvas.bshift = 16; m_Canvas.ashift = 24; ResizeCanvas(window_width, window_height); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); glGenTextures(1, &m_GlTexId); glBindTexture(GL_TEXTURE_2D, m_GlTexId); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); return true; } void Renderer::ResizeCanvas(int32_t w, int32_t h) { size_t realloc_size = (size_t)(w * h) * sizeof(m_Canvas.pixels[0]); void *realloc_data = realloc(m_Canvas.pixels, realloc_size); if (!realloc_data) { printf("could not resize offscreen buffer\n"); return; } m_Canvas.w = w; m_Canvas.h = h; m_Canvas.pixels = (uint32_t*)realloc_data; } void Renderer::Draw(RenderGroup& rgroup) { rgroup.Sort(); REntity_Rectangle clear_rect = { REntityType_Rectangle, 0, 0, (float)(m_Canvas.w-1), (float)(m_Canvas.h-1), 0.0f, rgroup.m_ClearColor }; DrawRectangle(rgroup, clear_rect); float z = -1; for (RSortEntry sort_entry : rgroup.m_RSortEntries) { if (sort_entry.z >= z) { z = sort_entry.z; } REntity& entity = rgroup.m_REntities[sort_entry.entity_index]; switch (entity.type) { case REntityType_Rectangle: { DrawRectangle(rgroup, entity.rect); } break; case REntityType_Bitmap: { Color color = {0.0f, 0.0f, 0.0f, 1.0f}; DrawMonoBitmap(entity.bitmap, color); } break; default:; } } glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_GlTexId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Canvas.w, m_Canvas.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_Canvas.pixels); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(-1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f( 1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f( 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex2f(-1.0f, 1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D, 0); } void Renderer::DrawRectangle(RenderGroup& rgroup, REntity_Rectangle& rect) { int32_t xmin = WorldXToScreenX(rgroup, rect.x0); int32_t ymin = WorldYToScreenY(rgroup, rect.y0); int32_t xmax = WorldXToScreenX(rgroup, rect.x1); int32_t ymax = WorldYToScreenY(rgroup, rect.y1); if (xmin < 0) { xmin = 0; } if (ymin < 0) { ymin = 0; } if (xmax >= m_Canvas.w) { xmax = m_Canvas.w - 1; } if (ymax >= m_Canvas.h) { ymax = m_Canvas.h - 1; } uint32_t rshift = m_Canvas.rshift; uint32_t gshift = m_Canvas.gshift; uint32_t bshift = m_Canvas.bshift; for (int32_t y = ymin; y <= ymax; ++y) { uint32_t *pixel = m_Canvas.pixels + y * m_Canvas.w + xmin; for (int32_t x = xmin; x <= xmax; ++x) { uint32_t r = (uint32_t)(rect.color.r * 255.0f); uint32_t g = (uint32_t)(rect.color.g * 255.0f); uint32_t b = (uint32_t)(rect.color.b * 255.0f); uint32_t val = r << rshift | g << gshift | b << bshift; *pixel++ = val; } } } void Renderer::DrawMonoBitmap(REntity_Bitmap& bitmap, Color color) { int32_t x0 = (int32_t)bitmap.x; int32_t y0 = (int32_t)bitmap.y; int32_t x1 = (int32_t)bitmap.x + bitmap.w - 1; int32_t y1 = (int32_t)bitmap.y + bitmap.h - 1; int32_t cut_left = 0; int32_t cut_bot = 0; if (x0 < 0) { cut_left = x0; x0 = 0; } if (y0 < 0) { cut_bot = y0; y0 = 0; } if (x1 >= m_Canvas.w) { x1 = m_Canvas.w - 1; } if (y1 >= m_Canvas.h) { y1 = m_Canvas.h - 1; } uint32_t rshift = m_Canvas.rshift; uint32_t gshift = m_Canvas.gshift; uint32_t bshift = m_Canvas.bshift; uint8_t* grayscale = (uint8_t*)bitmap.data + (-cut_bot * bitmap.w) + (-cut_left); uint32_t* rgba = m_Canvas.pixels + y0 * m_Canvas.w + x0; for (int32_t y = y0; y <= y1; y++) { uint8_t *grayscale_pixels = grayscale; uint32_t *rgba_pixels = rgba; for (int32_t x = x0; x <= x1; x++) { float alpha = *grayscale_pixels / 255.0f; // Todo: we do not want to blend with existing color! uint32_t rgba_result = *rgba_pixels; float r0 = (rgba_result >> rshift) & 0xff; float g0 = (rgba_result >> gshift) & 0xff; float b0 = (rgba_result >> bshift) & 0xff; float r1 = r0 + (color.r - r0)*alpha; float g1 = g0 + (color.g - g0)*alpha; float b1 = b0 + (color.b - b0)*alpha; rgba_result = (uint32_t)r1 << rshift | (uint32_t)g1 << gshift | (uint32_t)b1 << bshift; *rgba_pixels = rgba_result; grayscale_pixels++; rgba_pixels++; } grayscale += bitmap.w; rgba += m_Canvas.w; } } int32_t Renderer::WorldXToScreenX(RenderGroup& rgroup, float x) { float xscreen = (x / rgroup.m_CameraWidth) * (float)rgroup.m_ScreenWidth; return (int32_t)xscreen; } int32_t Renderer::WorldYToScreenY(RenderGroup& rgroup, float y) { float yscreen = (y / rgroup.m_CameraHeight) * (float)rgroup.m_ScreenHeight; return (int32_t)yscreen; }