foreign fn putchar(c: i32) -> i32; foreign fn print_number(n: i32); foreign fn sqrtf(num: f32) -> f32; struct Vec3 { x: f32, y: f32, z: f32 } struct Ray { origin: Vec3, dir: Vec3 } struct Sphere { center: Vec3, radius: f32, color: Vec3 } struct Hit { hit: bool, t: f32, normal: Vec3, color: Vec3 } fn vec3_add(a: Vec3, b: Vec3) -> Vec3 { return Vec3 { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; } fn vec3_sub(a: Vec3, b: Vec3) -> Vec3 { return Vec3 { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z }; } fn vec3_mul(a: Vec3, t: f32) -> Vec3 { return Vec3 { x: a.x * t, y: a.y * t, z: a.z * t }; } fn vec3_dot(a: Vec3, b: Vec3) -> f32 { return a.x * b.x + a.y * b.y + a.z * b.z; } fn vec3_normalize(v: Vec3) -> Vec3 { let len = sqrtf(vec3_dot(v, v)); if len == 0.0 { return Vec3 { x: 0.0, y: 0.0, z: 0.0 }; } return vec3_mul(v, 1.0 / len); } fn intersect_sphere(ray: *Ray, sphere: *Sphere, t_min: f32, t_max: f32) -> Hit { let oc = vec3_sub((*ray).origin, (*sphere).center); let a = vec3_dot((*ray).dir, (*ray).dir); let half_b = vec3_dot(oc, (*ray).dir); let c = vec3_dot(oc, oc) - (*sphere).radius * (*sphere).radius; let discriminant = half_b * half_b - a * c; let miss = Hit { hit: false, t: 0.0, normal: Vec3 { x: 0.0, y: 0.0, z: 0.0 }, color: Vec3 { x: 0.0, y: 0.0, z: 0.0 } }; if discriminant < 0.0 { return miss; } let sqrtd = sqrtf(discriminant); let root = (-half_b - sqrtd) / a; if root < t_min { root = (-half_b + sqrtd) / a; if root < t_min { return miss; } } if root > t_max { return miss; } let hit_point = vec3_add((*ray).origin, vec3_mul((*ray).dir, root)); let normal = vec3_mul(vec3_sub(hit_point, (*sphere).center), 1.0 / (*sphere).radius); return Hit { hit: true, t: root, normal: normal, color: (*sphere).color }; } fn ray_color(ray: *Ray, spheres: *[Sphere; 2]) -> Vec3 { let closest_so_far = 1000.0; let hit_anything = false; let hit_color = Vec3 { x: 0.0, y: 0.0, z: 0.0 }; let hit_normal = Vec3 { x: 0.0, y: 0.0, z: 0.0 }; let i = 0; while i < 2 { // Pointer arithmetic to array structs via the &[] index syntax let hit = intersect_sphere(ray, &(*spheres)[i], 0.001, closest_so_far); if hit.hit { hit_anything = true; closest_so_far = hit.t; hit_color = hit.color; hit_normal = hit.normal; } i = i + 1; } if hit_anything { // We remap the surface normal from [-1..1] to the [0..1] color space let mapped_normal = vec3_mul(vec3_add(hit_normal, Vec3 { x: 1.0, y: 1.0, z: 1.0 }), 0.5); // Multiply the resulting surface map shading by the intrinsic color of the object return Vec3 { x: hit_color.x * mapped_normal.x, y: hit_color.y * mapped_normal.y, z: hit_color.z * mapped_normal.z }; } // Beautiful skybox blue to white linear gradient let unit_dir = vec3_normalize((*ray).dir); let t = 0.5 * (unit_dir.y + 1.0); return vec3_add( vec3_mul(Vec3 { x: 1.0, y: 1.0, z: 1.0 }, 1.0 - t), vec3_mul(Vec3 { x: 0.5, y: 0.7, z: 1.0 }, t) ); } fn main() -> i32 { let image_width = 1024; let image_height = 1024; let spheres: [Sphere; 2] = [ Sphere { center: Vec3 { x: 0.0, y: 0.0, z: -1.0 }, radius: 0.5, color: Vec3 { x: 1.0, y: 0.2, z: 0.2 } // Red sphere }, Sphere { center: Vec3 { x: 0.0, y: -100.5, z: -1.0 }, radius: 100.0, color: Vec3 { x: 0.2, y: 0.8, z: 0.2 } // Green ground } ]; // Print standard PPM Image File Header putchar(80); putchar(51); putchar(10); // "P3\n" print_number(image_width); putchar(32); // " " print_number(image_height); putchar(10); // "\n" print_number(255); putchar(10); // "255\n" let j = image_height - 1; while j >= 0 { let i = 0; while i < image_width { let u = (i as f32) / (image_width as f32 - 1.0); let v = (j as f32) / (image_height as f32 - 1.0); // Set up a classic look-at viewport layout let lower_left_corner = Vec3 { x: -2.0, y: -2.0, z: -1.0 }; let horizontal = Vec3 { x: 4.0, y: 0.0, z: 0.0 }; let vertical = Vec3 { x: 0.0, y: 4.0, z: 0.0 }; let origin = Vec3 { x: 0.0, y: 0.0, z: 0.0 }; let dir = vec3_sub( vec3_add(lower_left_corner, vec3_add(vec3_mul(horizontal, u), vec3_mul(vertical, v))), origin ); let ray = Ray { origin: origin, dir: dir }; let col = ray_color(&ray, &spheres); // Precedence parser respects parenthesis grouping over multiplication let ir = (255.999 * col.x) as i32; let ig = (255.999 * col.y) as i32; let ib = (255.999 * col.z) as i32; print_number(ir); putchar(32); print_number(ig); putchar(32); print_number(ib); putchar(10); i = i + 1; } j = j - 1; } return 0; }