diff --git a/foo.reid b/foo.reid deleted file mode 100644 index 4f1f1bb..0000000 --- a/foo.reid +++ /dev/null @@ -1,475 +0,0 @@ -import std::concat_strings; -import std::print; -import std::from_str; -import std::free_string; -import std::add_num_to_str; - -/////////////////// -/// SDL externs /// -/////////////////// - -// Helper struct for stack allocated const sized strings, because structs are -// easier to create uninit than arrays. -struct SDL_Window {} -struct SDL_Renderer {} -struct SDL_Texture {} -struct SDL_Event { type: u32, reserved: [u8; 124] } -struct SDL_FRect { x: f32, y: f32, w: f32, h: f32 } -struct SDL_Rect { x: i32, y: i32, w: i32, h: i32 } -extern fn SDL_malloc(size: u64) -> *u8; -extern fn SDL_Init(flags: u32) -> bool; -extern fn SDL_Quit(); -extern fn SDL_CreateWindowAndRenderer(title: *char, width: i32, height: i32, flags: i32, - window_out: &mut *SDL_Window, renderer_out: &mut *SDL_Renderer) -> bool; -extern fn SDL_Delay(ms: u32); -extern fn SDL_SetRenderDrawColor(renderer: *SDL_Renderer, r: u8, g: u8, b: u8, a: u8); -extern fn SDL_RenderClear(renderer: *SDL_Renderer); -extern fn SDL_RenderPresent(renderer: *SDL_Renderer); -extern fn SDL_HasEvent(event_type: u32) -> bool; -extern fn SDL_PollEvent(event: &mut SDL_Event) -> bool; -extern fn SDL_PumpEvents(); -extern fn SDL_FlushEvents(min_type: u32, max_type: u32); -extern fn SDL_GetTicks() -> u64; -extern fn SDL_SetWindowTitle(window: *SDL_Window, title: *char) -> bool; -extern fn SDL_CreateTexture(renderer: *SDL_Renderer, - pixel_format: u32, texture_access: u32, width: u32, height: u32) -> *SDL_Texture; -extern fn SDL_RenderTexture(renderer: *SDL_Renderer, - texture: *SDL_Texture, srcfrect: &SDL_FRect, dstfrect: &SDL_FRect) -> bool; -extern fn SDL_UpdateTexture(texture: *SDL_Texture, rect: &SDL_Rect, pixels: *u8, pitch: u32) -> bool; -extern fn SDL_GetError() -> *char; -extern fn SDL_GetWindowSize(window: *SDL_Window, w: &mut i32, h: &mut i32) -> bool; -extern fn SDL_rand(max_exclusive: u32) -> u32; -extern fn SDL_SetTextureScaleMode(texture: *SDL_Texture, scale_mode: i32) -> bool; -extern fn SDL_sqrtf(value: f32) -> f32; -extern fn SDL_randf() -> f32; -extern fn SDL_powf(value: f32, power: f32) -> f32; - -// SDL error reporting helper -fn print_sdl_error(context: *char) { - let mut message = from_str(context); - - let delim = from_str(": "); - concat_strings(&mut message, delim); - free_string(&delim); - - let error_msg = from_str(SDL_GetError()); - concat_strings(&mut message, error_msg); - free_string(&error_msg); - - print(message); - - free_string(&message); -} - -///////////////////////////////// -/// Main setup and frame loop /// -///////////////////////////////// - -struct GameState { - renderer: *SDL_Renderer, - window: *SDL_Window, - render_texture: *SDL_Texture, - frame_counter: u32, - last_fps_reset: u64, - pixels: *u8, - pixels_w: u32, - pixels_h: u32, - pixels_bpp: u32, -} - -fn main() -> i32 { - let SDL_INIT_VIDEO = 32; - let SDL_WINDOW_RESIZABLE = 32; - let SDL_PIXELFORMAT_RGBA8888 = 373694468; - let SDL_PIXELFORMAT_ABGR8888 = 376840196; - let SDL_PIXELFORMAT_RGB24 = 386930691; - let SDL_PIXELFORMAT_BGR24 = 390076419; - let SDL_PIXELFORMAT_RGB96_FLOAT = 454057996; - let SDL_PIXELFORMAT_BGR96_FLOAT = 457203724; - let SDL_TEXTUREACCESS_STREAMING = 1; - let SDL_SCALEMODE_NEAREST = 0; - let SDL_SCALEMODE_LINEAR = 1; - let SDL_SCALEMODE_PIXELART = 2; - - let init_success = SDL_Init(SDL_INIT_VIDEO); - if init_success == false { - print_sdl_error("SDL init failed"); - return 1; - } - - let mut window = SDL_Window::null(); - let mut renderer = SDL_Renderer::null(); - let gfx_init_success = SDL_CreateWindowAndRenderer( - "graphical reid program", 640, 480, SDL_WINDOW_RESIZABLE, - &mut window, &mut renderer - ); - if gfx_init_success == false { - print_sdl_error("SDL renderer and window creation failed"); - return 1; - } - - let width = 320; - let height = 240; - let bpp = 4; - let render_texture = SDL_CreateTexture(renderer, - SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, width, height); - SDL_SetTextureScaleMode(render_texture, SDL_SCALEMODE_NEAREST); - - let pixels_len = (width * height * bpp) as u64; - let pixels = SDL_malloc(pixels_len); - let mut game_state = GameState { - renderer: renderer, - window: window, - render_texture: render_texture, - frame_counter: 0, - last_fps_reset: 0, - pixels: pixels, - pixels_w: width, - pixels_h: height, - pixels_bpp: bpp, - }; - - while frame_loop(&mut game_state) {} - - SDL_Quit(); - return 0; -} - -fn frame_loop(game_state: &mut GameState) -> bool { - let mut event = SDL_Event { type: 0, reserved: [0; 124] }; - while (SDL_PollEvent(&mut event)) { - if event.type == 256 { // SDL_EVENT_QUIT - return false; - } - } - - let mut screen_width = 0; - let mut screen_height = 0; - SDL_GetWindowSize(*game_state.window, &mut screen_width, &mut screen_height); - - let renderer = *game_state.renderer; - SDL_SetRenderDrawColor(renderer, 0, 50, 90, 255); - SDL_RenderClear(renderer); - - let w = *game_state.pixels_w; - let h = *game_state.pixels_h; - let bpp = *game_state.pixels_bpp; - for y in 0..h { - for x in 0..w { - render_pixel(x, y, game_state); - } - } - - let texture_area = SDL_Rect { x: 0, y: 0, w: w as i32, h: h as i32 }; - if SDL_UpdateTexture(*game_state.render_texture, &texture_area, *game_state.pixels as *u8, bpp * w) == false { - print_sdl_error("UpdateTexture error"); - } - let src = SDL_FRect { x: 0.0, y: 0.0, w: w as f32, h: h as f32 }; - let aspect_ratio = src.w / src.h; - let scaled_width = screen_height as f32 * aspect_ratio; - let dst = SDL_FRect { x: (screen_width as f32 - scaled_width) / 2.0, y: 0.0, w: scaled_width, h: screen_height as f32 }; - if SDL_RenderTexture(renderer, *game_state.render_texture, &src, &dst) == false { - print_sdl_error("RenderTexture error"); - } - - SDL_RenderPresent(renderer); - SDL_Delay(1); - - *game_state.frame_counter = *game_state.frame_counter + 1; - let t = SDL_GetTicks(); - if (t - *game_state.last_fps_reset) >= 1000 { - let mut title = from_str("graphical reid program "); - add_num_to_str(&mut title, *game_state.frame_counter as u64); - let fps_unit = from_str(" fps"); - concat_strings(&mut title, fps_unit); - free_string(&fps_unit); - SDL_SetWindowTitle(*game_state.window, title.inner); - free_string(&title); - *game_state.frame_counter = 0; - *game_state.last_fps_reset = t; - } - - return true; -} - -fn render_pixel(x: u32, y: u32, game_state: &mut GameState) { - let w = *game_state.pixels_w; - let h = *game_state.pixels_h; - let bpp = *game_state.pixels_bpp; - - let samples = 1; - let old_sample_weight = 0.75; - let new_sample_weight = 0.25 / samples as f32; - let mut rgb = vec_mul_scalar(old_sample_weight, [ - srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 0]), - srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 1]), - srgb_to_linear(*game_state.pixels[(x + y * w) * bpp + 2]) - ]); - for sample in 0..samples { - rgb = vec_add(rgb, vec_mul_scalar(new_sample_weight, shade(x, y, *game_state.frame_counter, w, h))); - } - *game_state.pixels[(x + y * w) * bpp + 0] = linear_to_srgb(rgb[0]); - *game_state.pixels[(x + y * w) * bpp + 1] = linear_to_srgb(rgb[1]); - *game_state.pixels[(x + y * w) * bpp + 2] = linear_to_srgb(rgb[2]); - *game_state.pixels[(x + y * w) * bpp + 3] = 255; -} - - -///////////////// -/// Rendering /// -///////////////// - -struct Ray { - origin: [f32; 3], - direction: [f32; 3], -} - -struct Material { - // 0 = lambertian diffuse - // 1 = mirror - type: u32, - // Generally the "color" of the surface (linear factors of how much of each - // color channel this surface does not absorb), but the idea is that the - // type governs what this means. - linear_color: [f32; 3], -} - -struct Hit { - hit: bool, - front_face: bool, - distance: f32, - normal: [f32; 3], - position: [f32; 3], - material: Material, -} -fn no_hit() -> Hit { - Hit { - hit: false, front_face: true, distance: 0.0, normal: [0.0; 3], position: [0.0; 3], - material: Material { type: 0, linear_color: [0.0; 3] }, - } -} - -struct Sphere { - center: [f32; 3], - radius: f32, - material: Material, -} - -fn shade(x: u32, y: u32, t: u32, w: u32, h: u32) -> [f32; 3] { - let jitter_x = SDL_randf() - 0.5; - let jitter_y = SDL_randf() - 0.5; - - let pixel_scale = 1.0 / h as f32; - let pixel_pos = [ - (x as f32 + jitter_x) * pixel_scale, - 1.0 - (y as f32 + jitter_y) * pixel_scale, - 0.0 - 1.0 - ]; - let camera_pos = [w as f32 * 0.5f32 * pixel_scale, h as f32 * 0.5f32 * pixel_scale, 0.0f32]; - let dir = vec_normalize(vec_sub(pixel_pos, camera_pos)); - let ray = Ray { origin: camera_pos, direction: dir }; - let beige_lambertian = Material { type: 0, linear_color: [0.3, 0.2, 0.1] }; - let green_lambertian = Material { type: 0, linear_color: [0.1, 0.5, 0.06] }; - let greenish_mirror = Material { type: 1, linear_color: [0.9, 1.0, 0.95] }; - let spheres = [ - // Ground - Sphere { center: vec_sub(camera_pos, [0.0, 100001.0, 0.0]), radius: 100000.0, material: beige_lambertian }, - // Centered unit sphere - Sphere { center: vec_add(camera_pos, [0.0, 0.0, 0.0 - 5.0]), radius: 1.0, material: green_lambertian }, - // The unit sphere on the right - Sphere { center: vec_add(camera_pos, [2.0, 0.0, 0.0 - 6.0]), radius: 1.0, material: greenish_mirror } - ]; - return shade_world(ray, &spheres, 3); -} - -fn shade_world(ray: Ray, spheres: &[Sphere; 3], bounces_left: u8) -> [f32; 3] { - if bounces_left == 0 { - return [0.0, 0.0, 0.0]; - } - - let mut closest_hit = no_hit(); - closest_hit.distance = 100.0; - for i in 0..3 { - let sphere_hit = ray_sphere_closest_hit(ray, *spheres[i], [0.001, closest_hit.distance]); - if sphere_hit.hit { - closest_hit = sphere_hit; - } - } - - if closest_hit.hit { - //return vec_mul_scalar(0.5, vec_add(closest_hit.normal, [1.0, 1.0, 1.0])); // normal - //return vec_mul_scalar(closest_hit.distance / 10.0, [1.0, 1.0, 1.0]); // depth - if closest_hit.material.type == 0 { - let bounce_dir = vec_normalize(vec_add(closest_hit.normal, random_unit_vec())); - let bounce_ray = Ray { origin: closest_hit.position, direction: bounce_dir }; - return vec_mul_componentwise( - closest_hit.material.linear_color, - shade_world(bounce_ray, spheres, bounces_left - 1) - ); - } else if closest_hit.material.type == 1 { - let bounce_dir = vec_reflect(ray.direction, closest_hit.normal); - let bounce_ray = Ray { origin: closest_hit.position, direction: bounce_dir }; - return vec_mul_componentwise( - closest_hit.material.linear_color, - shade_world(bounce_ray, spheres, bounces_left - 1) - ); - } else { - return [1.0, 0.0, 1.0]; - } - } - - return shade_sky(ray); -} - -fn shade_sky(ray: Ray) -> [f32; 3] { - let a = 0.5 * (ray.direction[1] + 1.0); - return vec_add( - vec_mul_scalar(1.0 - a, [1.0, 1.0, 1.0]), - vec_mul_scalar(a, [0.5, 0.7, 1.0]) - ); -} - -// Returns the distance from the ray origin to the sphere, or -1.0 if the ray doesn't hit. -fn ray_sphere_closest_hit(ray: Ray, sphere: Sphere, interval: [f32; 2]) -> Hit { - let to_sphere = vec_sub(sphere.center, ray.origin); - let h = vec_dot(ray.direction, to_sphere); - let c = vec_length_squared(to_sphere) - sphere.radius * sphere.radius; - let discriminant = h * h - c; - if discriminant < 0.0 { - return no_hit(); - } - - let discriminant_sqrt = SDL_sqrtf(discriminant); - let mut distance = h - discriminant_sqrt; - if interval_surrounds(interval, distance) == false { - distance = h - discriminant_sqrt; - if interval_surrounds(interval, distance) == false { - return no_hit(); - } - } - let hit_position = vec_add(ray.origin, vec_mul_scalar(distance, ray.direction)); - let mut front_face = true; - let mut normal = vec_normalize(vec_sub(hit_position, sphere.center)); - if vec_dot(normal, ray.direction) > 0.0 { - normal = vec_mul_scalar(0.0 - 1.0, normal); - front_face = false; - } - - return Hit { - hit: true, - front_face: front_face, - distance: distance, - normal: normal, - position: hit_position, - material: sphere.material, - }; -} - - -////////////////// -/// Other math /// -////////////////// - -fn clamp(min: f32, max: f32, value: f32) -> f32 { - if value > max { - return max; - } - if value < min { - return min; - } - return value; -} - -fn abs(f: f32) -> f32 { - if f < 0.0 { - return f * (0.0 - 1.0); - } - return f; -} - -fn vec_add(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] { - return [lhs[0] + rhs[0], lhs[1] + rhs[1], lhs[2] + rhs[2]]; -} - -fn vec_sub(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] { - return [lhs[0] - rhs[0], lhs[1] - rhs[1], lhs[2] - rhs[2]]; -} - -fn vec_dot(lhs: [f32; 3], rhs: [f32; 3]) -> f32 { - return lhs[0] * rhs[0] + lhs[1] * rhs[1] + lhs[2] * rhs[2]; -} - -fn vec_mul_componentwise(lhs: [f32; 3], rhs: [f32; 3]) -> [f32; 3] { - return [lhs[0] * rhs[0], lhs[1] * rhs[1], lhs[2] * rhs[2]]; -} - -fn vec_mul_scalar(lhs: f32, rhs: [f32; 3]) -> [f32; 3] { - return [lhs * rhs[0], lhs * rhs[1], lhs * rhs[2]]; -} - -fn vec_normalize(v: [f32; 3]) -> [f32; 3] { - let len_reciprocal = 1.0f32 / SDL_sqrtf(vec_length_squared(v)); - return vec_mul_scalar(len_reciprocal, v); -} - -fn vec_length_squared(v: [f32; 3]) -> f32 { - return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; -} - -fn vec_abs(v: [f32; 3]) -> [f32; 3] { - return [abs(v[0]), abs(v[1]), abs(v[2])]; -} - -fn vec_reflect(direction: [f32; 3], normal: [f32; 3]) -> [f32; 3] { - return vec_sub(direction, vec_mul_scalar(2.0f32 * vec_dot(direction, normal), normal)); -} - -fn interval_surrounds(interval: [f32; 2], value: f32) -> bool { - return (interval[0] < value) && (value < interval[1]); -} - -fn random_unit_vec() -> [f32; 3] { - let mut point = [ - SDL_randf() * 2.0f32 - 1.0f32, - SDL_randf() * 2.0f32 - 1.0f32, - SDL_randf() * 2.0f32 - 1.0f32 - ]; - let mut lensq = vec_length_squared(point); - while lensq > 1.0 { - point = [ - SDL_randf() * 2.0f32 - 1.0f32, - SDL_randf() * 2.0f32 - 1.0f32, - SDL_randf() * 2.0f32 - 1.0f32 - ]; - lensq = vec_length_squared(point); - } - let len_reciprocal = 1.0f32 / SDL_sqrtf(lensq); - return vec_mul_scalar(len_reciprocal, point); -} - -fn random_unit_vec_on_hemi(normal: [f32; 3]) -> [f32; 3] { - let rand_vec = random_unit_vec(); - if vec_dot(rand_vec, normal) < 0.0f32 { - return vec_mul_scalar(0.0f32 - 1.0f32, rand_vec); - } - return rand_vec; -} - -fn linear_to_srgb(linear: f32) -> u8 { - let mut floating_srgb = 0.0; - if linear <= 0.0031308f32 { - floating_srgb = 12.92f32 * linear; - } else { - floating_srgb = SDL_powf(linear as f32, 1.0 / 2.4) * 1.055f32 - 0.055f32; - } - let clamped = clamp(0.0, 1.0, floating_srgb); - return (clamped * 255.999) as u8; -} - -fn srgb_to_linear(srgb: u8) -> f32 { - let floating_srgb = srgb as f32 / 255.0; - if floating_srgb <= 0.04045f32 { - return floating_srgb / 12.92f32; - } - return SDL_powf((floating_srgb as f32 + 0.055) / 1.055, 2.4); -}