diff --git a/Makefile b/Makefile index 338afc4..d3dbf2d 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CFLAGS += -LDFLAGS := -lraylib +LDFLAGS := -lraylib -lm OBJECTS := raylib-ascii-render: lib diff --git a/src/asciify.frag b/src/asciify.frag new file mode 100644 index 0000000..d0fea0b --- /dev/null +++ b/src/asciify.frag @@ -0,0 +1,27 @@ +// x-run: make -C.. run +#version 330 + +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec4 fragColor; + +uniform sampler2D texture0; +uniform sampler2D fontAtlas; +uniform vec2 fontSize; +uniform vec2 charactersXY; + +out vec4 finalColor; + +void main() +{ + vec2 screenSize = fontSize * charactersXY; + vec2 pixelCoord = floor(fragTexCoord * screenSize); + vec2 symbol = floor(pixelCoord / fontSize) / screenSize; + vec2 inSymbol = mod(pixelCoord, fontSize) / fontSize; + vec4 texelColor = vec4(0.0, 0.0, 0.0, 1.0); + + texelColor.rgb = texture(texture0, symbol * fontSize).rgb; + + finalColor = texelColor; +} + diff --git a/src/main.c b/src/main.c index 80b164e..3778f79 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,38 @@ // x-run: make -C.. run +#include #include +#include +#include #include -int main(void) { - InitWindow(320, 240, "Loading..."); - SetTargetFPS(30); +#define LEN(X) (sizeof(X)/sizeof(X[0])) +#define EARTHSIZE(V) (V/6.371) - int font_size = 16, chars_x = 80, chars_y = 24; +const static struct planet { + int anchor; + Color color; + float radius; + float distance; + float year_length; +} planets[] = { + { -1, { 252, 229, 112, 200 }, EARTHSIZE( 6.570), 0.0, 1.00 }, // SUN + { 0, { 26, 26, 26, 255 }, EARTHSIZE( 2.440), 5.7, 87.97 }, // MERCURY + { 0, { 230, 230, 230, 255 }, EARTHSIZE( 6.052), 10.8, 224.70 }, // VENUS + { 0, { 47, 106, 105, 255 }, EARTHSIZE( 6.371), 15.0, 365.26 }, // EARTH + { 0, { 153, 61, 0, 255 }, EARTHSIZE( 3.390), 22.8, 686.98 }, // MARS + { 0, { 176, 127, 53, 255 }, EARTHSIZE( 6.991), 58.8, 4332.82 }, // JUPITER + { 0, { 176, 143, 54, 255 }, EARTHSIZE( 5.823), 88.9, 10755.70 }, // SATURN + { 0, { 85, 128, 170, 255 }, EARTHSIZE(25.362), 288.0, 30687.15 }, // URANUS + { 0, { 54, 104, 150, 255 }, EARTHSIZE(24.622), 450.0, 60190.03 }, // NEPTUNE + { 3, { 127, 127, 127, 255 }, EARTHSIZE( 1.737), 1.9, 27.30 }, // MOON +}; + +int main(int argc, char **argv) { + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(320, 240, "Loading..."); + SetTargetFPS(60); + + int font_size = 16, chars_x = 160, chars_y = 50; Font font = LoadFontEx("unscii-16-full.ttf", font_size, NULL, 256); Vector2 glyphSize = MeasureTextEx(font, "A", font_size, 0); @@ -26,16 +52,105 @@ int main(void) { } Texture2D tex_atlas = LoadTextureFromImage(img_atlas); - SetWindowSize(chars_x * glyphSize.x, chars_y * glyphSize.y); + int wsize_x = chars_x * glyphSize.x, wsize_y = chars_y * glyphSize.y; + SetWindowMinSize(glyphSize.x * 40, glyphSize.y * 12); + SetWindowSize(wsize_x, wsize_y); + RenderTexture2D framebuffer = LoadRenderTexture(wsize_x, wsize_y); + Camera3D cam = { + .fovy = 45.0, + .projection = CAMERA_PERSPECTIVE, + .target.x = 0.0, + .target.y = 0.0, + .target.z = 0.0, + .up.x = 0.0, + .up.y = 1.0, + .up.z = 0.0, + .position.x = 50.0, + .position.y = 50.0, + .position.z = 50.0, + }; + + Shader shd_asciify = LoadShader(NULL, "./src/asciify.frag"); + int locAtlas = GetShaderLocation(shd_asciify, "fontAtlas"); + + { + float tmp[2]; + + tmp[0] = glyphSize.x; tmp[1] = glyphSize.y; + printf("glyph size: %fx%f\n", tmp[0], tmp[1]); + SetShaderValue(shd_asciify, GetShaderLocation(shd_asciify, "fontSize"), tmp, SHADER_UNIFORM_VEC2); + tmp[0] = chars_x; tmp[1] = chars_y; + SetShaderValue(shd_asciify, GetShaderLocation(shd_asciify, "charactersXY"), tmp, SHADER_UNIFORM_VEC2); + } + + SetCameraMode(cam, CAMERA_FREE); + + double time = 0.; + bool shading = false; for (int frame = 0; !WindowShouldClose(); frame++) { + time += GetFrameTime() * 8.; + if (IsWindowResized()) { + wsize_x = GetScreenWidth(); wsize_y = GetScreenHeight(); + wsize_x = (chars_x = floor(wsize_x / glyphSize.x)) * glyphSize.x; + wsize_y = (chars_y = floor(wsize_y / glyphSize.y)) * glyphSize.y; + UnloadRenderTexture(framebuffer); + framebuffer = LoadRenderTexture(wsize_x, wsize_y); + + float tmp[2]; + tmp[0] = chars_x; tmp[1] = chars_y; + SetShaderValue(shd_asciify, GetShaderLocation(shd_asciify, "charactersXY"), tmp, SHADER_UNIFORM_VEC2); + } + + if (IsKeyDown(KEY_LEFT_SHIFT) && IsKeyPressed(KEY_F)) { + SetWindowSize(wsize_x, wsize_y); + } + + if (IsKeyPressed(KEY_S)) shading ^= 1; + BeginDrawing(); - ClearBackground(BLACK); - DrawTexture(tex_atlas, 0, 0, WHITE); + UpdateCamera(&cam); + BeginTextureMode(framebuffer); + BeginMode3D(cam); + { + ClearBackground(BLANK); + DrawLine3D(Vector3Zero(), (Vector3){ 1.0, 0.0, 0.0 }, RED); + DrawLine3D(Vector3Zero(), (Vector3){ 0.0, 1.0, 0.0 }, GREEN); + DrawLine3D(Vector3Zero(), (Vector3){ 0.0, 0.0, 1.0 }, BLUE); + + Vector3 positions[LEN(planets)] = { 0 }; + for (int i = 0; i < LEN(planets); i++) { + struct planet planet = planets[i]; + Vector3 orbit = planet.anchor == -1 ? Vector3Zero() : positions[planet.anchor]; + Vector3 offset = { 0. }; + float phase = time * M_PI * 2.0 / planets[i].year_length; + offset.x = sin(phase) * planets[i].distance; + offset.z = cos(phase) * planets[i].distance; + Vector3 pos = Vector3Add(orbit, offset); + DrawSphere(pos, planets[i].radius, planets[i].color); + DrawCircle3D(orbit, planets[i].distance, (Vector3){ 1.0, 0.0, 0.0 }, 90.0, planets[i].color); + positions[i] = pos; + } + } + EndMode3D(); + EndTextureMode(); + + ClearBackground(RED); + DrawRectangle(0, 0, wsize_x, wsize_y, BLACK); + if (shading) BeginShaderMode(shd_asciify); + SetShaderValueTexture(shd_asciify, locAtlas, tex_atlas); + DrawTexturePro(framebuffer.texture, (Rectangle){ + 0, 0, wsize_x, -wsize_y + }, (Rectangle){ + 0, 0, wsize_x, wsize_y + }, (Vector2){ 0, 0 }, 0.0, WHITE); + if (shading) EndShaderMode(); EndDrawing(); } UnloadFont(font); UnloadImage(img_atlas); + UnloadShader(shd_asciify); UnloadTexture(tex_atlas); + UnloadRenderTexture(framebuffer); }