Skip to content

Instantly share code, notes, and snippets.

@RealNeGate
Last active November 6, 2022 06:59
Show Gist options
  • Save RealNeGate/62efdbd9175f63840f9bad9fe6ea9d3d to your computer and use it in GitHub Desktop.
Save RealNeGate/62efdbd9175f63840f9bad9fe6ea9d3d to your computer and use it in GitHub Desktop.
// Reject modernity, return to wrinkly ass GL shit
#if !defined(_WIN32)
#error "Fuck you"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#define _USE_MATH_DEFINES
#include <math.h>
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <GL/gl.h>
#define STB_TRUETYPE_IMPLEMENTATION
#include "stb_truetype.h"
#include "dyn_array.h"
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
// no need for do while crap if it's an expression :p
// it's basically an assert but it doesn't get vaporized
#define CHECK(x) ((x) ? (void)0 : abort())
enum {
BITMAP_W = 512, BITMAP_H = 512
};
typedef struct {
uint64_t user_id; // 0 is self
const char* content;
} Message;
typedef struct {
float x, y;
} Point;
typedef struct {
float x, y, w, h;
} Rect;
typedef struct {
float curr, target;
} Scrollbar;
static DynArray(Message) messages;
static float font_size;
static unsigned char ttf_buffer[1<<20];
static unsigned char temp_bitmap[BITMAP_W * BITMAP_H];
static stbtt_packedchar cdata[128];
static GLuint ftex;
static int mouse_scroll;
static Point mouse_pos;
static void update_scroll(Scrollbar* s, float dt, float min, float max) {
if (s->target > max) s->target = max;
if (s->target < min) s->target = min;
s->curr += (s->target - s->curr) * (1.0f - expf(-10.0f * dt));
}
static bool rect_contains(const Rect* a, const Point* b) {
return (b->x >= a->x && b->y >= a->y && b->x < (a->x + a->w) && b->y < (a->y + a->h));
}
static LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) {
if (msg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
} else if (msg == WM_MOUSEWHEEL) {
mouse_scroll = GET_WHEEL_DELTA_WPARAM(wparam) < 0 ? -1 : 1;
return 0;
}
return DefWindowProcW(wnd, msg, wparam, lparam);
}
static void set_color(void fn(float, float, float, float), uint32_t color) {
fn(
((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f,
((color >> 0) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f
);
}
static void draw_rect(uint32_t color, float x, float y, float w, float h) {
float x1 = x + w;
float y1 = y + h;
glBegin(GL_QUADS);
set_color(glColor4f, color);
glVertex2f(x, y), glVertex2f(x1, y);
glVertex2f(x1, y1), glVertex2f(x, y1);
glEnd();
}
static void draw_text(uint32_t color, float x, float y, const char* text) {
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, ftex);
glBegin(GL_QUADS);
set_color(glColor4f, color);
float xx = x, yy = 0.0f;
for (; *text; text++) {
if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q;
stbtt_GetPackedQuad(cdata, BITMAP_W, BITMAP_H, *text, &xx, &yy, &q, 1);
q.x0 = ceilf(q.x0);
q.x1 = ceilf(q.x1);
glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,y-q.y0);
glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,y-q.y0);
glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,y-q.y1);
glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,y-q.y1);
}
}
glEnd();
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}
static void draw_circle(uint32_t color, float x, float y, float w, float h, float angle_start, float angle_range) {
enum { RINGS = 32 };
uint32_t fade_end = (color & ~0xFF000000) | 0x20000000;
float radius_x = w*0.5f, center_x = x + radius_x;
float radius_y = h*0.5f, center_y = y + radius_y;
float step = angle_range / RINGS;
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// draw ring around the circle that's fading so we can pretend there's smooth edges
glBegin(GL_TRIANGLE_STRIP);
for (int i = 0; i < RINGS + 1; i++) {
float rad = angle_start + (i*step), c = cosf(rad), s = sinf(rad);
set_color(glColor4f, color);
glVertex2f(center_x + c*radius_x*1.0f, center_y + s*radius_y*1.0f);
set_color(glColor4f, fade_end);
glVertex2f(center_x + c*(radius_x+1.0f), center_y + s*(radius_y+1.0f));
}
glEnd();
glBegin(GL_TRIANGLE_FAN);
set_color(glColor4f, color);
glVertex2f(center_x, center_y);
for (int i = 0; i < RINGS + 1; i++) {
float rad = angle_start + (i*step), c = cosf(rad), s = sinf(rad);
glVertex2f(center_x + c*radius_x, center_y + s*radius_y);
}
glEnd();
glDisable(GL_BLEND);
}
static void draw_rounded_rect(uint32_t color, float x, float y, float w, float h) {
float corner = h;
draw_circle(color, x, y + 1.0f, corner, corner - 2.0f, 0.5f*M_PI, M_PI);
draw_rect(color, x + corner*0.5f, y, w - corner, corner);
draw_circle(color, x + w - corner, y + 1.0f, corner, corner - 2.0f, 1.5f*M_PI, M_PI);
}
static float measure_text_w(const char* text) {
float xx = 0.0f, yy = 0.0f;
for (; *text; text++) {
if (*text >= 32 && *text < 128) {
stbtt_aligned_quad q;
stbtt_GetPackedQuad(cdata, BITMAP_W, BITMAP_H, *text, &xx, &yy, &q, 1);
}
}
return xx;
}
static void draw_text_button(uint32_t color, float x, float y, float h, const char* text) {
float text_w = measure_text_w(text);
float w = text_w + 20.0f, corner = h * 0.5f;
// rounded rect
draw_circle(color, x, y + 1.0f, corner * 2.0f, (corner*2.0f) - 2.0f, 0.5f*M_PI, M_PI);
draw_rect(color, x + corner, y, w, h);
draw_circle(color, x + w, y + 1.0f, corner * 2.0f, (corner*2.0f) - 2.0f, 1.5f*M_PI, M_PI);
draw_text(0xFFFFFFFF, x + corner + 10.0f, y + (corner - 8.0f), text);
}
static void draw_circle_text_button(uint32_t color, float x, float y, float diameter, const char* text) {
float text_w = measure_text_w(text);
draw_circle(color, x, y, diameter, diameter, 0.0f, 2.0f*M_PI);
draw_text(0xFFFFFFFF, x + diameter*0.5f - text_w*0.5f, y + diameter*0.5f - font_size*0.25f, text);
}
int main() {
WNDCLASSEXW wc = {
.cbSize = sizeof(wc),
.lpfnWndProc = WindowProc,
.hInstance = GetModuleHandleA(NULL),
.lpszClassName = L"MINE",
.hCursor = LoadCursor(NULL, IDC_ARROW),
};
CHECK(RegisterClassExW(&wc));
HWND window = CreateWindowExW(
WS_EX_APPWINDOW, L"MINE", L"Da square", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720, NULL, NULL, wc.hInstance, NULL
);
CHECK(window);
ShowWindow(window, SW_SHOW);
SetFocus(window);
// WGL shit
HDC dc = GetDC(window);
PIXELFORMATDESCRIPTOR pfd = {
.nSize = sizeof(pfd),
.nVersion = 1,
.iPixelType = PFD_TYPE_RGBA,
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
.cColorBits = 32,
.cAlphaBits = 8,
.iLayerType = PFD_MAIN_PLANE
};
int pixel_format;
CHECK(pixel_format = ChoosePixelFormat(dc, &pfd));
CHECK(SetPixelFormat(dc, pixel_format, &pfd));
HGLRC glctx;
CHECK(glctx = wglCreateContext(dc));
CHECK(wglMakeCurrent(dc, glctx));
set_color(glClearColor, 0xFF1C1B1F);
{
font_size = 36.0;
FILE* f = fopen("assets/OpenSans-SemiBold.ttf", "rb");
fread(ttf_buffer, 1, 1<<20, f);
stbtt_pack_context pc;
stbtt_PackBegin(&pc, temp_bitmap, BITMAP_W, BITMAP_H, 0, 1, NULL);
stbtt_PackSetOversampling(&pc, 1, 1);
stbtt_PackFontRange(&pc, ttf_buffer, 0, font_size, 32, 96, cdata + 32);
stbtt_PackEnd(&pc);
// can free ttf_buffer at this point
glGenTextures(1, &ftex);
glBindTexture(GL_TEXTURE_2D, ftex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
// can free temp_bitmap at this point
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
fclose(f);
}
messages = dyn_array_create(Message);
dyn_array_put(messages, (Message){ 0, "Hey!" });
dyn_array_put(messages, (Message){ 0, "Ok really not gonna answer" });
dyn_array_put(messages, (Message){ 1, "Fuck you" });
dyn_array_put(messages, (Message){ 1, "I hate you and your grandma" });
dyn_array_put(messages, (Message){ 0, "My grandma's dead" });
dyn_array_put(messages, (Message){ 1, "And?" });
LARGE_INTEGER now, start_time, frequency;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start_time);
for (;;) {
// da pump
mouse_scroll = 0;
MSG msg;
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) goto exit;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
// Get time delta
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
float dt = (now.QuadPart - start_time.QuadPart) / (double) frequency.QuadPart;
start_time = now;
RECT rect;
GetClientRect(window, &rect);
int w = rect.right - rect.left, h = rect.bottom - rect.top;
POINT mp;
GetCursorPos(&mp);
ScreenToClient(window, &mp);
mouse_pos = (Point){ mp.x, h - mp.y };
glViewport(0, 0, w, h);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, w, 0, h, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#if 1
float dp = 96.0f / 160.0f;
// User list
float split = w * 0.2f;
{
static Scrollbar user_list_scroll;
update_scroll(&user_list_scroll, dt, 0.0f, 300.0f);
Rect r = { 0.0f, 0.0f, split, h };
glPushMatrix();
glTranslatef(0.0f, user_list_scroll.curr, 0.0f);
float line_height = 104.0f*dp;
for (int i = 0; i < 15; i++) {
float top = (h - 16.0f*dp) - (line_height * i);
float x = 16.0f*dp;
char ch[2] = { 'A' + i };
draw_circle_text_button(0xFF004787, x, top - 72.0f*dp, 72.0f*dp, ch);
{
float x2 = x + (72.0f + 16.0f)*dp;
draw_rect(0xFF222426, x2, top - (36.0f - 8.0f)*dp, 168.0f*dp, 24.0f*dp);
draw_rect(0xFF222426, x2, top - (72.0f - 8.0f)*dp, 72.0f*dp, 24.0f*dp);
}
// draw separator
draw_rect(0xFF43474E, 16.0f*dp, top - 88.0f*dp, split - (32.0f*dp), 1.0f);
}
glPopMatrix();
if (rect_contains(&r, &mouse_pos)) {
// user list hot now, we can scroll
user_list_scroll.target -= mouse_scroll*100.0f;
// draw_rect(0xFF00FF00, r.x, r.y, r.w, r.h);
}
}
draw_rect(0xFF43474E, split, 0.0f, 1.0f, h);
// Message view
{
float msg_view_x = split + 32.0f*dp;
float msg_view_y = (32.0f+96.0f+32.0f)*dp;
float msg_view_w = (w - 32.0f*dp) - msg_view_x;
float msg_view_h = (h - 32.0f*dp) - msg_view_y;
// draw_rect(0xFFFF0000, msg_view_x, msg_view_y, msg_view_w, msg_view_h);
float msg_h = 104.0f*dp;
float right = msg_view_x + msg_view_w;
float current = msg_view_y + msg_view_h;
dyn_array_for(i, messages) {
Message* msg = &messages[i];
float msg_w = measure_text_w(msg->content) + 20.0f + (88.0f*dp);
float msg_x = msg->user_id == 0 ? msg_view_x : (right - msg_w);
current -= msg_h;
draw_text_button(0xFF004787, msg_x, current, 88.0f*dp, msg->content);
// draw_rect(0xFF0000FF, msg_x, current, msg_w, font_size*0.5f);
}
}
// Text field and send button
{
float field_w = w - (split + 96.0f*dp + 96.0f*dp);
float x = split + 32.0f*dp;
draw_rounded_rect(0xFF222426, x, 32.0f*dp, field_w, 96.0f*dp);
x += field_w + (32.0f*dp);
draw_circle(0xFF004787, x, 32.0f*dp, 96.0f*dp, 96.0f*dp, 0.0f, 2.0f*M_PI);
}
#else
draw_rect(0xFFE6E1E5, 0.0f, 0.0f, 400.0f, 200.0f);
draw_text(0xFF000000, 20.0f, 20.0f, "Hello, World!");
draw_circle(0xFF004A77, 500.0f, 500.0f, 100.0f, 100.0f, 0.0f, 2.0f * M_PI);
draw_rounded_rect(0xFF004A77, 700.0f, 300.0f, 103.0f, 43.0f);
draw_text_button(0xFF386A20, 700.0f, 350.0f, "Enabled");
for (int i = 0; i < 4; i++) {
draw_circle_text_button(0xFF6750A4, 950.0f, 300.0f + (i * 56.0f), 43.0f, "A");
}
#endif
// draw_circle(0xFF6750A4, mouse_pos.x - 5.0f, mouse_pos.y - 5.0f, 10.0f, 10.0f, 0.0f, 2.0f * M_PI);
SwapBuffers(dc);
}
exit:
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment