2025-02-07 17:55:46 -06:00

503 lines
12 KiB
C++

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <math.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <SDL3/SDL.h>
#include "math/vec3.hpp"
#include "math/vec4.hpp"
#include "math/mat3x3.hpp"
#include "math/mat4x4.hpp"
struct glyph {
int32_t width;
int32_t height;
int32_t horiBearingX;
int32_t horiBearingY;
int32_t horiAdvance;
SDL_Texture * texture;
};
static struct glyph glyphs[0x80 - 0x20] = {0};
int32_t face_height;
int
load_outline_char(SDL_Renderer * renderer,
const FT_Face face,
const FT_Int32 load_flags,
const FT_Render_Mode render_mode,
const FT_ULong char_code)
{
FT_Error error;
FT_UInt glyph_index = FT_Get_Char_Index(face, char_code);
error = FT_Load_Glyph(face, glyph_index, load_flags);
if (error) {
printf("FT_Load_Glyph %s\n", FT_Error_String(error));
return -1;
}
error = FT_Render_Glyph(face->glyph, render_mode);
if (error) {
printf("FT_Render_Glyph %s\n", FT_Error_String(error));
return -1;
}
struct glyph * glyph = &glyphs[char_code - 0x20];
glyph->width = face->glyph->bitmap.width;
glyph->height = face->glyph->bitmap.rows;
glyph->horiBearingX = face->glyph->metrics.horiBearingX;
glyph->horiBearingY = face->glyph->metrics.horiBearingY;
glyph->horiAdvance = face->glyph->metrics.horiAdvance;
if (face->glyph->bitmap.pitch != 0) {
SDL_Surface * surface = SDL_CreateSurface(face->glyph->bitmap.width,
face->glyph->bitmap.rows,
SDL_PIXELFORMAT_RGBA8888);
SDL_LockSurface(surface);
for (unsigned int y = 0; y < face->glyph->bitmap.rows; y++) {
for (unsigned int x = 0; x < face->glyph->bitmap.width; x++) {
int s_ix = y * face->glyph->bitmap.pitch + x;
int d_ix = y * surface->pitch + x * 4;
uint8_t gray = face->glyph->bitmap.buffer[s_ix];
((uint8_t *)surface->pixels)[d_ix + 0] = gray;
((uint8_t *)surface->pixels)[d_ix + 1] = gray;
((uint8_t *)surface->pixels)[d_ix + 2] = gray;
((uint8_t *)surface->pixels)[d_ix + 3] = 255;
}
}
SDL_UnlockSurface(surface);
if (glyph->texture != NULL)
SDL_DestroyTexture(glyph->texture);
glyph->texture = SDL_CreateTextureFromSurface(renderer, surface);
if (glyph->texture == NULL) {
printf("%s\n", SDL_GetError());
}
assert(glyph->texture != NULL);
SDL_DestroySurface(surface);
}
return 0;
}
int load_font(SDL_Renderer * renderer, int font_size)
{
FT_Library library;
FT_Face face;
FT_Error error;
error = FT_Init_FreeType(&library);
if (error) {
printf("FT_Init_FreeType\n");
return -1;
}
error = FT_New_Face(library, "/home/bilbo/timer/DejaVuSansMono.ttf", 0, &face);
if (error) {
printf("FT_New_Face\n");
return -1;
}
error = FT_Set_Pixel_Sizes(face, 0, font_size);
if (error) {
printf("FT_Set_Pixel_Sizes: %s %d\n", FT_Error_String(error), error);
return -1;
}
for (int char_code = 0x20; char_code <= 0x7f; char_code++) {
load_outline_char(renderer, face, FT_LOAD_DEFAULT, FT_RENDER_MODE_NORMAL, char_code);
}
printf("loaded size %ld\n", face->size->metrics.height >> 6);
face_height = face->size->metrics.height;
return 0;
}
int32_t render_text(SDL_Renderer * renderer, int32_t x, int32_t y, const char * s, int length)
{
int32_t x_advance = x;
int32_t y_advance = y;
for (int i = 0; i < length; i++) {
char c = s[i];
struct glyph * glyph = &glyphs[c - 0x20];
x_advance += glyph->horiAdvance;
if (glyph->texture != NULL) {
float x = (float)(x_advance + glyph->horiBearingX) / 64.f;
float y = (float)(y_advance - glyph->horiBearingY) / 64.f;
SDL_FRect srect = {
.x = 0.f,
.y = 0.f,
.w = (float)glyph->width,
.h = (float)glyph->height
};
SDL_FRect drect = {
.x = x,
.y = y,
.w = (float)glyph->width,
.h = (float)glyph->height
};
SDL_RenderTexture(renderer, glyph->texture, &srect, &drect);
}
}
return x_advance;
}
using vec3 = vec<3, float>;
using vec4 = vec<4, float>;
using mat3x3 = mat<3, 3, float>;
using mat4x4 = mat<4, 4, float>;
struct line {
vec3 a;
vec3 b;
};
constexpr int num_lines = 5;
struct state {
struct line line[num_lines];
vec3 normal;
vec3 mouse_position;
};
struct state state = {
.line = {
{{ 0, -0.5, 0 }, { 0, 0.5, 0 }},
{{ 0, -0.5, -0.5 }, { 0, 0.5, -0.5 }},
{{ 0, -0.5, 0.5 }, { 0, 0.5, 0.5 }},
{{ 0, 0.5, 0.5 }, { 0, 0.5, -0.5 }},
{{ 0, -0.5, 0.5 }, { 0, -0.5, -0.5 }},
},
.normal = { -1, 0, 0 },
};
static int window_width = 1;
static int window_height = 1;
static inline int min(int a, int b)
{
return (a > b) ? b : a;
}
const float deg = 0.017453292519943295;
float deg45 = 0.7853981633974483;
static float vtheta = 0;
vec3 transform_vertex(vec3 v, float scale)
{
float dim = ((float)min(window_height, window_width)) / 2.0f;
v = v * scale;
mat3x3 rot1 = {
cos(vtheta), -sin(vtheta), 0,
sin(vtheta), cos(vtheta), 0,
0, 0, 1,
};
mat3x3 rot2 = {
1, 0, 0,
0, cos(deg45), -sin(deg45),
0, sin(deg45), cos(deg45),
};
mat3x3 rot3 = {
cos(-deg45), 0, sin(-deg45),
0, 1, 0,
sin(-deg45), 0, cos(-deg45),
};
(void)rot1; (void)rot2; (void)rot3;
v = rot1 * v;
//v = rot3 * v;
v = rot2 * v;
//
return {
v.x * dim + window_width / 2.0f,
v.y * dim + window_height / 2.0f,
v.z,
};
}
vec3 inverse_transform(float x, float y)
{
float dim = ((float)min(window_height, window_width)) / 2.0f;
assert(dim != 0);
return {
-(window_width - 2 * x) / (2 * dim),
-(window_height - 2 * y) / (2 * dim),
0,
};
}
struct line transform_line(struct line line, float scale)
{
return {
transform_vertex(line.a, scale),
transform_vertex(line.b, scale),
};
}
static inline void render_line(SDL_Renderer * renderer, struct line line)
{
assert(SDL_RenderLine(renderer, line.a.x, line.a.y, line.b.x, line.b.y));
}
void render_basis(SDL_Renderer * renderer)
{
// magenta: Z
assert(SDL_SetRenderDrawColorFloat(renderer, 1, 0, 1, 1));
render_line(renderer, transform_line({{0, 0, 0}, {0, 0, 1}}, 1.0f));
// yellow: Y
assert(SDL_SetRenderDrawColorFloat(renderer, 1, 1, 0, 1));
render_line(renderer, transform_line({{0, 0, 0}, {0, 1, 0}}, 1.0f));
// cyan: X
assert(SDL_SetRenderDrawColorFloat(renderer, 0, 1, 1, 1));
render_line(renderer, transform_line({{0, 0, 0}, {1, 0, 0}}, 1.0f));
}
mat3x3 rotate_to(vec3 old_normal, vec3 new_normal)
{
vec3 s = cross(old_normal, new_normal);
float c = dot(old_normal, new_normal);
mat3x3 i = mat3x3();
mat3x3 v = {
0, -s.z, s.y,
s.z, 0, -s.x,
-s.y, s.x, 0,
};
float mag = magnitude(s);
mat3x3 v2 = v * v;
float c_mag2 = (1 - c) / (mag * mag);
mat3x3 v2_c_mag2 = (v2 * c_mag2);
mat3x3 t = i + v + v2_c_mag2;
return t;
}
mat4x4 look_at(vec3 eye, vec3 center, vec3 up)
{
vec3 x;
vec3 y;
vec3 z;
z = eye - center;
z = z / magnitude(z);
y = up;
x = cross(y, z);
y = cross(z, x);
x = x / magnitude(x);
y = y / magnitude(y);
mat4x4 t = {
x.x, x.y, x.z, -dot(x, eye),
y.x, y.y, y.z, -dot(y, eye),
z.x, z.y, z.z, -dot(z, eye),
0, 0, 0, 1.0f
};
return t;
}
void render_lines(SDL_Renderer * renderer)
{
render_basis(renderer);
// line
assert(SDL_SetRenderDrawColorFloat(renderer, 1, 0.5, 0.5, 1));
for (int i = 0; i < num_lines; i++) {
struct line tl = transform_line(state.line[i], 1.0f);
render_line(renderer, tl);
}
// normal
assert(SDL_SetRenderDrawColorFloat(renderer, 1, 0, 0, 1));
struct line normal_line = {{0, 0, 0}, state.normal};
render_line(renderer, transform_line(normal_line, 0.5f));
// mouse
assert(SDL_SetRenderDrawColorFloat(renderer, 0, 0, 1, 1));
struct line mouse_line = {{0, 0, 0}, state.mouse_position};
render_line(renderer, transform_line(mouse_line, 0.5f));
// foo
mat3x3 t = rotate_to(state.normal, -state.mouse_position);
vec3 nr = t * state.normal;
//mat4x4 t = look_at({0, 0, 0}, state.mouse_position - state.normal, {0, 0, 1});
//vec4 nr4 = t * (vec4){state.normal.x, state.normal.y, state.normal.z, 0};
//vec3 nr = {nr.x, nr.y, nr.z};
assert(SDL_SetRenderDrawColorFloat(renderer, 0, 1, 0, 1));
struct line nr_line = {{0, 0, 0}, nr};
render_line(renderer, transform_line(nr_line, 0.5f));
{
assert(SDL_SetRenderDrawColorFloat(renderer, 0.5, 1, 0.5, 1));
for (int i = 0; i < num_lines; i++) {
struct line l = state.line[i];
/*
vec4 a4 = t * l.a;
vec4 b4 = t * l.b;
vec3 a = {a4.x, a4.y, a4.z};
vec3 b = {b4.x, b4.y, b4.z};
*/
vec3 a = t * l.a;
vec3 b = t * l.b;
struct line tl = {a, b};
render_line(renderer, transform_line(tl, 1.0f));
}
}
}
void render_text_state(SDL_Renderer * renderer)
{
int32_t x_advance = 10 << 6;
int32_t y_advance = face_height;
x_advance = render_text(renderer, x_advance, y_advance, "dot: ", 5);
//float d = dot(state.normal, state.mouse_position);
/*
float d = dot({0, 1}, state.mouse_position);
char buf[64];
int len = snprintf(buf, 64, "%.03f", d);
x_advance = render_text(renderer, x_advance, y_advance, buf, len);
*/
vec3 z = transform_vertex({0, 0, 1}, 1.1);
vec3 y = transform_vertex({0, 1, 0}, 1.1);
vec3 x = transform_vertex({1, 0, 0}, 1.1);
render_text(renderer, ((z.x - 30) * 64), ((z.y + 10) * 64), "+z", 2);
render_text(renderer, ((y.x - 30) * 64), ((y.y + 10) * 64), "+y", 2);
render_text(renderer, ((x.x - 30) * 64), ((x.y + 10) * 64), "+x", 2);
}
static float theta = 0;
void update_mouse_position()
{
vec3 pos = {0, 1, 1};
pos = pos / magnitude(pos);
mat3x3 rot1 = {
cos(theta), -sin(theta), 0,
sin(theta), cos(theta), 0,
0, 0, 1,
};
state.mouse_position = rot1 * pos;
theta += deg;
}
int main()
{
SDL_Window * window;
SDL_Renderer * renderer;
bool success = SDL_Init(SDL_INIT_VIDEO);
if (!success) printf("error: `%s`\n", SDL_GetError());
assert(success == true);
window = SDL_CreateWindow("sandbox",
512, // w
512, // h
SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_MAXIMIZED);
int num_drivers = SDL_GetNumRenderDrivers();
printf("available drivers:\n");
for (int i = 0; i < num_drivers; i++) {
const char * s = SDL_GetRenderDriver(i);
printf(" %s\n", s);
}
renderer = SDL_CreateRenderer(window, "opengles2");
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
const char * name = SDL_GetRendererName(renderer);
assert(name != NULL);
printf("renderer: %s\n", name);
const SDL_PixelFormat * formats = (const SDL_PixelFormat *)SDL_GetPointerProperty(props, SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL);
assert(formats != NULL);
while (*formats != SDL_PIXELFORMAT_UNKNOWN) {
printf("%s\n", SDL_GetPixelFormatName(*formats++));
}
uint64_t ticks = SDL_GetTicks();
int font_size = 25;
load_font(renderer, font_size);
while (1) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
bool success = SDL_GetWindowSizeInPixels(window, &window_width, &window_height);
assert(success == true);
render_text_state(renderer);
render_lines(renderer);
while (SDL_GetTicks() - ticks < (1000 / 60)) { SDL_Delay(1); }
SDL_RenderPresent(renderer);
ticks = SDL_GetTicks();
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
goto exit;
case SDL_EVENT_KEY_DOWN:
if (event.key.key == SDLK_ESCAPE)
goto exit;
break;
case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (event.button.button == 1) {
//vec3 mv = inverse_transform(event.button.x, event.button.y);
//float m = magnitude(mv);
//state.mouse_position = mv / m;
}
break;
default:
break;
}
}
update_mouse_position();
vtheta += deg / 10;
}
exit:
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}