From b5bc962e834992eeba2abdd10f6e37ee2ba20295 Mon Sep 17 00:00:00 2001
From: Vito Caputo <vcaputo@pengaru.com>
Date: Mon, 19 Mar 2018 21:39:09 -0700
Subject: ray: libize raytracer core, introduces src/libs

This is the first step of breaking out all the core rendering stuffs
into reusable libraries and making modules purely compositional,
consumers of various included rendering/effects libraries.

Expect multiple modules leveraging libray for a variety of scenes and
such.  Also expect compositions mixing the various libraries for more
interesting visuals.
---
 src/libs/ray/ray_3f.h                   | 154 ++++++++++++++++++++
 src/libs/ray/ray_camera.c               |  70 +++++++++
 src/libs/ray/ray_camera.h               |  84 +++++++++++
 src/libs/ray/ray_color.h                |  29 ++++
 src/libs/ray/ray_euler.c                |  90 ++++++++++++
 src/libs/ray/ray_euler.h                |  33 +++++
 src/libs/ray/ray_light_emitter.h        |  18 +++
 src/libs/ray/ray_object.h               |  18 +++
 src/libs/ray/ray_object_light.h         |  14 ++
 src/libs/ray/ray_object_plane.h         |  16 ++
 src/libs/ray/ray_object_point.h         |  15 ++
 src/libs/ray/ray_object_sphere.h        |  16 ++
 src/libs/ray/ray_object_type.h          |  12 ++
 src/libs/ray/ray_ray.h                  |  11 ++
 src/libs/ray/ray_render.c               | 251 ++++++++++++++++++++++++++++++++
 src/libs/ray/ray_render.h               |  15 ++
 src/libs/ray/ray_render_object.h        | 121 +++++++++++++++
 src/libs/ray/ray_render_object_plane.h  |  61 ++++++++
 src/libs/ray/ray_render_object_point.h  |  45 ++++++
 src/libs/ray/ray_render_object_sphere.h |  90 ++++++++++++
 src/libs/ray/ray_scene.h                |  16 ++
 src/libs/ray/ray_surface.h              |  15 ++
 22 files changed, 1194 insertions(+)
 create mode 100644 src/libs/ray/ray_3f.h
 create mode 100644 src/libs/ray/ray_camera.c
 create mode 100644 src/libs/ray/ray_camera.h
 create mode 100644 src/libs/ray/ray_color.h
 create mode 100644 src/libs/ray/ray_euler.c
 create mode 100644 src/libs/ray/ray_euler.h
 create mode 100644 src/libs/ray/ray_light_emitter.h
 create mode 100644 src/libs/ray/ray_object.h
 create mode 100644 src/libs/ray/ray_object_light.h
 create mode 100644 src/libs/ray/ray_object_plane.h
 create mode 100644 src/libs/ray/ray_object_point.h
 create mode 100644 src/libs/ray/ray_object_sphere.h
 create mode 100644 src/libs/ray/ray_object_type.h
 create mode 100644 src/libs/ray/ray_ray.h
 create mode 100644 src/libs/ray/ray_render.c
 create mode 100644 src/libs/ray/ray_render.h
 create mode 100644 src/libs/ray/ray_render_object.h
 create mode 100644 src/libs/ray/ray_render_object_plane.h
 create mode 100644 src/libs/ray/ray_render_object_point.h
 create mode 100644 src/libs/ray/ray_render_object_sphere.h
 create mode 100644 src/libs/ray/ray_scene.h
 create mode 100644 src/libs/ray/ray_surface.h

(limited to 'src/libs')

diff --git a/src/libs/ray/ray_3f.h b/src/libs/ray/ray_3f.h
new file mode 100644
index 0000000..2c04a5a
--- /dev/null
+++ b/src/libs/ray/ray_3f.h
@@ -0,0 +1,154 @@
+#ifndef _RAY_3F_H
+#define _RAY_3F_H
+
+#include <math.h>
+
+typedef struct ray_3f_t {
+	float	x, y, z;
+} ray_3f_t;
+
+
+/* return the result of (a + b) */
+static inline ray_3f_t ray_3f_add(const ray_3f_t *a, const ray_3f_t *b)
+{
+	ray_3f_t	res = {
+				.x = a->x + b->x,
+				.y = a->y + b->y,
+				.z = a->z + b->z,
+			};
+
+	return res;
+}
+
+
+/* return the result of (a - b) */
+static inline ray_3f_t ray_3f_sub(const ray_3f_t *a, const ray_3f_t *b)
+{
+	ray_3f_t	res = {
+				.x = a->x - b->x,
+				.y = a->y - b->y,
+				.z = a->z - b->z,
+			};
+
+	return res;
+}
+
+
+/* return the result of (-v) */
+static inline ray_3f_t ray_3f_negate(const ray_3f_t *v)
+{
+	ray_3f_t	res = {
+				.x = -v->x,
+				.y = -v->y,
+				.z = -v->z,
+			};
+
+	return res;
+}
+
+
+/* return the result of (a * b) */
+static inline ray_3f_t ray_3f_mult(const ray_3f_t *a, const ray_3f_t *b)
+{
+	ray_3f_t	res = {
+				.x = a->x * b->x,
+				.y = a->y * b->y,
+				.z = a->z * b->z,
+			};
+
+	return res;
+}
+
+
+/* return the result of (v * scalar) */
+static inline ray_3f_t ray_3f_mult_scalar(const ray_3f_t *v, float scalar)
+{
+	ray_3f_t	res = {
+				.x = v->x * scalar,
+				.y = v->y * scalar,
+				.z = v->z * scalar,
+			};
+
+	return res;
+}
+
+
+/* return the result of (uv / scalar) */
+static inline ray_3f_t ray_3f_div_scalar(const ray_3f_t *v, float scalar)
+{
+	ray_3f_t	res = {
+				.x = v->x / scalar,
+				.y = v->y / scalar,
+				.z = v->z / scalar,
+			};
+
+	return res;
+}
+
+
+/* return the result of (a . b) */
+static inline float ray_3f_dot(const ray_3f_t *a, const ray_3f_t *b)
+{
+	return a->x * b->x + a->y * b->y + a->z * b->z;
+}
+
+
+/* return the length of the supplied vector */
+static inline float ray_3f_length(const ray_3f_t *v)
+{
+	return sqrtf(ray_3f_dot(v, v));
+}
+
+
+/* return the normalized form of the supplied vector */
+static inline ray_3f_t ray_3f_normalize(const ray_3f_t *v)
+{
+	return ray_3f_mult_scalar(v, 1.0f / ray_3f_length(v));
+}
+
+
+/* return the distance between two arbitrary points */
+static inline float ray_3f_distance(const ray_3f_t *a, const ray_3f_t *b)
+{
+	ray_3f_t	delta = ray_3f_sub(a, b);
+
+	return ray_3f_length(&delta);
+}
+
+
+/* return the cross product of two unit vectors */
+static inline ray_3f_t ray_3f_cross(const ray_3f_t *a, const ray_3f_t *b)
+{
+	ray_3f_t	product;
+
+	product.x = a->y * b->z - a->z * b->y;
+	product.y = a->z * b->x - a->x * b->z;
+	product.z = a->x * b->y - a->y * b->x;
+
+	return product;
+}
+
+
+/* return the linearly interpolated vector between the two vectors at point alpha (0-1.0) */
+static inline ray_3f_t ray_3f_lerp(const ray_3f_t *a, const ray_3f_t *b, float alpha)
+{
+	ray_3f_t	lerp_a, lerp_b;
+
+	lerp_a = ray_3f_mult_scalar(a, 1.0f - alpha);
+	lerp_b = ray_3f_mult_scalar(b, alpha);
+
+	return ray_3f_add(&lerp_a, &lerp_b);
+}
+
+
+/* return the normalized linearly interpolated vector between the two vectors at point alpha (0-1.0) */
+static inline ray_3f_t ray_3f_nlerp(const ray_3f_t *a, const ray_3f_t *b, float alpha)
+{
+	ray_3f_t	lerp;
+
+	lerp = ray_3f_lerp(a, b, alpha);
+
+	return ray_3f_normalize(&lerp);
+}
+
+#endif
diff --git a/src/libs/ray/ray_camera.c b/src/libs/ray/ray_camera.c
new file mode 100644
index 0000000..b942cf3
--- /dev/null
+++ b/src/libs/ray/ray_camera.c
@@ -0,0 +1,70 @@
+#include "fb.h"
+
+#include "ray_camera.h"
+#include "ray_euler.h"
+
+
+/* Produce a vector from the provided orientation vectors and proportions. */
+static ray_3f_t project_corner(ray_3f_t *forward, ray_3f_t *left, ray_3f_t *up, float focal_length, float horiz, float vert)
+{
+	ray_3f_t	tmp;
+	ray_3f_t	corner;
+
+	corner = ray_3f_mult_scalar(forward, focal_length);
+	tmp = ray_3f_mult_scalar(left, horiz);
+	corner = ray_3f_add(&corner, &tmp);
+	tmp = ray_3f_mult_scalar(up, vert);
+	corner = ray_3f_add(&corner, &tmp);
+
+	return ray_3f_normalize(&corner);
+}
+
+
+/* Produce vectors for the corners of the entire camera frame, used for interpolation. */
+static void project_corners(const ray_camera_t *camera, ray_camera_frame_t *frame)
+{
+	ray_3f_t	forward, left, up, right, down;
+	float		half_horiz = (float)camera->width * 0.5f;
+	float		half_vert = (float)camera->height * 0.5f;
+
+	ray_euler_basis(&camera->orientation, &forward, &up, &left);
+	right = ray_3f_negate(&left);
+	down = ray_3f_negate(&up);
+
+	frame->nw = project_corner(&forward, &left, &up, camera->focal_length, half_horiz, half_vert);
+	frame->ne = project_corner(&forward, &right, &up, camera->focal_length, half_horiz, half_vert);
+	frame->se = project_corner(&forward, &right, &down, camera->focal_length, half_horiz, half_vert);
+	frame->sw = project_corner(&forward, &left, &down, camera->focal_length, half_horiz, half_vert);
+}
+
+
+/* Prepare a frame of camera projection, initializing res_frame. */
+void ray_camera_frame_prepare(const ray_camera_t *camera, ray_camera_frame_t *res_frame)
+{
+	res_frame->camera = camera;
+
+	project_corners(camera, res_frame);
+
+	res_frame->x_delta = 1.0f / (float)camera->width;
+	res_frame->y_delta = 1.0f / (float)camera->height;
+}
+
+
+/* Begin a frame's fragment, initializing frame and ray. */
+void ray_camera_fragment_begin(ray_camera_frame_t *frame, fb_fragment_t *fb_fragment, ray_ray_t *res_ray, ray_camera_fragment_t *res_fragment)
+{
+	res_fragment->frame = frame;
+	res_fragment->fb_fragment = fb_fragment;
+	res_fragment->ray = res_ray;
+
+	res_fragment->x = res_fragment->y = 0;
+
+	res_fragment->x_alpha = frame->x_delta * (float)fb_fragment->x;
+	res_fragment->y_alpha = frame->y_delta * (float)fb_fragment->y;
+
+	res_fragment->cur_w = ray_3f_lerp(&frame->nw, &frame->sw, res_fragment->y_alpha);
+	res_fragment->cur_e = ray_3f_lerp(&frame->ne, &frame->se, res_fragment->y_alpha);
+
+	res_ray->origin = frame->camera->position;
+	res_ray->direction = ray_3f_nlerp(&res_fragment->cur_w, &res_fragment->cur_e, res_fragment->x_alpha);
+}
diff --git a/src/libs/ray/ray_camera.h b/src/libs/ray/ray_camera.h
new file mode 100644
index 0000000..e393beb
--- /dev/null
+++ b/src/libs/ray/ray_camera.h
@@ -0,0 +1,84 @@
+#ifndef _RAY_CAMERA_H
+#define _RAY_CAMERA_H
+
+#include <math.h>
+
+#include "fb.h"
+
+#include "ray_3f.h"
+#include "ray_euler.h"
+#include "ray_ray.h"
+
+
+typedef struct ray_camera_t {
+	ray_3f_t	position;		/* position of camera, the origin of all its rays */
+	ray_euler_t	orientation;		/* orientation of the camera */
+	float		focal_length;		/* controls the field of view */
+	unsigned	width;			/* width of camera viewport in pixels */
+	unsigned	height;			/* height of camera viewport in pixels */
+} ray_camera_t;
+
+
+typedef struct ray_camera_frame_t {
+	const ray_camera_t	*camera;		/* the camera supplied to frame_begin() */
+
+	ray_3f_t		nw, ne, sw, se;		/* directions pointing through the corners of the frame fragment */
+	float			x_delta, y_delta;	/* interpolation step delta along the x and y axis */
+} ray_camera_frame_t;
+
+
+typedef struct ray_camera_fragment_t {
+	ray_camera_frame_t	*frame;			/* the frame supplied to fragment_begin() */
+	fb_fragment_t		*fb_fragment;		/* the fragment supplied to fragment_begin() */
+	ray_ray_t		*ray;			/* the ray supplied to frame_begin(), which gets updated as we step through the frame. */
+
+	ray_3f_t		cur_w, cur_e;		/* current row's west and east ends */
+	float			x_alpha, y_alpha;	/* interpolation position along the x and y axis */
+	unsigned		x, y;			/* integral position within frame fragment */
+} ray_camera_fragment_t;
+
+
+void ray_camera_frame_prepare(const ray_camera_t *camera, ray_camera_frame_t *res_frame);
+void ray_camera_fragment_begin(ray_camera_frame_t *frame, fb_fragment_t *fb_fragment, ray_ray_t *res_ray, ray_camera_fragment_t *res_fragment);
+
+
+/* Step the ray through the fragment on the x axis, returns 1 when rays remain on this axis, 0 at the end. */
+/* When 1 is returned, fragment->ray is left pointing through the new coordinate. */
+static inline int ray_camera_fragment_x_step(ray_camera_fragment_t *fragment)
+{
+	fragment->x++;
+
+	if (fragment->x >= fragment->fb_fragment->width) {
+		fragment->x = 0;
+		fragment->x_alpha = fragment->frame->x_delta * (float)fragment->fb_fragment->x;
+		return 0;
+	}
+
+	fragment->x_alpha += fragment->frame->x_delta;
+	fragment->ray->direction = ray_3f_nlerp(&fragment->cur_w, &fragment->cur_e, fragment->x_alpha);
+
+	return 1;
+}
+
+
+/* Step the ray through the fragment on the y axis, returns 1 when rays remain on this axis, 0 at the end. */
+/* When 1 is returned, fragment->ray is left pointing through the new coordinate. */
+static inline int ray_camera_fragment_y_step(ray_camera_fragment_t *fragment)
+{
+	fragment->y++;
+
+	if (fragment->y >= fragment->fb_fragment->height) {
+		fragment->y = 0;
+		fragment->y_alpha = fragment->frame->y_delta * (float)fragment->fb_fragment->y;
+		return 0;
+	}
+
+	fragment->y_alpha += fragment->frame->y_delta;
+	fragment->cur_w = ray_3f_lerp(&fragment->frame->nw, &fragment->frame->sw, fragment->y_alpha);
+	fragment->cur_e = ray_3f_lerp(&fragment->frame->ne, &fragment->frame->se, fragment->y_alpha);
+	fragment->ray->direction = ray_3f_nlerp(&fragment->cur_w, &fragment->cur_e, fragment->x_alpha);
+
+	return 1;
+}
+
+#endif
diff --git a/src/libs/ray/ray_color.h b/src/libs/ray/ray_color.h
new file mode 100644
index 0000000..9fe62c1
--- /dev/null
+++ b/src/libs/ray/ray_color.h
@@ -0,0 +1,29 @@
+#ifndef _RAY_COLOR_H
+#define _RAY_COLOR_H
+
+#include <stdint.h>
+
+#include "ray_3f.h"
+
+typedef ray_3f_t ray_color_t;
+
+/* convert a vector into a packed, 32-bit rgb pixel value */
+static inline uint32_t ray_color_to_uint32_rgb(ray_color_t color) {
+	uint32_t	pixel;
+
+	/* doing this all per-pixel, ugh. */
+
+	if (color.x > 1.0f) color.x = 1.0f;
+	if (color.y > 1.0f) color.y = 1.0f;
+	if (color.z > 1.0f) color.z = 1.0f;
+
+	pixel = (uint32_t)(color.x * 255.0f);
+	pixel <<= 8;
+	pixel |= (uint32_t)(color.y * 255.0f);
+	pixel <<= 8;
+	pixel |= (uint32_t)(color.z * 255.0f);
+
+	return pixel;
+}
+
+#endif
diff --git a/src/libs/ray/ray_euler.c b/src/libs/ray/ray_euler.c
new file mode 100644
index 0000000..f143e25
--- /dev/null
+++ b/src/libs/ray/ray_euler.c
@@ -0,0 +1,90 @@
+#include <assert.h>
+#include <math.h>
+
+#include "ray_3f.h"
+#include "ray_euler.h"
+
+/* produce orthonormal basis vectors from euler angles, rotated in the specified order */
+void ray_euler_basis(const ray_euler_t *e, ray_3f_t *forward, ray_3f_t *up, ray_3f_t *left)
+{
+	float	cos_yaw = cosf(e->yaw);
+	float	sin_yaw = sinf(e->yaw);
+	float	cos_roll = cosf(e->roll);
+	float	sin_roll = sinf(e->roll);
+	float	cos_pitch = cosf(e->pitch);
+	float	sin_pitch = sinf(e->pitch);
+
+	/* Rotation matrices from http://www.songho.ca/opengl/gl_anglestoaxes.html */
+	switch (e->order) {
+	case RAY_EULER_ORDER_PYR:
+		/* pitch, yaw, roll */
+		up->x = -cos_yaw * sin_roll;
+		up->y = -sin_pitch * sin_yaw * sin_roll + cos_pitch * cos_roll;
+		up->z = cos_pitch * sin_yaw * sin_roll + sin_pitch * cos_roll;
+
+		forward->x = sin_yaw;
+		forward->y = -sin_pitch * cos_yaw;
+		forward->z = cos_pitch * cos_yaw;
+		break;
+
+	case RAY_EULER_ORDER_YRP:
+		/* yaw, roll, pitch */
+		up->x = -cos_yaw * sin_roll * cos_pitch + sin_yaw * sin_pitch;
+		up->y = cos_roll * cos_pitch;
+		up->z = sin_yaw * sin_roll * cos_pitch + cos_yaw * sin_pitch;
+
+		forward->x = cos_yaw * sin_roll * sin_pitch + sin_yaw * cos_pitch;
+		forward->y = -cos_roll * sin_pitch;
+		forward->z = -sin_yaw * sin_roll * sin_pitch + cos_yaw * cos_pitch;
+		break;
+
+	case RAY_EULER_ORDER_RPY:
+		/* roll, pitch, yaw */
+		up->x = -sin_roll * cos_pitch;
+		up->y = cos_roll * cos_pitch;
+		up->z = sin_pitch;
+
+		forward->x = cos_roll * sin_yaw + sin_roll * sin_pitch * cos_yaw;
+		forward->y = sin_roll * sin_yaw - cos_roll * sin_pitch * cos_yaw;
+		forward->z = cos_pitch * cos_yaw;
+		break;
+
+	case RAY_EULER_ORDER_PRY:
+		/* pitch, roll, yaw */
+		up->x = -sin_roll;
+		up->y = cos_pitch * cos_roll;
+		up->z = sin_pitch * cos_roll;
+
+		forward->x = cos_roll * sin_yaw;
+		forward->y = cos_pitch * sin_roll * sin_yaw - sin_pitch * cos_yaw;
+		forward->z = sin_pitch * sin_roll * sin_yaw + cos_pitch * cos_yaw;
+		break;
+
+	case RAY_EULER_ORDER_RYP:
+		/* roll, yaw, pitch */
+		up->x = -sin_roll * cos_pitch + cos_roll * sin_yaw * sin_pitch;
+		up->y = cos_roll * cos_pitch + sin_roll * sin_yaw * sin_pitch;
+		up->z = cos_yaw * sin_pitch;
+
+		forward->x = sin_roll * sin_pitch + cos_roll * sin_yaw * cos_pitch;
+		forward->y = -cos_roll * sin_pitch + sin_roll * sin_yaw * cos_pitch;
+		forward->z = cos_yaw * cos_pitch;
+		break;
+
+	case RAY_EULER_ORDER_YPR:
+		/* yaw, pitch, roll */
+		up->x = -cos_yaw * sin_roll + sin_yaw * sin_pitch * cos_roll;
+		up->y = cos_pitch * cos_roll;
+		up->z = sin_yaw * sin_roll + cos_yaw * sin_pitch * cos_roll;
+
+		forward->x = sin_yaw * cos_pitch;
+		forward->y = -sin_pitch;
+		forward->z = cos_yaw * cos_pitch;
+		break;
+
+	default:
+		assert(0);
+	}
+
+	*left = ray_3f_cross(up, forward);
+}
diff --git a/src/libs/ray/ray_euler.h b/src/libs/ray/ray_euler.h
new file mode 100644
index 0000000..39da17e
--- /dev/null
+++ b/src/libs/ray/ray_euler.h
@@ -0,0 +1,33 @@
+#ifndef _RAY_EULER_H
+#define _RAY_EULER_H
+
+#include <math.h>
+
+#include "ray_3f.h"
+
+/* Desired order to apply euler angle rotations */
+typedef enum ray_euler_order_t {
+	RAY_EULER_ORDER_PYR,
+	RAY_EULER_ORDER_YRP,
+	RAY_EULER_ORDER_RPY,
+	RAY_EULER_ORDER_PRY,
+	RAY_EULER_ORDER_RYP,
+	RAY_EULER_ORDER_YPR,
+} ray_euler_order_t;
+
+/* euler angles are convenient for describing orientation */
+typedef struct ray_euler_t {
+	ray_euler_order_t	order;	/* order to apply rotations in */
+	float			pitch;	/* pitch in radiasn */
+	float			yaw;	/* yaw in radians */
+	float			roll;	/* roll in radians */
+} ray_euler_t;
+
+
+/* convenience macro for converting degrees to radians */
+#define RAY_EULER_DEGREES(_deg) \
+	(_deg * (2 * M_PI / 360.0f))
+
+void ray_euler_basis(const ray_euler_t *e, ray_3f_t *forward, ray_3f_t *up, ray_3f_t *left);
+
+#endif
diff --git a/src/libs/ray/ray_light_emitter.h b/src/libs/ray/ray_light_emitter.h
new file mode 100644
index 0000000..3b5509e
--- /dev/null
+++ b/src/libs/ray/ray_light_emitter.h
@@ -0,0 +1,18 @@
+#ifndef _RAY_LIGHT_EMITTER_H
+#define _RAY_LIGHT_EMITTER_H
+
+#include "ray_object_point.h"
+#include "ray_object_sphere.h"
+
+typedef enum ray_light_emitter_type_t {
+	RAY_LIGHT_EMITTER_TYPE_SPHERE,
+	RAY_LIGHT_EMITTER_TYPE_POINT,
+} ray_light_emitter_type_t;
+
+typedef union ray_light_emitter_t {
+	ray_light_emitter_type_t	type;
+	ray_object_sphere_t		sphere;
+	ray_object_point_t		point;
+} ray_light_emitter_t;
+
+#endif
diff --git a/src/libs/ray/ray_object.h b/src/libs/ray/ray_object.h
new file mode 100644
index 0000000..fc5ae1b
--- /dev/null
+++ b/src/libs/ray/ray_object.h
@@ -0,0 +1,18 @@
+#ifndef _RAY_OBJECT_H
+#define _RAY_OBJECT_H
+
+#include "ray_object_light.h"
+#include "ray_object_plane.h"
+#include "ray_object_point.h"
+#include "ray_object_sphere.h"
+#include "ray_object_type.h"
+
+typedef union ray_object_t {
+	ray_object_type_t	type;
+	ray_object_sphere_t	sphere;
+	ray_object_point_t	point;
+	ray_object_plane_t	plane;
+	ray_object_light_t	light;
+} ray_object_t;
+
+#endif
diff --git a/src/libs/ray/ray_object_light.h b/src/libs/ray/ray_object_light.h
new file mode 100644
index 0000000..ca9bac9
--- /dev/null
+++ b/src/libs/ray/ray_object_light.h
@@ -0,0 +1,14 @@
+#ifndef _RAY_OBJECT_LIGHT_H
+#define _RAY_OBJECT_LIGHT_H
+
+#include "ray_light_emitter.h"
+#include "ray_object_type.h"
+
+
+typedef struct ray_object_light_t {
+	ray_object_type_t	type;
+	float			brightness;
+	ray_light_emitter_t	emitter;
+} ray_object_light_t;
+
+#endif
diff --git a/src/libs/ray/ray_object_plane.h b/src/libs/ray/ray_object_plane.h
new file mode 100644
index 0000000..96ed437
--- /dev/null
+++ b/src/libs/ray/ray_object_plane.h
@@ -0,0 +1,16 @@
+#ifndef _RAY_OBJECT_PLANE_H
+#define _RAY_OBJECT_PLANE_H
+
+#include "ray_3f.h"
+#include "ray_object_type.h"
+#include "ray_surface.h"
+
+
+typedef struct ray_object_plane_t {
+	ray_object_type_t	type;
+	ray_surface_t		surface;
+	ray_3f_t		normal;
+	float			distance;
+} ray_object_plane_t;
+
+#endif
diff --git a/src/libs/ray/ray_object_point.h b/src/libs/ray/ray_object_point.h
new file mode 100644
index 0000000..5685fdc
--- /dev/null
+++ b/src/libs/ray/ray_object_point.h
@@ -0,0 +1,15 @@
+#ifndef _RAY_OBJECT_POINT_H
+#define _RAY_OBJECT_POINT_H
+
+#include "ray_3f.h"
+#include "ray_object_type.h"
+#include "ray_surface.h"
+
+
+typedef struct ray_object_point_t {
+	ray_object_type_t	type;
+	ray_surface_t		surface;
+	ray_3f_t		center;
+} ray_object_point_t;
+
+#endif
diff --git a/src/libs/ray/ray_object_sphere.h b/src/libs/ray/ray_object_sphere.h
new file mode 100644
index 0000000..71b6334
--- /dev/null
+++ b/src/libs/ray/ray_object_sphere.h
@@ -0,0 +1,16 @@
+#ifndef _RAY_OBJECT_SPHERE_H
+#define _RAY_OBJECT_SPHERE_H
+
+#include "ray_3f.h"
+#include "ray_object_type.h"
+#include "ray_surface.h"
+
+
+typedef struct ray_object_sphere_t {
+	ray_object_type_t	type;
+	ray_surface_t		surface;
+	ray_3f_t		center;
+	float			radius;
+} ray_object_sphere_t;
+
+#endif
diff --git a/src/libs/ray/ray_object_type.h b/src/libs/ray/ray_object_type.h
new file mode 100644
index 0000000..ab797d2
--- /dev/null
+++ b/src/libs/ray/ray_object_type.h
@@ -0,0 +1,12 @@
+#ifndef _RAY_OBJECT_TYPE_H
+#define _RAY_OBJECT_TYPE_H
+
+typedef enum ray_object_type_t {
+	RAY_OBJECT_TYPE_SENTINEL,
+	RAY_OBJECT_TYPE_SPHERE,
+	RAY_OBJECT_TYPE_POINT,
+	RAY_OBJECT_TYPE_PLANE,
+	RAY_OBJECT_TYPE_LIGHT,
+} ray_object_type_t;
+
+#endif
diff --git a/src/libs/ray/ray_ray.h b/src/libs/ray/ray_ray.h
new file mode 100644
index 0000000..91469a2
--- /dev/null
+++ b/src/libs/ray/ray_ray.h
@@ -0,0 +1,11 @@
+#ifndef _RAY_RAY_H
+#define _RAY_RAY_H
+
+#include "ray_3f.h"
+
+typedef struct ray_ray_t {
+	ray_3f_t	origin;
+	ray_3f_t	direction;
+} ray_ray_t;
+
+#endif
diff --git a/src/libs/ray/ray_render.c b/src/libs/ray/ray_render.c
new file mode 100644
index 0000000..8b930f6
--- /dev/null
+++ b/src/libs/ray/ray_render.c
@@ -0,0 +1,251 @@
+#include <stdlib.h>
+#include <math.h>
+
+#include "fb.h"
+
+#include "ray_camera.h"
+#include "ray_color.h"
+#include "ray_render_object.h"
+#include "ray_ray.h"
+#include "ray_scene.h"
+
+#define MAX_RECURSION_DEPTH	4
+#define MIN_RELEVANCE		0.05f
+
+typedef struct ray_render_t {
+	const ray_scene_t	*scene;		/* scene being rendered */
+	const ray_camera_t	*camera;	/* camera rendering the scene */
+
+	ray_color_t		ambient_light;
+	ray_camera_frame_t	frame;
+
+	ray_render_object_t	objects[];
+} ray_render_t;
+
+/* Determine if the ray is obstructed by an object within the supplied distance, for shadows */
+static inline int ray_is_obstructed(ray_render_t *render, unsigned depth, ray_ray_t *ray, float distance)
+{
+	ray_render_object_t	*object;
+
+	for (object = render->objects; object->type; object++) {
+		float	ood;
+
+		if (ray_render_object_intersects_ray(object, depth, ray, &ood) &&
+		    ood < distance) {
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* shadow test */
+static inline int point_is_shadowed(ray_render_t *render, unsigned depth, ray_3f_t *light_direction, float distance, ray_3f_t *point)
+{
+	ray_ray_t	shadow_ray;
+
+	shadow_ray.direction = *light_direction;
+	shadow_ray.origin = *point;
+
+	if (ray_is_obstructed(render, depth + 1, &shadow_ray, distance))
+		return 1;
+
+	return 0;
+}
+
+
+/* a faster powf() that's good enough for our purposes.
+ * XXX: note there's a faster technique which exploits the IEEE floating point format:
+ * https://github.com/ekmett/approximate/blob/master/cbits/fast.c#L185
+ */
+static inline float approx_powf(float x, float y)
+{
+	return expf(y * logf(x));
+}
+
+
+/* Determine the color @ distance on ray on object viewed from origin */
+static inline ray_color_t shade_intersection(ray_render_t *render, ray_render_object_t *object, ray_ray_t *ray, ray_3f_t *intersection, ray_3f_t *normal, unsigned depth, float *res_reflectivity)
+{
+	ray_surface_t	surface = ray_render_object_surface(object, intersection);
+	ray_color_t	color = ray_3f_mult(&surface.color, &render->ambient_light);
+	ray_object_t	*light;
+
+	/* visit lights for shadows and illumination */
+	for (light = render->scene->lights; light->type; light++) {
+		ray_3f_t	lvec = ray_3f_sub(&light->light.emitter.point.center, intersection);
+		float		ldist = ray_3f_length(&lvec);
+		float		lvec_normal_dot;
+
+		lvec = ray_3f_mult_scalar(&lvec, (1.0f / ldist)); /* normalize lvec */
+#if 1
+		if (point_is_shadowed(render, depth, &lvec, ldist, intersection))
+			continue;
+#endif
+		lvec_normal_dot = ray_3f_dot(normal, &lvec);
+
+		if (lvec_normal_dot > 0) {
+#if 1
+			float		rvec_lvec_dot = ray_3f_dot(&ray->direction, &lvec);
+			float		intensity = light->light.brightness * (1.0 / (ldist * ldist));
+			ray_color_t	diffuse;
+
+			diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot);
+			diffuse = ray_3f_mult_scalar(&diffuse, surface.diffuse);
+			color = ray_3f_add(&color, &diffuse);
+
+			if (rvec_lvec_dot > 0) {
+				ray_color_t	specular;
+
+				/* FIXME: assumes light is a point for its color */
+				specular = ray_3f_mult_scalar(&light->light.emitter.point.surface.color, approx_powf(rvec_lvec_dot, surface.highlight_exponent));
+				specular = ray_3f_mult_scalar(&specular, surface.specular);
+				color = ray_3f_add(&color, &specular);
+			}
+
+			color = ray_3f_mult_scalar(&color, intensity);
+#else
+			ray_color_t	diffuse;
+
+			diffuse = ray_3f_mult_scalar(&surface.color, lvec_normal_dot);
+			color = ray_3f_add(&color, &diffuse);
+#endif
+		}
+	}
+
+	/* for now just treat specular as the reflectivity */
+	*res_reflectivity = surface.specular;
+
+	return color;
+}
+
+
+static inline ray_render_object_t * find_nearest_intersection(ray_render_t *render, ray_render_object_t *reflector, ray_ray_t *ray, unsigned depth, float *res_distance)
+{
+	ray_render_object_t	*nearest_object = NULL;
+	float			nearest_object_distance = INFINITY;
+	ray_render_object_t	*object;
+
+	for (object = render->objects; object->type; object++) {
+		float		distance;
+
+		/* Don't bother checking if a reflected ray intersects the object reflecting it,
+		 * reflector = NULL for primary rays, which will never compare as true here. */
+		if (object == reflector)
+			continue;
+
+		/* Does this ray intersect object? */
+		if (ray_render_object_intersects_ray(object, depth, ray, &distance)) {
+			/* Is it the nearest intersection? */
+			if (distance < nearest_object_distance) {
+				nearest_object = object;
+				nearest_object_distance = distance;
+			}
+		}
+	}
+
+	if (nearest_object)
+		*res_distance = nearest_object_distance;
+
+	return nearest_object;
+}
+
+
+static inline ray_color_t trace_ray(ray_render_t *render, ray_ray_t *primary_ray)
+{
+	ray_color_t		color = { .x = 0.0f, .y = 0.0f, .z = 0.0f };
+	ray_3f_t		intersection, normal;
+	ray_render_object_t	*reflector = NULL;
+	float			relevance = 1.0f, reflectivity;
+	unsigned		depth = 0;
+	ray_ray_t		reflected_ray, *ray = primary_ray;
+
+	do {
+		ray_render_object_t	*nearest_object;
+		float		nearest_distance;
+
+		if (reflector) {
+			float		dot = ray_3f_dot(&ray->direction, &normal);
+			ray_3f_t	new_direction = ray_3f_mult_scalar(&normal, dot * 2.0f);
+
+			new_direction = ray_3f_sub(&ray->direction, &new_direction);
+
+			reflected_ray.origin = intersection;
+			reflected_ray.direction = new_direction;
+
+			ray = &reflected_ray;
+		}
+
+		nearest_object = find_nearest_intersection(render, reflector, ray, depth, &nearest_distance);
+		if (nearest_object) {
+			ray_3f_t	more_color;
+			ray_3f_t	rvec;
+
+			rvec = ray_3f_mult_scalar(&ray->direction, nearest_distance);
+			intersection = ray_3f_add(&ray->origin, &rvec);
+			normal = ray_render_object_normal(nearest_object, &intersection);
+
+			more_color = shade_intersection(render, nearest_object, ray, &intersection, &normal, depth, &reflectivity);
+			more_color = ray_3f_mult_scalar(&more_color, relevance);
+			color = ray_3f_add(&color, &more_color);
+		}
+
+		reflector = nearest_object;
+	} while (reflector && (++depth < MAX_RECURSION_DEPTH) && (relevance *= reflectivity) >= MIN_RELEVANCE);
+
+	return color;
+}
+
+
+void ray_render_trace_fragment(ray_render_t *render, fb_fragment_t *fb_fragment)
+{
+	uint32_t		*buf = fb_fragment->buf;
+	ray_camera_fragment_t	fragment;
+	ray_ray_t		ray;
+
+	ray_camera_fragment_begin(&render->frame, fb_fragment, &ray, &fragment);
+	do {
+		do {
+			*buf = ray_color_to_uint32_rgb(trace_ray(render, &ray));
+			buf++;
+		} while (ray_camera_fragment_x_step(&fragment));
+
+		buf = ((void *)buf) + fb_fragment->stride;
+	} while (ray_camera_fragment_y_step(&fragment));
+}
+
+
+/* prepare the scene for rendering with camera, must be called whenever anything in the scene+camera pair has been changed. */
+/* this is basically a time for the raytracer to precompute whatever it can which otherwise ends up occurring per-ray */
+/* the camera is included so primary rays which all have a common origin may be optimized for */
+ray_render_t * ray_render_new(const ray_scene_t *scene, const ray_camera_t *camera)
+{
+	ray_render_t	*render;
+	ray_object_t	*object;
+	unsigned	i;
+
+	for (i = 0, object = scene->objects; object->type; object++)
+		i++;
+
+	render = malloc(sizeof(ray_render_t) + i * sizeof(ray_render_object_t));
+	if (!render)
+		return NULL;
+
+	render->scene = scene;
+	render->camera = camera;
+
+	render->ambient_light = ray_3f_mult_scalar(&scene->ambient_color, scene->ambient_brightness);
+	ray_camera_frame_prepare(camera, &render->frame);
+
+	for (i = 0, object = scene->objects; object->type; object++)
+		render->objects[i++] = ray_render_object_prepare(object, camera);
+
+	return render;
+}
+
+
+void ray_render_free(ray_render_t *render)
+{
+	free(render);
+}
diff --git a/src/libs/ray/ray_render.h b/src/libs/ray/ray_render.h
new file mode 100644
index 0000000..dfd769f
--- /dev/null
+++ b/src/libs/ray/ray_render.h
@@ -0,0 +1,15 @@
+#ifndef _RAY_RENDER_H
+#define _RAY_RENDER_H
+
+#include "fb.h"
+
+#include "ray_camera.h"
+#include "ray_scene.h"
+
+typedef struct ray_render_t ray_render_t;
+
+ray_render_t * ray_render_new(const ray_scene_t *scene, const ray_camera_t *camera);
+void ray_render_free(ray_render_t *render);
+void ray_render_trace_fragment(ray_render_t *render, fb_fragment_t *fb_fragment);
+
+#endif
diff --git a/src/libs/ray/ray_render_object.h b/src/libs/ray/ray_render_object.h
new file mode 100644
index 0000000..1136eee
--- /dev/null
+++ b/src/libs/ray/ray_render_object.h
@@ -0,0 +1,121 @@
+#ifndef _RAY_RENDER_OBJECT_H
+#define _RAY_RENDER_OBJECT_H
+
+#include <assert.h>
+
+#include "ray_camera.h"
+#include "ray_object.h"
+#include "ray_object_type.h"
+#include "ray_render_object_plane.h"
+#include "ray_render_object_point.h"
+#include "ray_render_object_sphere.h"
+#include "ray_ray.h"
+#include "ray_surface.h"
+
+typedef union ray_render_object_t {
+	ray_object_type_t		type;
+	ray_render_object_sphere_t	sphere;
+	ray_render_object_point_t	point;
+	ray_render_object_plane_t	plane;
+} ray_render_object_t;
+
+
+
+/* Prepare an object for rendering.
+ * If the object has any pre-calculating to do, this is where it happens.
+ * The pre-calculated stuff is object-resident under a _prepared struct member.
+ */
+static inline ray_render_object_t ray_render_object_prepare(const ray_object_t *object, const ray_camera_t *camera)
+{
+	ray_render_object_t	prepared = { .type = object->type };
+
+	switch (object->type) {
+	case RAY_OBJECT_TYPE_SPHERE:
+		prepared.sphere = ray_render_object_sphere_prepare(&object->sphere, camera);
+		break;
+
+	case RAY_OBJECT_TYPE_POINT:
+		prepared.point = ray_render_object_point_prepare(&object->point, camera);
+		break;
+
+	case RAY_OBJECT_TYPE_PLANE:
+		prepared.plane = ray_render_object_plane_prepare(&object->plane, camera);
+		break;
+
+	case RAY_OBJECT_TYPE_LIGHT:
+		/* TODO */
+		break;
+
+	default:
+		assert(0);
+	}
+
+	return prepared;
+}
+
+
+/* Determine if a ray intersects object.
+ * If the object is intersected, store where along the ray the intersection occurs in res_distance.
+ */
+static inline int ray_render_object_intersects_ray(ray_render_object_t *object, unsigned depth, ray_ray_t *ray, float *res_distance)
+{
+	switch (object->type) {
+	case RAY_OBJECT_TYPE_SPHERE:
+		return ray_render_object_sphere_intersects_ray(&object->sphere, depth, ray, res_distance);
+
+	case RAY_OBJECT_TYPE_POINT:
+		return ray_render_object_point_intersects_ray(&object->point, depth, ray, res_distance);
+
+	case RAY_OBJECT_TYPE_PLANE:
+		return ray_render_object_plane_intersects_ray(&object->plane, depth, ray, res_distance);
+
+	case RAY_OBJECT_TYPE_LIGHT:
+		/* TODO */
+	default:
+		assert(0);
+	}
+}
+
+
+/* Return the surface normal of object @ point */
+static inline ray_3f_t ray_render_object_normal(ray_render_object_t *object, ray_3f_t *point)
+{
+	switch (object->type) {
+	case RAY_OBJECT_TYPE_SPHERE:
+		return ray_render_object_sphere_normal(&object->sphere, point);
+
+	case RAY_OBJECT_TYPE_POINT:
+		return ray_render_object_point_normal(&object->point, point);
+
+	case RAY_OBJECT_TYPE_PLANE:
+		return ray_render_object_plane_normal(&object->plane, point);
+
+	case RAY_OBJECT_TYPE_LIGHT:
+		/* TODO */
+	default:
+		assert(0);
+	}
+}
+
+
+/* Return the surface of object @ point */
+static inline ray_surface_t ray_render_object_surface(ray_render_object_t *object, ray_3f_t *point)
+{
+	switch (object->type) {
+	case RAY_OBJECT_TYPE_SPHERE:
+		return ray_render_object_sphere_surface(&object->sphere, point);
+
+	case RAY_OBJECT_TYPE_POINT:
+		return ray_render_object_point_surface(&object->point, point);
+
+	case RAY_OBJECT_TYPE_PLANE:
+		return ray_render_object_plane_surface(&object->plane, point);
+
+	case RAY_OBJECT_TYPE_LIGHT:
+		/* TODO */
+	default:
+		assert(0);
+	}
+}
+
+#endif
diff --git a/src/libs/ray/ray_render_object_plane.h b/src/libs/ray/ray_render_object_plane.h
new file mode 100644
index 0000000..6e00089
--- /dev/null
+++ b/src/libs/ray/ray_render_object_plane.h
@@ -0,0 +1,61 @@
+#ifndef _RAY_RENDER_OBJECT_PLANE_H
+#define _RAY_RENDER_OBJECT_PLANE_H
+
+#include "ray_3f.h"
+#include "ray_camera.h"
+#include "ray_object_plane.h"
+#include "ray_object_type.h"
+#include "ray_ray.h"
+#include "ray_surface.h"
+
+
+typedef struct ray_render_object_plane_t {
+	ray_object_plane_t	object;
+	float			primary_dot_plus;
+} ray_render_object_plane_t;
+
+
+static ray_render_object_plane_t ray_render_object_plane_prepare(const ray_object_plane_t *plane, const ray_camera_t *camera)
+{
+	ray_render_object_plane_t	prepared = { .object = *plane };
+
+	prepared.primary_dot_plus = ray_3f_dot(&plane->normal, &camera->position) + plane->distance;
+
+	return prepared;
+}
+
+
+static inline int ray_render_object_plane_intersects_ray(ray_render_object_plane_t *plane, unsigned depth, ray_ray_t *ray, float *res_distance)
+{
+	float	d = ray_3f_dot(&plane->object.normal, &ray->direction);
+
+	if (d < 0.0f) {
+		float	distance = plane->primary_dot_plus;
+
+		if (depth)
+			distance = (ray_3f_dot(&plane->object.normal, &ray->origin) + plane->object.distance);
+
+		distance /= -d;
+		if (distance > 0.0f) {
+			*res_distance = distance;
+
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static inline ray_3f_t ray_render_object_plane_normal(ray_render_object_plane_t *plane, ray_3f_t *point)
+{
+	return plane->object.normal;
+}
+
+
+static inline ray_surface_t ray_render_object_plane_surface(ray_render_object_plane_t *plane, ray_3f_t *point)
+{
+	return plane->object.surface;
+}
+
+#endif
diff --git a/src/libs/ray/ray_render_object_point.h b/src/libs/ray/ray_render_object_point.h
new file mode 100644
index 0000000..52c6fd6
--- /dev/null
+++ b/src/libs/ray/ray_render_object_point.h
@@ -0,0 +1,45 @@
+#ifndef _RAY_RENDER_OBJECT_POINT_H
+#define _RAY_RENDER_OBJECT_POINT_H
+
+#include "ray_3f.h"
+#include "ray_camera.h"
+#include "ray_object_point.h"
+#include "ray_object_type.h"
+#include "ray_ray.h"
+#include "ray_surface.h"
+
+
+typedef struct ray_render_object_point_t {
+	ray_object_point_t	object;
+} ray_render_object_point_t;
+
+
+static ray_render_object_point_t ray_render_object_point_prepare(const ray_object_point_t *point, const ray_camera_t *camera)
+{
+	ray_render_object_point_t	prepared = { .object = *point };
+
+	return prepared;
+}
+
+
+static inline int ray_render_object_point_intersects_ray(ray_render_object_point_t *point, unsigned depth, ray_ray_t *ray, float *res_distance)
+{
+	/* TODO: determine a ray:point intersection */
+	return 0;
+}
+
+
+static inline ray_3f_t ray_render_object_point_normal(ray_render_object_point_t *point, ray_3f_t *_point)
+{
+	ray_3f_t	normal;
+
+	return normal;
+}
+
+
+static inline ray_surface_t ray_render_object_point_surface(ray_render_object_point_t *point, ray_3f_t *_point)
+{
+	return point->object.surface;
+}
+
+#endif
diff --git a/src/libs/ray/ray_render_object_sphere.h b/src/libs/ray/ray_render_object_sphere.h
new file mode 100644
index 0000000..addf1f5
--- /dev/null
+++ b/src/libs/ray/ray_render_object_sphere.h
@@ -0,0 +1,90 @@
+#ifndef _RAY_RENDER_OBJECT_SPHERE_H
+#define _RAY_RENDER_OBJECT_SPHERE_H
+
+#include <math.h>
+
+#include "ray_3f.h"
+#include "ray_camera.h"
+#include "ray_color.h"
+#include "ray_object_sphere.h"
+#include "ray_object_type.h"
+#include "ray_ray.h"
+#include "ray_surface.h"
+
+
+typedef struct ray_render_object_sphere_t {
+	ray_object_sphere_t	object;
+	ray_3f_t		primary_v;
+	float			primary_dot_vv;
+	float			r2;
+	float			r_inv;
+} ray_render_object_sphere_t;
+
+
+static ray_render_object_sphere_t ray_render_object_sphere_prepare(const ray_object_sphere_t *sphere, const ray_camera_t *camera)
+{
+	ray_render_object_sphere_t	prepared = { .object = *sphere };
+
+	prepared.primary_v = ray_3f_sub(&sphere->center, &camera->position);
+	prepared.primary_dot_vv = ray_3f_dot(&prepared.primary_v, &prepared.primary_v);
+
+	prepared.r2 = sphere->radius * sphere->radius;
+
+	/* to divide by radius via multiplication in ray_object_sphere_normal() */
+	prepared.r_inv = 1.0f / sphere->radius;
+
+	return prepared;
+}
+
+
+static inline int ray_render_object_sphere_intersects_ray(ray_render_object_sphere_t *sphere, unsigned depth, ray_ray_t *ray, float *res_distance)
+{
+	ray_3f_t	v = sphere->primary_v;
+	float		dot_vv = sphere->primary_dot_vv;
+	float		b, disc;
+
+	if (depth) {
+		v = ray_3f_sub(&sphere->object.center, &ray->origin);
+		dot_vv = ray_3f_dot(&v, &v);
+	}
+
+	b = ray_3f_dot(&v, &ray->direction);
+	disc = sphere->r2 - (dot_vv - (b * b));
+	if (disc > 0) {
+		float	i1, i2;
+
+		disc = sqrtf(disc);
+
+		i1 = b - disc;
+		i2 = b + disc;
+
+		if (i2 > 0 && i1 > 0) {
+			*res_distance = i1;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+/* return the normal of the surface at the specified point */
+static inline ray_3f_t ray_render_object_sphere_normal(ray_render_object_sphere_t *sphere, ray_3f_t *point)
+{
+	ray_3f_t	normal;
+
+	normal = ray_3f_sub(point, &sphere->object.center);
+	normal = ray_3f_mult_scalar(&normal, sphere->r_inv);	/* normalize without the sqrt() */
+
+	return normal;
+}
+
+
+/* return the surface of the sphere @ point */
+static inline ray_surface_t ray_render_object_sphere_surface(ray_render_object_sphere_t *sphere, ray_3f_t *point)
+{
+	/* uniform solids for now... */
+	return sphere->object.surface;
+}
+
+#endif
diff --git a/src/libs/ray/ray_scene.h b/src/libs/ray/ray_scene.h
new file mode 100644
index 0000000..ff9c440
--- /dev/null
+++ b/src/libs/ray/ray_scene.h
@@ -0,0 +1,16 @@
+#ifndef _RAY_SCENE_H
+#define _RAY_SCENE_H
+
+#include "ray_color.h"
+
+typedef union ray_object_t ray_object_t;
+
+typedef struct ray_scene_t {
+	ray_object_t	*objects;
+	ray_object_t	*lights;
+
+	ray_color_t	ambient_color;
+	float		ambient_brightness;
+} ray_scene_t;
+
+#endif
diff --git a/src/libs/ray/ray_surface.h b/src/libs/ray/ray_surface.h
new file mode 100644
index 0000000..2e7544b
--- /dev/null
+++ b/src/libs/ray/ray_surface.h
@@ -0,0 +1,15 @@
+#ifndef _RAY_MATERIAL_H
+#define _RAY_MATERIAL_H
+
+#include "ray_3f.h"
+#include "ray_color.h"
+
+/* Surface properties we expect every object to be able to introspect */
+typedef struct ray_surface_t {
+	ray_color_t	color;
+	float		specular;
+	float		highlight_exponent;
+	float		diffuse;
+} ray_surface_t;
+
+#endif
-- 
cgit v1.2.3