#if 0 /* #/ ================================================================ #/ #/ landscape.c #/ #/ ================================================================ #/ #/ Self-compilation shell script #/ SRC=${0##*./} BIN=${SRC%.*} gcc \ -Wall -Wextra -Werror -pedantic \ -Wno-old-style-declaration \ -Wno-missing-braces \ -Wno-unused-variable \ -Wno-unused-but-set-variable \ -Wno-unused-parameter \ -Wno-overlength-strings \ -O3 \ -fsanitize=undefined,address,leak -mshstk \ -lX11 -lm \ -o $BIN $SRC && \ ./$BIN $@ && rm $BIN exit $? # */ #endif #include "../reduced_system_layer.c" #define EPS 1e-8 enum { MAX_NUM_TILES = 10 * 1024 * 1024, }; typedef struct { i32 index; i32 version; } Handle; typedef union { struct { f64 x, y; }; struct { f64 v[2]; }; } Vec2; typedef union { struct { f64 x, y, z; }; struct { f64 r, g, b; }; struct { f64 v[3]; }; } Vec3; typedef union { struct { f64 x, y, z, w; }; struct { f64 r, g, b, a; }; struct { f64 v[4]; }; } Vec4; typedef struct { b8 visible; f64 x; f64 y; f64 size; } Screen_Area; typedef struct { i64 time; i32 map_num_x; i32 map_num_y; f64 map[MAX_NUM_TILES]; f64 screen_distance; f64 zoom; f64 tilt; Vec3 eye_position; Vec3 right; Vec3 up; Vec3 forward; Vec3 screen_x_axis; Vec3 screen_y_axis; Vec3 screen_z_axis; } World; #define CHECK(fail_return, condition) \ assert(condition); \ if (!(condition)) \ return fail_return; World world = {0}; f64 dot(Vec3 a, Vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } Vec3 cross(Vec3 a, Vec3 b) { return (Vec3) { .x = a.y * b.z - a.z * b.y, .y = a.z * b.x - a.x * b.z, .z = a.x * b.y - a.y * b.x, }; } Vec3 neg(Vec3 a) { return (Vec3) { .x = -a.x, .y = -a.y, .z = -a.z, }; } Vec3 add(Vec3 a, Vec3 b) { return (Vec3) { .x = a.x + b.x, .y = a.y + b.y, .z = a.z + b.z, }; } Vec3 sub(Vec3 a, Vec3 b) { return (Vec3) { .x = a.x - b.x, .y = a.y - b.y, .z = a.z - b.z, }; } Vec3 mul(Vec3 a, f64 b) { return (Vec3) { .x = a.x * b, .y = a.y * b, .z = a.z * b, }; } Vec3 v_div(Vec3 a, f64 b) { if (b > -EPS && b < EPS) { assert(0); return (Vec3) { .x = 0., .y = 0., .z = 1., }; } return (Vec3) { .x = a.x / b, .y = a.y / b, .z = a.z / b, }; } Vec3 normal(Vec3 a) { return v_div(a, sqrt(dot(a, a))); } void resolve_axes(void) { world.right = (Vec3) { .x = 1., .y = 0., .z = 0., }; world.forward = (Vec3) { .x = 0., .y = 1., .z = 0., }; world.up = (Vec3) { .x = 0., .y = 0., .z = 1., }; if (world.tilt < 0.0) world.tilt += M_PI * 2.0 * floor(-world.tilt / (M_PI * 2.0) + 1.0); if (world.tilt >= M_PI * 2.0) world.tilt -= M_PI * 2.0 * floor(world.tilt / (M_PI * 2.0)); world.screen_z_axis = (Vec3) { .x = 0., .y = sin(world.tilt), .z = -cos(world.tilt), }; world.screen_y_axis = normal(cross(world.screen_z_axis, world.right)); world.screen_x_axis = normal(cross(world.screen_y_axis, world.screen_z_axis)); } Screen_Area world_to_screen(Vec3 p, f64 size) { Vec3 r = sub(p, world.eye_position); f64 l = dot(r, world.screen_z_axis); if (l < EPS) return (Screen_Area) { .visible = 0, }; f64 k = (world.screen_distance / l) * world.zoom; f64 x = k * dot(r, world.screen_x_axis); f64 y = k * dot(r, world.screen_y_axis); f64 hw = platform.frame_width * .5; f64 hh = platform.frame_height * .5; return (Screen_Area) { .visible = 1, .x = hw + x, .y = hh + y, .size = k * size, }; } Vec3 screen_to_world(i32 x, i32 y) { f64 fx = ((f64) x - platform.frame_width * .5) / world.zoom; f64 fy = ((f64) y - platform.frame_height * .5) / world.zoom; Vec3 r = add(add( mul(world.screen_x_axis, fx), mul(world.screen_y_axis, fy)), mul(world.screen_z_axis, world.screen_distance) ); if (r.z < -EPS || r.z > EPS) r = mul(r, -world.eye_position.z / r.z); return add(world.eye_position, r); } i64 time_milliseconds() { struct timespec t; timespec_get(&t, TIME_UTC); return (i64) t.tv_sec * 1000ll + (i64) t.tv_nsec / 1000000ll; } u32 u32_from_rgb(f32 red, f32 green, f32 blue) { i32 r = (i32) floor(red * 255.f); i32 g = (i32) floor(green * 255.f); i32 b = (i32) floor(blue * 255.f); if (r < 0) r = 0; if (r > 255) r = 255; if (g < 0) g = 0; if (g > 255) g = 255; if (b < 0) b = 0; if (b > 255) b = 255; return (r << 16) | (g << 8) | b; } i32 main(i32 argc, c8 **argv) { (void) argc; (void) argv; u32 background = u32_from_rgb(.03f, .08f, .01f); platform = (Platform) { .title = "Landscape", .frame_width = 960, .frame_height = 720, }; p_init(); world.screen_distance = 1.; world.zoom = 1e+3; world.eye_position = (Vec3) { .z = 10., }; i64 time_0 = time_milliseconds(); while (!platform.done) { p_handle_events(); i64 time_elapsed = time_milliseconds() - time_0; time_0 += time_elapsed; resolve_axes(); if (time_elapsed > 0) { if (platform.key_down['w']) world.tilt += .001 * time_elapsed; if (platform.key_down['s']) world.tilt -= .001 * time_elapsed; f64 k = world.eye_position.z; if (world.zoom > EPS) k /= world.zoom; if (platform.wheel_dy != 0) world.eye_position.z -= (f64) platform.wheel_dy; if (platform.key_down[BUTTON_RIGHT]) { world.eye_position.x -= platform.cursor_dx * k; world.eye_position.y += platform.cursor_dy * k; } world.time += time_elapsed; } for (i32 j = 0; j < platform.frame_height; ++j) for (i32 i = 0; i < platform.frame_width; ++i) platform.pixels[j * platform.frame_width + i] = background; p_render_frame(); } p_cleanup(); return 0; }