Files
compiler-old/examples/raytracer.src
T
2026-04-23 00:19:11 +02:00

204 lines
5.3 KiB
Plaintext

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;
}