~zjm/Moon3D

10a45062adc42df7a692f138bec4ea19c9dcd1dd — Zack Michener a month ago e6657c5 refactor
move dither to new src; remove old src and test
72 files changed, 1 insertions(+), 3907 deletions(-)

R src2/3d/{device/dither.c => er.c}
R src2/3d/{device/dither.h => er.h}
M src/moon3d.h
D src2/3d.h
D src2/3d/abstract.h
D src2/3d/abstract/aabbox.c
D src2/3d/abstract/aabbox.h
D src2/3d/abstract/color.h
D src2/3d/abstract/plane.c
D src2/3d/abstract/plane.h
D src2/3d/abstract/point.c
D src2/3d/abstract/point.h
D src2/3d/abstract/polygon.c
D src2/3d/abstract/polygon.h
D src2/3d/abstract/transform.c
D src2/3d/abstract/transform.h
D src2/3d/abstract/vertex.c
D src2/3d/abstract/vertex.h
D src2/3d/device.h
D src2/3d/device/endpoint.c
D src2/3d/device/endpoint.h
D src2/3d/device/rasterize.c
D src2/3d/device/rasterize.h
D src2/3d/device/viewport.c
D src2/3d/device/viewport.h
D src2/3d/image.h
D src2/3d/image/camera.c
D src2/3d/image/camera.h
D src2/3d/image/clip.c
D src2/3d/image/clip.h
D src2/3d/image/project.c
D src2/3d/image/project.h
D src2/3d/render.c
D src2/3d/render.h
D src2/3d/vertex_pool.c
D src2/3d/vertex_pool.h
D src2/3d/world.h
D src2/3d/world/light.c
D src2/3d/world/light.h
D src2/3d/world/mesh.c
D src2/3d/world/mesh.h
D src2/3d/world/object.c
D src2/3d/world/object.h
D src2/arraylist.h
D src2/input.c
D src2/input.h
D src2/main.c
D src2/player.c
D src2/player.h
D src2/scene.c
D src2/scene.h
D src2/scenes/cubeworld.c
D src2/scenes/cubeworld.h
D src2/scenes/moonworld.c
D src2/scenes/moonworld.h
D src2/sketch.c
D src2/sketch.h
D test2/assertions.h
D test2/assertions.test.c
D test2/clip.test.c
D test2/color.test.c
D test2/endpoint.test.c
D test2/light.test.c
D test2/main.test.c
D test2/model.test.c
D test2/performance.test.c
D test2/project.test.c
D test2/rasterize.test.c
D test2/render.test.c
D test2/tests.h
D test2/transform.test.c
D test2/vector.test.c
R src2/3d/device/dither.c => src/dither.c +0 -0

R src2/3d/device/dither.h => src/dither.h +0 -1
@@ 1,6 1,5 @@
#pragma once

#include "../abstract.h"
#include "viewport.h"

void Dither(Viewport *frame);

M src/moon3d.h => src/moon3d.h +1 -0
@@ 2,6 2,7 @@

#include "camera.h"
#include "clip.h"
#include "dither.h"
#include "mesh.h"
#include "model.h"
#include "obj.h"

D src2/3d.h => src2/3d.h +0 -7
@@ 1,7 0,0 @@
#pragma once

#include "3d/abstract.h"
#include "3d/device.h"
#include "3d/image.h"
#include "3d/world.h"
#include "3d/render.h"

D src2/3d/abstract.h => src2/3d/abstract.h +0 -9
@@ 1,9 0,0 @@
#pragma once

#include "abstract/aabbox.h"
#include "abstract/color.h"
#include "abstract/plane.h"
#include "abstract/point.h"
#include "abstract/polygon.h"
#include "abstract/transform.h"
#include "abstract/vertex.h"

D src2/3d/abstract/aabbox.c => src2/3d/abstract/aabbox.c +0 -53
@@ 1,53 0,0 @@
#include "aabbox.h"
#include <stdio.h>
#include <stdlib.h>

AABBox *NewAABBox(double left, double right, double bottom, double top,
		double far, double near)
{
	AABBox *v = malloc(sizeof(AABBox));
	v->left = left;
	v->right = right;
	v->bottom = bottom;
	v->top = top;
	v->far = far;
	v->near = near;
	return v;
}

AABBox Vol(double left, double right, double bottom, double top, double far, double near)
{
	AABBox v = { left, right, bottom, top, near, far };
	return v;
}

AABBox *FromAABBox(AABBox *volume, Transform transform)
{
	AABBox *newAABBox;
	Vertex rightTopNear = V(
			volume->right,
			volume->top,
			volume->near);
	Vertex leftBottomFar = V(
			volume->left,
			volume->bottom,
			volume->far);

	CommitTransform(transform, &rightTopNear);
	CommitTransform(transform, &leftBottomFar);

	newAABBox = NewAABBox(
			leftBottomFar.x,
			rightTopNear.x,
			leftBottomFar.y,
			rightTopNear.y,
			leftBottomFar.z,
			rightTopNear.z);
	return newAABBox;
}

void PrintAABBox(AABBox V)
{
	printf("           %- 6.2f\n         ┌────────┐\n      ┌──┴─────┐ ─┼──%- 6.2f\n      │        │  │%- 6.2f\n%- 6.2f│%- 6.2f  │  │\n      │        ├──┘\n      └────────┘\n        %- 6.2f\n\n",
		V.top, V.far, V.right, V.left, V.near, V.bottom);
}

D src2/3d/abstract/aabbox.h => src2/3d/abstract/aabbox.h +0 -18
@@ 1,18 0,0 @@
#pragma once

#include "transform.h"

// "Axis-Aligned Bounding Box"
typedef struct AABBox {
	double left;
	double right;
	double bottom;
	double top;
	double near;
	double far;
} AABBox;

AABBox *NewAABBox(double left, double right, double bottom, double top, double far, double near);
AABBox Vol(double left, double right, double bottom, double top, double far, double near);
AABBox *FromAABBox(AABBox *volume, Transform transform);
void PrintAABBox(AABBox vol);

D src2/3d/abstract/color.h => src2/3d/abstract/color.h +0 -15
@@ 1,15 0,0 @@
#pragma once

#include <stdint.h>

typedef double Color;
typedef uint32_t Pixel;

#define BLACK	0.0
#define WHITE	1.0
#define GRAY	0.5
#define LT_GRAY 0.67
#define DK_GRAY 0.33

#define ColorPixel(X) (0xFF000000 | ((uint32_t)((X)*0xFF) << 16) | ((uint32_t)((X)*0xFF) << 8) | (uint32_t)((X)*0xFF))
#define PixelColor(X) ((double)((X) & 0x000000FF) / (double)0xFF)

D src2/3d/abstract/plane.c => src2/3d/abstract/plane.c +0 -9
@@ 1,9 0,0 @@
#include "plane.h"

Plane Pl(Vertex ref, Vector normal)
{
	Plane p;
	p.ref = ref;
	p.normal = normal;
	return p;
}

D src2/3d/abstract/plane.h => src2/3d/abstract/plane.h +0 -10
@@ 1,10 0,0 @@
#pragma once

#include "vertex.h"

typedef struct Plane {
	Vertex ref;
	Vector normal;
} Plane;

Plane Pl(Vertex ref, Vector normal);

D src2/3d/abstract/point.c => src2/3d/abstract/point.c +0 -10
@@ 1,10 0,0 @@
#include "point.h"
#include <stdlib.h>

Point *NewPoint(Vector position, Color color)
{
	Point *p = malloc(sizeof(Point));
	p->position = position;
	p->color = color;
	return p;
}

D src2/3d/abstract/point.h => src2/3d/abstract/point.h +0 -11
@@ 1,11 0,0 @@
#pragma once

#include "vertex.h"
#include "color.h"

typedef struct Point {
	Vector position;
	Color color;
} Point;

Point *NewPoint(Vector position, Color color);

D src2/3d/abstract/polygon.c => src2/3d/abstract/polygon.c +0 -152
@@ 1,152 0,0 @@
#include "polygon.h"
#include "../../arraylist.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

Polygon *NewPolygon(void)
{
  Polygon *p = malloc(sizeof(Polygon));
  p->vertices = NULL;
  p->vList = NULL;
  return p;
}

void AddVertex(Polygon *polygon, Vertex *vertex)
{
  vIndex vi = FindVertex(polygon->vertices, vertex);
  if (vi == VERTEX_NOT_FOUND) {
    vi = list_count(polygon->vertices);
    list_push(polygon->vertices, vertex);
  }
  list_push(polygon->vList, vi);
}

Polygon *FromPolygon(Polygon *polygon, Transform transform)
{
  Polygon *newPoly = NewPolygon();
  int i;
  Vertex *newVert;

  // transform all the polygon's vertices
  for (i = 0; i < NumVertices(polygon); i++) {
    newVert = NewVertex(0, 0, 0);
    *newVert = ApplyTransform(transform, *PolygonVertex(polygon, i));
    list_push(newPoly->vList, list_count(newPoly->vertices));
    list_push(newPoly->vertices, newVert);
  }

  return newPoly;
}

Polygon *CopyPolygon(Polygon *polygon)
{
  Polygon *newPoly = NewPolygon();
  int i;
  Vertex *newVert;

  // transform all the polygon's vertices
  for (i = 0; i < NumVertices(polygon); i++) {
    newVert = NewVertex(0, 0, 0);
    *newVert = *PolygonVertex(polygon, i);
    list_push(newPoly->vList, list_count(newPoly->vertices));
    list_push(newPoly->vertices, newVert);
  }

  return newPoly;
}

Vertex *PolygonVertex(Polygon *polygon, int i)
/* returns the ith vertex in a polygon */
{
  int index = i % NumVertices(polygon);
  return polygon->vertices[polygon->vList[index]];
}

Vector PolygonCenter(Polygon *polygon)
{
  double x = 0, y = 0, z = 0;
  int n = NumVertices(polygon);
  for (int i = 0; i < n; i++) {
    Vertex *v = PolygonVertex(polygon, i);
    x += v->x;
    y += v->y;
    z += v->z;
  }

  return V(x/n, y/n, z/n);
}

int NumVertices(Polygon *polygon)
{
  return list_count(polygon->vList);
}

Vector PolygonNormal(Polygon *polygon)
{
  assert(NumVertices(polygon) >= 3);
  Vertex a = Homogenize(*PolygonVertex(polygon, 0));
  Vertex b = Homogenize(*PolygonVertex(polygon, 1));
  Vertex c = Homogenize(*PolygonVertex(polygon, 2));
  Vector n = NormalizeVector(CrossProd(VecSub(b, a), VecSub(c, b)));
  return n;
}

void HomogenizePolygon(Polygon *polygon)
{
  for (int i = 0; i < NumVertices(polygon); i++) {
    Vertex *v = PolygonVertex(polygon, i);
    *v = Homogenize(*v);
  }
}

Polygon **Triangularize(Polygon *polygon)
{
  Polygon **triangles = NULL;

  if (NumVertices(polygon) < 3) {
    return NULL;
  } else if (NumVertices(polygon) == 3) {
    list_push(triangles, polygon);
  } else {
    Vertex *a = PolygonVertex(polygon, 0);
    for (int i = 1; i < NumVertices(polygon) - 1; i++) {
      Vertex *b = PolygonVertex(polygon, i);
      Vertex *c = PolygonVertex(polygon, i + 1);
      Polygon *tri = NewPolygon();
      AddVertex(tri, a);
      AddVertex(tri, b);
      AddVertex(tri, c);
      list_push(triangles, tri);
    }
  }

  return triangles;
}

bool IsTriangle(Polygon *polygon)
{
  return NumVertices(polygon) == 3;
}

void FreePolygon(Polygon *p)
{
  int i;
  for (i = 0; i < list_count(p->vertices); i++) {
    free(p->vertices[i]);
  }
  list_free(p->vertices);
  list_free(p->vList);
  free(p);
}

void PrintPolygon(Polygon *polygon)
{
	int i;
	Vertex *v;
	printf("%d-gon:\n", NumVertices(polygon));
	for (i = 0; i < NumVertices(polygon); i++) {
		v = PolygonVertex(polygon, i);
		PrintVec(*v);
	}
}

D src2/3d/abstract/polygon.h => src2/3d/abstract/polygon.h +0 -25
@@ 1,25 0,0 @@
#pragma once

#include "vertex.h"
#include "transform.h"
#include <stdbool.h>

typedef struct Polygon {
	vIndex *vList;		// list of indices into vertices of polygon vertices, CCW order
	Vertex **vertices;	// list of possible vertices in polygon
} Polygon;

Polygon *NewPolygon();
void AddVertex(Polygon *polygon, Vertex *vertex);
Polygon *FromPolygon(Polygon *polygon, Transform transform);
Polygon *CopyPolygon(Polygon *polygon);
Vertex *PolygonVertex(Polygon *polygon, int i);
Vector PolygonCenter(Polygon *polygon);
int NumVertices(Polygon *polygon);
Vector PolygonNormal(Polygon *polygon);
void HomogenizePolygon(Polygon *polygon);
Polygon **Triangularize(Polygon *polygon);
bool IsTriangle(Polygon *polygon);
void FreePolygon(Polygon *p);

void PrintPolygon(Polygon *polygon);

D src2/3d/abstract/transform.c => src2/3d/abstract/transform.c +0 -251
@@ 1,251 0,0 @@
#include "transform.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>

Transform Identity(void)
{
	Transform t;
	SetIdentity(&t);
	return t;
}

void SetIdentity(Transform *m)
{
	m->m[0][0] = 1;
	m->m[0][1] = 0;
	m->m[0][2] = 0;
	m->m[0][3] = 0;

	m->m[1][0] = 0;
	m->m[1][1] = 1;
	m->m[1][2] = 0;
	m->m[1][3] = 0;

	m->m[2][0] = 0;
	m->m[2][1] = 0;
	m->m[2][2] = 1;
	m->m[2][3] = 0;

	m->m[3][0] = 0;
	m->m[3][1] = 0;
	m->m[3][2] = 0;
	m->m[3][3] = 1;
}

bool IsIdentity(Transform t)
{
	return
	t.m[0][0] == 1 && t.m[0][1] == 0 && t.m[0][2] == 0 && t.m[0][3] == 0 &&
	t.m[1][0] == 0 && t.m[1][1] == 1 && t.m[1][2] == 0 && t.m[1][3] == 0 &&
	t.m[2][0] == 0 && t.m[2][1] == 0 && t.m[2][2] == 1 && t.m[2][3] == 0 &&
	t.m[3][0] == 0 && t.m[3][1] == 0 && t.m[3][2] == 0 && t.m[3][3] == 1;
}

Transform *NewTransform(void)
{
	Transform *t = malloc(sizeof(Transform));
	SetIdentity(t);
	return t;
}

Transform FromTransform(Transform transform)
{
	int i, j;
	Transform newTransform = Identity();
	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++) {
			newTransform.m[i][j] = transform.m[i][j];
		}
	}
	return newTransform;
}

void AddTransform(Transform A, Transform *subject)
{
	int i, j, s;
	Transform B = FromTransform(*subject);

	for (i = 0; i < 4; i++) {
		for (j = 0; j < 4; j++) {
			subject->m[i][j] = 0;
			for (s = 0; s < 4; s++) {
				subject->m[i][j] = subject->m[i][j] + A.m[i][s] * B.m[s][j];
			}
		}
	}
}

Transform ComposeTransform(Transform A, Transform B)
{
	Transform C;

	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			C.m[i][j] = 0;
			for (int s = 0; s < 4; s++) {
				C.m[i][j] = C.m[i][j] + A.m[i][s] * B.m[s][j];
			}
		}
	}

	return C;
}

Vector ApplyTransform(Transform m, Vector v)
/* Multiplies the transform matrix by the vector */
{
	double x, y, z, W;
	Vector result;

	x = m.m[0][0] * v.x +
		m.m[0][1] * v.y +
		m.m[0][2] * v.z +
		m.m[0][3];
	y = m.m[1][0] * v.x +
		m.m[1][1] * v.y +
		m.m[1][2] * v.z +
		m.m[1][3];
	z = m.m[2][0] * v.x +
		m.m[2][1] * v.y +
		m.m[2][2] * v.z +
		m.m[2][3];
	W = m.m[3][0] * v.x +
		m.m[3][1] * v.y +
		m.m[3][2] * v.z +
		m.m[3][3];

	// printf("w: %f, %f, %f, %f\n", x, y, z, W);
	result.x = x;
	result.y = y;
	result.z = z;
	result.w = W;
	return result;
}

void CommitTransform(Transform t, Vector *v)
{
	*v = Homogenize(ApplyTransform(t, *v));
}

Transform RotationX(double angle)
{
	Transform t = Identity();
	t.m[1][1] = cos(Rad(angle));
	t.m[1][2] = -sin(Rad(angle));
	t.m[2][1] = sin(Rad(angle));
	t.m[2][2] = cos(Rad(angle));
	return t;
}

Transform *RotateX(Transform *t, double angle)
{
	Transform rotation = RotationX(angle);
	AddTransform(rotation, t);
	return t;
}

Transform RotationY(double angle)
{
	Transform t = Identity();
	t.m[0][0] = cos(Rad(angle));
	t.m[0][2] = sin(Rad(angle));
	t.m[2][0] = -sin(Rad(angle));
	t.m[2][2] = cos(Rad(angle));
	return t;
}

Transform *RotateY(Transform *t, double angle)
{
	Transform rotation = RotationY(angle);
	AddTransform(rotation, t);
	return t;
}

Transform RotationZ(double angle)
{
	Transform t = Identity();
	t.m[0][0] = cos(Rad(angle));
	t.m[0][1] = -sin(Rad(angle));
	t.m[1][0] = sin(Rad(angle));
	t.m[1][1] = cos(Rad(angle));
	return t;
}

Transform *RotateZ(Transform *t, double angle)
{
	Transform rotation = RotationZ(angle);
	AddTransform(rotation, t);
	return t;
}

Transform Translation(Vector dv)
{
	Transform T = Identity();
	dv = Homogenize(dv);
	T.m[0][3] = dv.x;
	T.m[1][3] = dv.y;
	T.m[2][3] = dv.z;
	return T;
}

Transform *Translate(Transform *t, Vector p)
{
	Transform translation = Translation(p);
	AddTransform(translation, t);
	return t;
}

Transform Scalation(double sx, double sy, double sz)
{
	Transform S = Identity();
	S.m[0][0] = sx;
	S.m[1][1] = sy;
	S.m[2][2] = sz;
	return S;
}

Transform *Scale(Transform *t, double sx, double sy, double sz)
{
	Transform scale = Scalation(sx, sy, sz);
	AddTransform(scale, t);
	return t;
}

double Determinant(Transform mat)
{
	// Bareiss algorithm
	int i, j, k, n;
	double result;

	n = 4;
	Transform M = FromTransform(mat);

	for (i = 0; i < n-1; i++) {
		for (j = i + 1; j < n; j++)
			for (k = i + 1; k < n; k++) {
					M.m[j][k] = M.m[j][k] * M.m[i][i] - M.m[j][i] * M.m[i][k];
					if (i != 0) {
						M.m[j][k] = M.m[j][k] / M.m[i-1][i-1];
					}
			 }
	}

	result = M.m[n-1][n-1];
	return result;
}

void PrintMat(Transform mat)
{
	double det = Determinant(mat);
	printf("┌% 6.2f % 6.2f % 6.2f % 6.2f ┐\n",
			mat.m[0][0], mat.m[0][1], mat.m[0][2], mat.m[0][3]);
	printf("│% 6.2f % 6.2f % 6.2f % 6.2f │\n",
			mat.m[1][0], mat.m[1][1], mat.m[1][2], mat.m[1][3]);
	printf("│% 6.2f % 6.2f % 6.2f % 6.2f │\n",
			mat.m[2][0], mat.m[2][1], mat.m[2][2], mat.m[2][3]);
	printf("│% 6.2f % 6.2f % 6.2f % 6.2f │\n",
			mat.m[3][0], mat.m[3][1], mat.m[3][2], mat.m[3][3]);
	printf("└ Determinant: % 6.2f        ┘\n", det);
}

D src2/3d/abstract/transform.h => src2/3d/abstract/transform.h +0 -34
@@ 1,34 0,0 @@
#pragma once

#include "vertex.h"
#include <stdbool.h>

typedef struct Transform {
	double m[4][4];
} Transform;

#define MapRange(v, a0, a1, b0, b1) (((v)-(a0))*((b1)-(b0))/((a1)-(a0))+(b0))
#define Rad(x) ((x)*M_PI/180)

Transform Identity(void);
void SetIdentity(Transform *m);
bool IsIdentity(Transform t);
Transform *NewTransform(void);
Transform FromTransform(Transform transform);
void AddTransform(Transform A, Transform *subject);
Transform ComposeTransform(Transform A, Transform B);
Vector ApplyTransform(Transform t, Vector v);
void CommitTransform(Transform t, Vector *v);
Transform RotationX(double angle);
Transform *RotateX(Transform *t, double angle);
Transform RotationY(double angle);
Transform *RotateY(Transform *t, double angle);
Transform RotationZ(double angle);
Transform *RotateZ(Transform *t, double angle);
Transform Translation(Vector dv);
Transform *Translate(Transform *t, Vector p);
Transform Scalation(double sx, double sy, double sz);
Transform *Scale(Transform *t, double sx, double sy, double sz);
double Determinant(Transform mat);

void PrintMat(Transform mat);

D src2/3d/abstract/vertex.c => src2/3d/abstract/vertex.c +0 -150
@@ 1,150 0,0 @@
#include "vertex.h"
#include <stdio.h>
#include <stdlib.h>
#include "../../arraylist.h"
#include <math.h>
#include <assert.h>

Vector *NewVector(double x, double y, double z)
{
  Vector *v = malloc(sizeof(Vector));
  *v = V4D(x, y, z, 1);
  return v;
}

Vector *NewVector4D(double x, double y, double z, double w)
{
  Vector *v = malloc(sizeof(Vector));
  *v = V4D(x, y, z, w);
  return v;
}

Vector V(double x, double y, double z)
{
  Vector v = { x, y, z, 1 };
  return v;
}

Vector V4D(double x, double y, double z, double w)
{
  Vector v = { x, y, z, w };
  return v;
}

Vector Homogenize(Vector v4d)
{
  Vector v = {
    v4d.x / v4d.w,
    v4d.y / v4d.w,
    v4d.z / v4d.w,
    1
  };
  return v;
}

Vector *FromVector(Vector v)
{
  return NewVector4D(v.x, v.y, v.z, v.w);
}

bool VectorEqual(Vector a, Vector b)
{
  return fabs(a.x - b.x) < EPSILON &&
      fabs(a.y - b.y) < EPSILON &&
      fabs(a.z - b.z) < EPSILON &&
      fabs(a.w - b.w) < EPSILON;
}

vIndex FindVertex(Vertex **vertices, Vertex *vertex)
{
	int i;
	for (i = 0; i < list_count(vertices); i++) {
		if (VectorEqual(*vertices[i], *vertex)) {
			return i;
		}
	}
	return VERTEX_NOT_FOUND;
}


Vector NegVec(Vector v)
{
	return V(-v.x, -v.y, -v.z);
}

Vector NegVec4D(Vector v)
{
	return V4D(-v.x, -v.y, -v.z, -v.w);
}

Vector VecMul(Vector v, double s)
{
	return V(s*v.x, s*v.y, s*v.z);
}

Vector VecMul4D(Vector v, double s)
{
	return V4D(s*v.x, s*v.y, s*v.z, s*v.w);
}

Vector VecAdd(Vector a, Vector b)
{
	assert(a.w == b.w);
	return V(a.x + b.x, a.y + b.y, a.z + b.z);
}

Vector VecAdd4D(Vector a, Vector b)
{
	return V4D(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}

Vector VecSub(Vector a, Vector b)
{
	assert(a.w == b.w);
	return V(a.x - b.x, a.y - b.y, a.z - b.z);
}

Vector VecSub4D(Vector a, Vector b)
{
	return V4D(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}

double VecLen(Vector v)
{
	return sqrt(DotProd(v, v));
}

double DotProd(Vector a, Vector b)
{
	assert(a.w == b.w);
	return a.x*b.x + a.y*b.y + a.z*b.z;
}

double DotProd4D(Vector a, Vector b)
{
	return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w;
}

Vector CrossProd(Vector a, Vector b)
{
	assert(a.w == b.w);
	double x = a.y * b.z - a.z * b.y;
	double y = a.z * b.x - a.x * b.z;
	double z = a.x * b.y - a.y * b.x;

	return V(x, y, z);
}

Vector NormalizeVector(Vector v)
{
	double len = VecLen(v);
	double x = v.x / len;
	double y = v.y / len;
	double z = v.z / len;
	return V(x, y, z);
}

void PrintVec(Vector vec)
{
	printf("( %+.16e, %+.16e, %+.16e, %+.16e )\n", vec.x, vec.y, vec.z, vec.w);
}

D src2/3d/abstract/vertex.h => src2/3d/abstract/vertex.h +0 -34
@@ 1,34 0,0 @@
#pragma once

#include <stdbool.h>

#define EPSILON 0.0000001
#define VERTEX_NOT_FOUND -1
typedef int vIndex; // index into a vertex list

typedef struct Vertex {
	double x, y, z, w;
} Vector, Vertex;

Vector Homogenize(Vector v4d);
bool VectorEqual(Vector a, Vector b);
#define VertexEqual(a, b) VectorEqual(a, b)
Vector V(double x, double y, double z);
Vector V4D(double x, double y, double z, double w);
vIndex FindVertex(Vertex **vertices, Vertex *vertex);

Vector NegVec(Vector vector);
Vector NegVec4D(Vector v);
Vector VecMul(Vector v, double s);
Vector VecMul4D(Vector v, double s);
Vector VecAdd(Vector a, Vector b);
Vector VecAdd4D(Vector a, Vector b);
Vector VecSub(Vector a, Vector b);
Vector VecSub4D(Vector a, Vector b);
double VecLen(Vector v);
double DotProd(Vector a, Vector b);
double DotProd4D(Vector a, Vector b);
Vector CrossProd(Vector a, Vector b);
Vector NormalizeVector(Vector v);

void PrintVec(Vector vec);

D src2/3d/device.h => src2/3d/device.h +0 -6
@@ 1,6 0,0 @@
#pragma once

#include "device/dither.h"
#include "device/endpoint.h"
#include "device/rasterize.h"
#include "device/viewport.h"

D src2/3d/device/endpoint.c => src2/3d/device/endpoint.c +0 -261
@@ 1,261 0,0 @@
#include "endpoint.h"
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include "../../arraylist.h"

Endpoint *NewEndpoint(double ymax, double x, double xinc)
{
	Endpoint *e = malloc(sizeof(Endpoint));
	e->ymax = ymax;
	e->x = x;
	e->xinc = xinc;
	e->next = NULL;
	return e;
}

Endpoint *MakeEndpoint(Vertex *upper, Vertex *lower)
{
	double ymax, x, xinc, z, zinc;
	Endpoint *endpoint;

	ymax = upper->y;
	x = lower->x;
	xinc = (upper->x - lower->x) / (upper->y - lower->y);
	z = lower->z;
	zinc = (upper->z - lower->z) / (upper->y - lower->y);
	endpoint = NewEndpoint(ymax, x, xinc);

	/* add custom properties here */
	endpoint->z = z;
	endpoint->zinc = zinc;

	return endpoint;
}

void InsertEndpoint(Endpoint **list, Endpoint *endpoint)
/* insertion sort, by x */
{
	Endpoint *curNode;

	endpoint->next = NULL;

	if (*list == NULL) {
		*list = endpoint;
	} else if (endpoint->x < (*list)->x) {
		endpoint->next = *list;
		*list = endpoint;
	} else {
		curNode = *list;
		while (curNode->next != NULL
				&& endpoint->x > curNode->next->x ) {
			curNode = curNode->next;
		}

		endpoint->next = curNode->next;
		curNode->next = endpoint;
	}
}

void InsertEndpoints(Endpoint **list, Endpoint *endpoint)
{
	Endpoint *curNode, *nextNode;

	curNode = endpoint;
	while (curNode != NULL) {
		nextNode = curNode->next;
		InsertEndpoint(list, curNode);
		curNode = nextNode;
	}
}

void IncrementEndpoints(Endpoint **endpoints, int y)
{
	Endpoint *curNode;
	curNode = *endpoints;

	while(curNode != NULL) {
		if (floor(curNode->ymax) != y + 1) {
			curNode->x += curNode->xinc;
			curNode->z += curNode->zinc;
		} else {
			curNode->x += curNode->xinc*(curNode->ymax - (y + 1));
		}
		curNode = curNode->next;
	}
}

void SortEndpoints(Endpoint **endpoints)
/* Bubble sort, 1 pass */
{
	Endpoint *curNode, *a, *b, *c;

	if (*endpoints == NULL || (*endpoints)->next == NULL)
		return;

	a = *endpoints;
	b = a->next;
	if (a->x > b->x) {
		c = b->next;
		*endpoints = b;
		b->next = a;
		a->next = c;
	}

	curNode = *endpoints;
	while(curNode != NULL &&
			curNode->next != NULL &&
			curNode->next->next != NULL) {
		a = curNode->next;
		b = a->next;
		if (a->x > b->x) {
			c = b->next;
			curNode->next = b;
			b->next = a;
			a->next = c;
		}

		curNode = curNode->next;
	}
}

void RemoveEndpoints(Endpoint **list, int y)
{
	Endpoint *curNode, *oldNode;

	while ((*list != NULL) && floor((*list)->ymax) == y) {
		oldNode = *list;
		*list = (*list)->next;
		free(oldNode);
	}
	if (*list == NULL) {
		return;
	}

	curNode = *list;
	while (curNode != NULL) {
		while (curNode->next != NULL && floor(curNode->next->ymax) == y) {
			oldNode = curNode->next;
			curNode->next = curNode->next->next;
			free(oldNode);
		}
		curNode = curNode->next;
	}
}

void FreeEndpoints(Endpoint *endpoints)
{
	Endpoint *curNode = endpoints;
	Endpoint *oldNode;
	while (curNode != NULL) {
		oldNode = curNode;
		curNode = curNode->next;
		free(oldNode);
	}
}

Endpoint **MakeEdgeTable(unsigned int numLines, Polygon *polygon)
{
	Endpoint **buckets = malloc(sizeof(Endpoint *)*numLines);
	vIndex i;
	unsigned int bucket;
	Vertex *a, *b, *upper, *lower;

	for (bucket = 0; bucket < numLines; bucket++) {
		buckets[bucket] = NULL;
	}

	for (i = 0; i < NumVertices(polygon); i++) {
		a = PolygonVertex(polygon, i);
		b = PolygonVertex(polygon, i+1);
		lower = LowerVertex(a, b);
		upper = (a == lower) ? b : a;

		if (upper->y != lower->y) {
			bucket = floor(lower->y);
			InsertEndpoint(&buckets[bucket], MakeEndpoint(upper, lower));
		}
	}

	return buckets;
}

int FirstBucket(Endpoint **table, int length)
/* returns first non-empty table index, or -1 if table is empty */
{
	int bucket;
	for (bucket = 0; bucket < length; bucket++) {
		if (table[bucket] != NULL) {
			return bucket;
		}
	}
	return -1;
}

void FreeEdgeTable(Endpoint **table, int length)
{
	int bucket;
	for (bucket = 0; bucket < length; bucket++) {
		if (table[bucket] != NULL) {
			// FreeEndpoints(table[bucket]);
		}
	}
	free(table);
}

Vertex *LowerVertex(Vertex *a, Vertex *b)
{
	if (a->y < b->y) {
		return a;
	} else {
		return b;
	}
}

void PrintEndpoint(Endpoint *endpoint)
{
	printf("│ymax: %.2f\n", endpoint->ymax);
	printf("│x:    %.2f (%+.2f)\n", endpoint->x, endpoint->xinc);
	printf("│z:    %.2f (%+.2f)\n", endpoint->z, endpoint->zinc);
	printf("├────────\n");
	if(endpoint->next == NULL) {
		printf("┴");
	}
	printf("\n");
}

void PrintEndpoints(Endpoint *endpoints)
{
	Endpoint *curNode = endpoints;
	// printf("┌────────────\n");
	while (curNode != NULL) {
		printf("│ymax: %.2f\n", curNode->ymax);
		printf("│x:    %.2f (%+.2f)\n", curNode->x, curNode->xinc);
		printf("│z:    %.2f (%+.2f)\n", curNode->z, curNode->zinc);
		printf("├────────\n");
		curNode = curNode->next;
	}
	printf("┴\n");
}

void PrintTable(Endpoint **table, int numBuckets)
{
	for(int i = 0; i < numBuckets; i++) {
		if (table[i] != NULL) {
			printf("┌─ %d ───────────\n", i);
			// printf("%d: ", i);
			PrintEndpoints(table[i]);
		}
	}
}

int ListLength(Endpoint *list)
{
	Endpoint *curNode = list;
	int count = 0;
	while (curNode != NULL) {
		count++;
		curNode = curNode->next;
	}
	return count;
}

D src2/3d/device/endpoint.h => src2/3d/device/endpoint.h +0 -34
@@ 1,34 0,0 @@
#pragma once

#include "../abstract.h"

typedef struct Endpoint {
	double ymax;
	double x;
	double xinc;
	double z;
	double zinc;
	int color;
	struct Endpoint *next;
} Endpoint;

Endpoint *NewEndpoint(double ymax, double x, double xinc);
Endpoint *MakeEndpoint(Vertex *upper, Vertex *lower);
void InsertEndpoint(Endpoint **list, Endpoint *endpoint);
void InsertEndpoints(Endpoint **list, Endpoint *endpoint);
void IncrementEndpoints(Endpoint **endpoints, int y);
void SortEndpoints(Endpoint **endpoints);
void RemoveEndpoints(Endpoint **list, int y);
void FreeEndpoints(Endpoint *endpoints);

Endpoint **MakeEdgeTable(unsigned int numLines, Polygon *polygon);
int FirstBucket(Endpoint **table, int length);
void FreeEdgeTable(Endpoint **table, int size);

Vertex *LowerVertex(Vertex *a, Vertex *b);

// debug
void PrintEndpoint(Endpoint *endpoint);
void PrintEndpoints(Endpoint *endpoints);
void PrintTable(Endpoint **table, int numBuckets);
int ListLength(Endpoint *list);

D src2/3d/device/rasterize.c => src2/3d/device/rasterize.c +0 -154
@@ 1,154 0,0 @@

#include "rasterize.h"
#include <math.h>
#include <stddef.h>

RasterSettings DefaultRasterSettings(void)
{
	RasterSettings s;
	s.showWireframes = false;
	s.showDepthBuffer = false;
	return s;
}

void RasterPolygon(Polygon *polygon, Color color, Viewport *viewport, RasterSettings settings)
{
	int y;
	Endpoint **ET = MakeEdgeTable(viewport->height, polygon);
	Endpoint *AET = NULL;

	y = FirstBucket(ET, viewport->height);
	if (y != -1) {
		do {
			InsertEndpoints(&AET, ET[y]);
			RemoveEndpoints(&AET, y);
			IncrementEndpoints(&AET, y);
			SortEndpoints(&AET);
			ColorSpans(&AET, y, color, viewport, settings);

			y += 1;
		} while (y < viewport->height && AET != NULL);
	}

	FreeEdgeTable(ET, viewport->height);
}

void ColorSpans(Endpoint **endpoints, int y, Color color, Viewport *viewport, RasterSettings settings)
{
	Endpoint *curNode;
	int xmin, xmax, x;
	double zmin, zmax;

	curNode = *endpoints;
	while (curNode != NULL && curNode->next != NULL) {
		xmin = floor(curNode->x);
		xmax = floor(curNode->next->x);
		zmin = curNode->z;
		zmax = curNode->next->z;
		for (x = xmin; x < xmax; x++) {
			// TODO: refactor to use constant zxinc per polygon
			double z = MapRange(x, xmin, xmax, zmin, zmax);

			if (settings.showDepthBuffer) {
				color = z;
			}

			if (UpdateDepthBuffer(x, y, z, viewport)) {
				WritePixel(x, y, color, viewport);
			}
		}
		curNode = curNode->next->next;
	}
}

void RasterTriangle(Polygon *tri, Color color)
{

}

void RasterLine(Vertex a, Vertex b, Color color, Viewport *viewport)
{
	double dx = b.x - a.x;
	double dy = b.y - a.y;
	if (fabs(dy) < fabs(dx)) {
		if (a.x > b.x) {
			RasterLineShallow(b, a, color, viewport);
		} else {
			RasterLineShallow(a, b, color, viewport);
		}
	} else {
		if (a.y > b.y) {
			RasterLineSteep(b, a, color, viewport);
		} else {
			RasterLineSteep(a, b, color, viewport);
		}
	}
}

void RasterLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport)
{
	double dx = b.x - a.x;
	double dy = b.y - a.y;
	double yi = 1;
	if (dy < 0) {
		yi = -1;
		dy = -dy;
	}

	double d = 2*dy - dx;
	double x;
	double y = a.y;

	for (x = a.x; x <= b.x; x++) {
		double z = MapRange(x, a.x, b.x, a.z, b.z);
		if (UpdateDepthBuffer(x, y, z, viewport)) {
			WritePixel(x, y, color, viewport);
		}
		if (d > 0) {
			y += yi;
			d -= 2*dx;
		}
		d += 2*dy;
	}
}

void RasterLineSteep(Vertex a, Vertex b, Color color, Viewport *viewport)
{
	double dx = b.x - a.x;
	double dy = b.y - a.y;
	double xi = 1;
	if (dx < 0) {
		xi = -1;
		dx = -dx;
	}

	double d = 2*dx - dy;
	double x = a.x;
	double y;

	for (y = a.y; y <= b.y; y++) {
		double z = MapRange(y, a.y, b.y, a.z, b.z);
		if (UpdateDepthBuffer(x, y, z, viewport)) {
			WritePixel(x, y, color, viewport);
		}
		if (d > 0) {
			x += xi;
			d -= 2*dy;
		}
		d += 2*dx;
	}
}

void WritePixel(int x, int y, Color color, Viewport *viewport)
{
	Pixel p = ColorPixel(color);
	if (x >= 0 && x < viewport->width && y >= 0 && y < viewport->height) {
		viewport->pixels[(viewport->height - 1 - y)*viewport->width+x] = p;
	}
}

Color GetPixel(int x, int y, Viewport *viewport)
{
	Pixel p = viewport->pixels[(viewport->height - 1 - y)*viewport->width+x];
	return PixelColor(p);
}

D src2/3d/device/rasterize.h => src2/3d/device/rasterize.h +0 -26
@@ 1,26 0,0 @@
#pragma once

#include "endpoint.h"
#include "../abstract.h"
#include "viewport.h"
#include <stdint.h>
#include <stdbool.h>

typedef struct RasterSettings {
	bool showWireframes;
	bool showDepthBuffer;
} RasterSettings;

RasterSettings DefaultRasterSettings(void);

void RasterPolygon(Polygon *polygon, Color color, Viewport *viewport, RasterSettings settings);
void ColorSpans(Endpoint **endpoints, int y, Color color, Viewport *viewport, RasterSettings settings);

void RasterTriangle(Polygon *tri, Color color);

void RasterLine(Vertex a, Vertex b, Color color, Viewport *viewport);
void RasterLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport);
void RasterLineSteep(Vertex a, Vertex b, Color color, Viewport *viewport);

void WritePixel(int x, int y, Color color, Viewport *viewport);
Color GetPixel(int x, int y, Viewport *viewport);

D src2/3d/device/viewport.c => src2/3d/device/viewport.c +0 -66
@@ 1,66 0,0 @@
#include "viewport.h"
#include "../image.h"
#include "../render.h"
#include <stdlib.h>
#include <assert.h>
#include <math.h>

Viewport *NewViewport(Pixel *pixels, Color bg, int width, int height)
{
	Viewport *viewport = malloc(sizeof(Viewport));
	viewport->pixels = pixels;
	viewport->background = bg;
	viewport->width = width;
	viewport->height = height;
	viewport->depthbuffer = NULL;
	ClearViewport(viewport);
	InitDeviceTransform(width, height);
	// z buffer enabled by default
	AddDepthBuffer(viewport);

	return viewport;
}

void ClearViewport(Viewport *viewport)
{
	for (int y = 0; y < viewport->height; y++) {
		for (int x = 0; x < viewport->width; x++) {
			WritePixel(x, y, viewport->background, viewport);
			if (viewport->depthbuffer != NULL)
				viewport->depthbuffer[y*viewport->width + x] = -INFINITY;
		}
	}
	// AddDepthBuffer(viewport);
}

void AddDepthBuffer(struct Viewport *viewport)
{
	assert(viewport->pixels != NULL);

	DepthVal *values = malloc(sizeof(DepthVal)*viewport->width*viewport->height);
	for(int y = 0; y < viewport->height; y++) {
		for (int x = 0; x < viewport->width; x++) {
			values[y*viewport->width + x] = -INFINITY;
		}
	}

	viewport->depthbuffer = values;
}

bool UpdateDepthBuffer(int x, int y, double z, struct Viewport *viewport)
{
	assert(viewport->depthbuffer != NULL);

	if (z > viewport->depthbuffer[y*viewport->width+x]) {
		viewport->depthbuffer[y*viewport->width+x] = z;
		return true;
	} else {
		return false;
	}
}

void DisableDepthBuffer(Viewport *viewport)
{
	free(viewport->depthbuffer);
	viewport->depthbuffer = NULL;
}

D src2/3d/device/viewport.h => src2/3d/device/viewport.h +0 -20
@@ 1,20 0,0 @@
#pragma once

#include "../abstract.h"

typedef double DepthVal;

typedef struct Viewport {
	Color background;
	int width;
	int height;
	Pixel *pixels;
	DepthVal *depthbuffer;
} Viewport;

Viewport *NewViewport(Pixel *pixels, Color bg, int width, int height);
Viewport *GetViewport(void);
void ClearViewport(Viewport *viewport);
void AddDepthBuffer(Viewport *viewport);
bool UpdateDepthBuffer(int x, int y, double z, Viewport *viewport);
void DisableDepthBuffer(Viewport *viewport);

D src2/3d/image.h => src2/3d/image.h +0 -5
@@ 1,5 0,0 @@
#pragma once

#include "image/camera.h"
#include "image/clip.h"
#include "image/project.h"
\ No newline at end of file

D src2/3d/image/camera.c => src2/3d/image/camera.c +0 -57
@@ 1,57 0,0 @@
#include "camera.h"
#include <stdlib.h>
#include <assert.h>

Camera *NewCamera(Vector position)
{
	Camera *camera = malloc(sizeof(Camera));
	camera->position = position;
	camera->direction = V(0, 0, -1);
	camera->up = V(0, 1, 0);
	camera->transform = Identity();
	return camera;
}

void LookAt(Vector p, Camera *camera)
{
	camera->direction = NormalizeVector(VecSub(p, camera->position));
}

void StandOn(Vector p, Camera *camera)
{
	camera->up = NormalizeVector(VecSub(p, camera->position));
}

void MoveCamera(Vector dv, Camera *camera)
{
	camera->position = VecAdd(camera->position, dv);
}

void RotateCamera(double rx, double ry, double rz, Camera *camera)
{
	Transform t = RotationX(rx);
	AddTransform(RotationY(ry), &t);
	AddTransform(RotationZ(rz), &t);
	camera->direction = NormalizeVector(ApplyTransform(t, camera->direction));
}

Transform AlignCameraToAxis(Camera *camera)
{
	Vector Rx, Ry, Rz;
	Transform op = Identity();
	Rz = NegVec(camera->direction);
	Rx = CrossProd(camera->up, NegVec(camera->direction));
	Ry = CrossProd(Rz, Rx);
	op.m[0][0] = Rx.x;
	op.m[0][1] = Rx.y;
	op.m[0][2] = Rx.z;

	op.m[1][0] = Ry.x;
	op.m[1][1] = Ry.y;
	op.m[1][2] = Ry.z;

	op.m[2][0] = Rz.x;
	op.m[2][1] = Rz.y;
	op.m[2][2] = Rz.z;
	return op;
}

D src2/3d/image/camera.h => src2/3d/image/camera.h +0 -18
@@ 1,18 0,0 @@
#pragma once

#include "../abstract.h"

typedef struct Camera {
	Vector position;
	Vector direction;
	Vector up;
	Transform transform;
} Camera;

Camera *NewCamera(Vector position);
void LookAt(Vector p, Camera *camera);
void StandOn(Vector p, Camera *camera);
void RotateCamera(double rx, double ry, double rz, Camera *camera);
void MoveCamera(Vector dv, Camera *camera);
Transform AlignCameraToAxis(Camera *camera);
Transform CameraTransform(Camera *camera);

D src2/3d/image/clip.c => src2/3d/image/clip.c +0 -179
@@ 1,179 0,0 @@
#include "clip.h"

#include "../../arraylist.h"

bool ClipLine(Vector *v0, Vector *v1)
/* alters v0 and v1 to fit within the canonical view volume */
{
	/* Adapted Liang-Barsky, from FvDF&H */
	double tmin = 0, tmax = 1;
	double dx = v1->x - v0->x;
	double dw = v1->w - v0->w;
	bool accept = false;

	if (ClipT(dx + dw, -v0->x - v0->w, &tmin, &tmax)) { /* x >= -w */
		if (ClipT(-dx + dw, v0->x - v0->w, &tmin, &tmax)) { /* x <= w */
			double dy = v1->y - v0->y;
			if (ClipT(dy + dw, -v0->y - v0->w, &tmin, &tmax)) { /* y >= -w */
				if (ClipT(-dy + dw, v0->y - v0->w, &tmin, &tmax)) { /* y <= w */
					double dz = v1->z - v0->z;
					if (ClipT(dz + dw, -v0->z - v0->w, &tmin, &tmax)) { /* z >= -w */
						if (ClipT(-dz + dw, v0->z - v0->w, &tmin, &tmax)) { /* z <= w */
							accept = true; /* part of line is visible */
							if (tmax < 1) {
								v1->x = v0->x + tmax * dx;
								v1->y = v0->y + tmax * dy;
								v1->z = v0->z + tmax * dz;
								v1->w = v0->w + tmax * dw;
							}
							if (tmin > 0) {
								v0->x += tmin * dx;
								v0->y += tmin * dy;
								v0->z += tmin * dz;
								v0->w += tmin * dw;
							}
						}
					}
				}
			}
		}
	}

	return accept;
}

bool ClipT(double den, double num, double *tE, double *tL)
{
	double t;

	if (den > 0) {
		/* Potentially entering clip region */
		t = num / den;
		if (t > *tL) {
			return false;
		} else if (t > *tE) {
			*tE = t;
		}
	} else if (den < 0) {
		/* Potentially leaving clip region */
		t = num / den;
		if (t < *tE) {
			return false;
		} else if (t < *tL) {
			*tL = t;
		}
	} else if (num > 0) {
		return false;
	}

	return true;
}

bool ClipPoint(Vector p)
{
	// planes in W-space
	Plane x_neg = Pl(V4D(0, 0, 0, 0), V4D(-1,  0,  0, -1));
	Plane x_pos = Pl(V4D(0, 0, 0, 0), V4D( 1,  0,  0, -1));
	Plane y_neg = Pl(V4D(0, 0, 0, 0), V4D( 0, -1,  0, -1));
	Plane y_pos = Pl(V4D(0, 0, 0, 0), V4D( 0,  1,  0, -1));
	// Plane z_neg = Pl(V4D(0, 0, 0, 0), V4D( 0,  0, -1, -1));
	// Plane z_pos = Pl(V4D(0, 0, 0, 0), V4D( 0,  0,  1, -1));

	return VertexInside(&p, x_neg)
		&& VertexInside(&p, x_pos)
		&& VertexInside(&p, y_neg)
		&& VertexInside(&p, y_pos);
		// && VertexInside(&p, z_neg)
		// && VertexInside(&p, z_pos);
}

bool ClipPolygon(Polygon **polygon)
/* alters the vertices of a polygon to fit within -w <= x, y, z <= w
 * returns whether polygon is visible after clipping */
{
	// planes in W-space
	Plane x_neg = Pl(V4D(0, 0, 0, 0), V4D(-1,  0,  0, -1));
	Plane x_pos = Pl(V4D(0, 0, 0, 0), V4D( 1,  0,  0, -1));
	Plane y_neg = Pl(V4D(0, 0, 0, 0), V4D( 0, -1,  0, -1));
	Plane y_pos = Pl(V4D(0, 0, 0, 0), V4D( 0,  1,  0, -1));
	Plane z_neg = Pl(V4D(0, 0, 0, 0), V4D( 0,  0, -1, -1));
	Plane z_pos = Pl(V4D(0, 0, 0, 0), V4D( 0,  0,  1, -1));

	ClipPolygonPlane(polygon, x_neg);
	ClipPolygonPlane(polygon, x_pos);
	ClipPolygonPlane(polygon, y_neg);
	ClipPolygonPlane(polygon, y_pos);
	ClipPolygonPlane(polygon, z_neg);
	ClipPolygonPlane(polygon, z_pos);

	// floating point errors can make some vertices still be out of bounds...
	ConstrainPolygon(*polygon);

	return NumVertices(*polygon) > 2;
}

void ConstrainPolygon(Polygon *polygon)
{
	for (int i = 0; i < NumVertices(polygon); i++) {
		Vertex *v = PolygonVertex(polygon, i);
		if (v->x < -v->w) v->x = -v->w;
		if (v->x > v->w) v->x = v->w;
		if (v->y < -v->w) v->y = -v->w;
		if (v->y > v->w) v->y = v->w;
		if (v->z < -v->w) v->z = -v->w;
		if (v->z > v->w) v->z = v->w;
	}
}

void ClipPolygonPlane(Polygon **polygon, Plane clipBoundary)
/* Sutherland-Hodgeman algorithm */
{
	Polygon *clipped = NewPolygon();
	int j;
	Vertex *s, *p, *i, *newVert;

	if ((*polygon)->vList == NULL) {
		return;
	}

	s = PolygonVertex(*polygon, list_last((*polygon)->vList));
	for (j = 0; j < NumVertices(*polygon); j++) {
		p = PolygonVertex(*polygon, j);
		if (VertexInside(p, clipBoundary)) {
			if (VertexInside(s, clipBoundary)) {
				newVert = NewVector4D(p->x, p->y, p->z, p->w);
				AddVertex(clipped, newVert);
			} else {
				i = FromVertex(ClipIntersect(s, p, clipBoundary));
				AddVertex(clipped, i);
				newVert = NewVector4D(p->x, p->y, p->z, p->w);
				AddVertex(clipped, newVert);
			}
		} else {
			if (VertexInside(s, clipBoundary)) {
				i = FromVertex(ClipIntersect(s, p, clipBoundary));
				AddVertex(clipped, i);
			}
		}
		s = p;
	}

	FreePolygon(*polygon);
	*polygon = clipped;
}

bool VertexInside(Vertex *v, Plane clipBoundary)
{
	Vector n = clipBoundary.normal;
	return DotProd4D(n, *v) <= 0;
}

Vertex ClipIntersect(Vertex *s, Vertex *p, Plane clipBoundary)
{
	Vector sp = VecSub4D(*s, *p);
	Vector n = clipBoundary.normal;

	// t = -n•p / n•(s-p)
	double t = DotProd4D(NegVec4D(n), *p) / DotProd4D(n, sp);
	return V4D(p->x + t*sp.x, p->y + t*sp.y, p->z + t*sp.z, p->w + t*sp.w);
}

D src2/3d/image/clip.h => src2/3d/image/clip.h +0 -13
@@ 1,13 0,0 @@
#pragma once

#include "../abstract.h"
#include <stdbool.h>

bool ClipT(double den, double num, double *tE, double *tL);
bool ClipLine(Vector *v0, Vector *v1);
bool ClipPoint(Vector p);
bool ClipPolygon(Polygon **polygon);
void ConstrainPolygon(Polygon *polygon);
void ClipPolygonPlane(Polygon **polygon, Plane clipBoundary);
bool VertexInside(Vertex *v, Plane clipBoundary);
Vertex ClipIntersect(Vertex *a, Vertex *b, Plane clipBoundary);

D src2/3d/image/project.c => src2/3d/image/project.c +0 -138
@@ 1,138 0,0 @@
#include "project.h"
#include "../../arraylist.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "camera.h"

static Transform *deviceTransform = NULL;

void InitDeviceTransform(unsigned int width, unsigned int height)
{
	// Move canonical volume to origin
	deviceTransform = malloc(sizeof(Transform));
	*deviceTransform = Translation(V(1, 1, 0));

	// Scale to viewport size
	Scale(deviceTransform, 0.5*(width-1), 0.5*(height-1), 1);
}

Projection *NewOrthoProjection(AABBox vol)
{
	Projection *projection = malloc(sizeof(Projection));

	projection->ortho.type = ORTHO;
	projection->ortho.near = vol.near;
	projection->ortho.far = vol.far;
	projection->ortho.left = vol.left;
	projection->ortho.right = vol.right;
	projection->ortho.bottom = vol.bottom;
	projection->ortho.top = vol.top;

	return projection;
}

Projection *NewPerspectiveProjection(double fov, double aspect, double near, double far)
/* field of view is in degrees */
{
	Projection *projection = malloc(sizeof(Projection));

	projection->perspective.type = PERSPECTIVE;
	projection->perspective.near = near;
	projection->perspective.far = far;
	projection->perspective.fov = Rad(fov);
	projection->perspective.aspect = aspect;

	return projection;
}

Transform OrthoTransform(Projection *projection)
{
	Transform t;

	assert(projection->ortho.type == ORTHO);

	t = Identity();

	// translate to canonical view volume
	Translate(&t, V(
			-(projection->ortho.right + projection->ortho.left) / 2,
			-(projection->ortho.top + projection->ortho.bottom) / 2,
			-projection->ortho.near));

	// scale to canonical view volume
	Scale(&t,
			2 / (projection->ortho.right - projection->ortho.left),
			2 / (projection->ortho.top - projection->ortho.bottom),
			1 / (projection->ortho.near - projection->ortho.far));

	return t;
}

Transform PerspectiveTransform(Projection *projection)
{
	Transform op, t;

	assert(projection->perspective.type == PERSPECTIVE);

	t = Identity();

	// perspective transform into canonical view volume
	op = Identity();
	op.m[0][0] = 1/(projection->perspective.aspect * tan(projection->perspective.fov/2));
	op.m[1][1] = 1/tan(projection->perspective.fov/2);
	op.m[2][2] = (projection->perspective.far + projection->perspective.near)
			/ (projection->perspective.far - projection->perspective.near);
	op.m[3][3] = 0;
	op.m[2][3] = (2*projection->perspective.far*projection->perspective.near)
			/ (projection->perspective.far - projection->perspective.near);
	op.m[3][2] = -1;
	AddTransform(op, &t);

	return t;
}

Transform CameraTransform(Camera *camera)
{
	Transform t = Translation(NegVec(camera->position));
	AddTransform(AlignCameraToAxis(camera), &t);
	AddTransform(camera->transform, &t);

	return t;
}

Transform GetProjectionTransform(Camera *camera, Projection *projection)
{
	Transform t = CameraTransform(camera);
	if (projection->ortho.type == ORTHO) {
		AddTransform(OrthoTransform(projection), &t);
	} else {
		AddTransform(PerspectiveTransform(projection), &t);
	}
	return t;
}

Vector ProjectPoint(Vertex p, Transform t)
{
	return ApplyTransform(t, p);
}

Vertex ViewmapPoint(Vertex p)
{
	assert(deviceTransform != NULL);
	return Homogenize(ApplyTransform(*deviceTransform, p));
}

Polygon *ProjectPolygon(Polygon *polygon, Transform t)
{
	return FromPolygon(polygon, t);
}

void ViewmapPolygon(Polygon *polygon)
{
	assert(deviceTransform != NULL);
	for (int i = 0; i < NumVertices(polygon); i++) {
		CommitTransform(*deviceTransform, PolygonVertex(polygon, i));
	}
}

D src2/3d/image/project.h => src2/3d/image/project.h +0 -46
@@ 1,46 0,0 @@
#pragma once

#include "../abstract.h"
#include <stdbool.h>
#include "camera.h"

typedef enum ProjectionType {
	ORTHO, PERSPECTIVE
} ProjectionType;

typedef struct PerspectiveProjection {
	ProjectionType type;
	double near;
	double far;
	double fov;
	double aspect;
} PerspectiveProjection;

typedef struct OrthoProjection {
	ProjectionType type;
	double near;
	double far;
	double left;
	double right;
	double bottom;
	double top;
} OrthoProjection;

typedef union Projection {
	PerspectiveProjection perspective;
	OrthoProjection ortho;
} Projection;

void InitDeviceTransform(unsigned int width, unsigned int height);

Projection *NewOrthoProjection(AABBox vol);
Projection *NewPerspectiveProjection(double fov, double aspect, double near, double far);

Transform OrthoTransform(Projection *projection);
Transform PerspectiveTransform(Projection *projection);
Transform GetProjectionTransform(Camera *camera, Projection *projection);

Vertex ProjectPoint(Vertex p, Transform t);
Vertex ViewmapPoint(Vertex p);
Polygon *ProjectPolygon(Polygon *polygon, Transform t);
void ViewmapPolygon(Polygon *polygon);

D src2/3d/render.c => src2/3d/render.c +0 -92
@@ 1,92 0,0 @@
#include "render.h"
#include "../arraylist.h"
#include "image.h"
#include "device.h"
#include "abstract.h"

RenderingContext MakeRenderingContext(Viewport *viewport, Camera *camera,
		Transform projectionTransform, RasterSettings settings)
{
	RenderingContext context;
	context.viewport = viewport;
	context.camera = camera;
	context.projectionTransform = projectionTransform;
	context.settings = settings;
	return context;
}

void DrawObject(Object *object, LightSource **lights, RenderingContext context, Transform imageTransform)
{
	Transform modelingTransform = ComposeTransform(Translation(object->position), object->transform);
	for (int i = 0; i < NumFacets(object->geometry); i++) {
		Polygon *modeled = FromPolygon(object->geometry->facets[i], modelingTransform);
		Color shade = LightPolygon(modeled, object->color, object->glow, lights);
		DrawPolygon(modeled, shade, context, imageTransform);
		FreePolygon(modeled);
	}
}

void DrawPolygon(Polygon *polygon, Color color, RenderingContext context, Transform imageTransform)
{
	Polygon *image = ProjectPolygon(polygon, imageTransform);
	if (ClipPolygon(&image)) {
		if (!BackfaceCulled(image)) {
			HomogenizePolygon(image);
			ViewmapPolygon(image);
			RasterPolygon(image, color, context.viewport, context.settings);
			if (context.settings.showWireframes) {
				WireframePolygon(image, WHITE, context.viewport);
			}
		}
	}
	FreePolygon(image);
}

void WireframePolygon(Polygon *polygon, Color color, Viewport *viewport)
{
	Vertex *a, *b;
	for (int i = 0; i < NumVertices(polygon); i++) {
		a = PolygonVertex(polygon, i);
		b = PolygonVertex(polygon, i+1);
		RasterLine(*a, *b, color, viewport);
	}
}

void DrawPoint(Point *p, RenderingContext context, Transform imageTransform)
{
	Vector projected = ProjectPoint(p->position, imageTransform);
	if (ClipPoint(projected)) {
		Vector mapped = ViewmapPoint(Homogenize(projected));
		if (UpdateDepthBuffer(mapped.x, mapped.y, mapped.z, context.viewport)) {
			WritePixel(mapped.x, mapped.y, p->color, context.viewport);
		}
	}
}

void DrawLine(Vertex a, Vertex b, Color color, Viewport *viewport, Transform imageTransform)
{
	Vector ap = ProjectPoint(a, imageTransform);
	Vector bp = ProjectPoint(b, imageTransform);
	Vector ac = ap;
	Vector bc = bp;
	if (ClipLine(&ac, &bc)) {
		Vertex av = ViewmapPoint(Homogenize(ac));
		Vertex bv = ViewmapPoint(Homogenize(bc));
		RasterLine(av, bv, color, viewport);
	}
}

void DrawAxes(RenderingContext context)
{
	Transform imageTransform = ComposeTransform(context.projectionTransform, CameraTransform(context.camera));
	DrawLine(V(0, 0, 0), V(10, 0, 0), LT_GRAY, context.viewport, imageTransform);
	DrawLine(V(0, 0, 0), V(0, 10, 0), LT_GRAY, context.viewport, imageTransform);
	DrawLine(V(0, 0, 0), V(0, 0, 10), LT_GRAY, context.viewport, imageTransform);
}

bool BackfaceCulled(Polygon *polygon)
{
	Vector normal = PolygonNormal(polygon);
	return normal.z <= 0;
}


D src2/3d/render.h => src2/3d/render.h +0 -24
@@ 1,24 0,0 @@
#pragma once

#include <stdint.h>
#include "abstract.h"
#include "world.h"
#include "device.h"
#include "image.h"

typedef struct RenderingContext {
	Viewport *viewport;
	Camera *camera;
	Transform projectionTransform;
	RasterSettings settings;
} RenderingContext;

RenderingContext MakeRenderingContext(Viewport *viewport, Camera *camera, Transform projectionTransform, RasterSettings settings);
void DrawObject(Object *object, LightSource **lights, RenderingContext context, Transform imageTransform);
void DrawPolygon(Polygon *polygon, Color color, RenderingContext context, Transform imageTransform);
void WireframePolygon(Polygon *polygon, Color color, Viewport *viewport);
void DrawPoint(Point *p, RenderingContext context, Transform imageTransform);
void DrawLine(Vertex a, Vertex b, Color color, Viewport *viewport, Transform imageTransform);
void DrawAxes(RenderingContext context);
bool BackfaceCulled(Polygon *polygon);


D src2/3d/vertex_pool.c => src2/3d/vertex_pool.c +0 -33
@@ 1,33 0,0 @@
#include "vertex_pool.h"
#include <stdlib.h>
#include <assert.h>

#define VPOOL_SIZE 1000000

VPool vpool_init()
{
	// number of used slots is in front of actual vertices
	int *raw_pool = (int *)malloc(sizeof(Vertex)*VPOOL_SIZE + sizeof(int));
	raw_pool[0] = 0;
	VPool pool = (VPool)(raw_pool + 1);
	return pool;
}

Vertex *vpool_new(VPool pool, double x, double y, double z, double w)
{
	assert(vpool_count(pool) < VPOOL_SIZE);
	Vertex *v = &(pool[vpool_count(pool)]);
	v->x = x;
	v->y = y;
	v->z = z;
	v->w = w;
	int *raw_pool = raw_pool(pool);
	raw_pool[0]++;
	return v;
}

void vpool_destroy(VPool pool)
{
	free(raw_pool(pool));
}


D src2/3d/vertex_pool.h => src2/3d/vertex_pool.h +0 -12
@@ 1,12 0,0 @@
#pragma once

#include "abstract/vertex.h"

typedef Vertex *VPool;

VPool vpool_init();
Vertex *vpool_new(VPool pool, double x, double y, double z, double w);
void vpool_destroy(VPool pool);

#define raw_pool(pool)		(((int *)(pool)) - 1)
#define vpool_count(pool)	(raw_pool(pool)[0])

D src2/3d/world.h => src2/3d/world.h +0 -6
@@ 1,6 0,0 @@
#pragma once

#include "world/light.h"
#include "world/mesh.h"
#include "world/object.h"


D src2/3d/world/light.c => src2/3d/world/light.c +0 -72
@@ 1,72 0,0 @@
#include "light.h"
#include "../../arraylist.h"
#include <stdlib.h>
#include <math.h>
#include <assert.h>

static double ambient = 0.2;

void AmbientLight(double amb)
{
	ambient = amb;
}

LightSource *PointLight(Vector position, double intensity)
{
	LightSource *l = malloc(sizeof(LightSource));
	l->position = position;
	l->intensity = intensity;
	l->type = POINT_LIGHT;
	return l;
}

SpotLightSource *SpotLight(Vector position, Vector direction, double spread, double intensity)
{
	SpotLightSource *l = malloc(sizeof(SpotLightSource));
	l->light.position = position;
	l->light.intensity = intensity;
	l->light.type = SPOT_LIGHT;
	l->direction = direction;
	l->spread = spread;
	return l;
}

double LightPolygon(Polygon *polygon, Color color, double glow, LightSource **lights)
{
	return fmin(glow + color * fmin((ambient + DiffuseLight(polygon, lights)), 1), 1);
}

double DiffuseLight(Polygon *polygon, LightSource **lights)
{
	double lightAmt = 0;
	Vector n = PolygonNormal(polygon);
	for (int i = 0; i < list_count(lights); i++) {
		Vector v = PolygonCenter(polygon);
		Vector l = VecSub(lights[i]->position, v);
		Vector nl = NormalizeVector(l);
		double dist = VecLen(l);
		double attenuation = fmin(1/(0.9+0.1*dist+0.001*dist*dist), 1);
		double incidence = DotProd(n, nl);

		if (IsLit(lights[i], incidence, nl)) {
			lightAmt += attenuation*incidence*lights[i]->intensity;
		}
	}
	return lightAmt;
}

bool IsLit(LightSource *light, double incidence, Vector l)
{
	if (incidence < 0) {
		return false;
	}

	if (light->type == SPOT_LIGHT) {
		SpotLightSource *spotlight = (SpotLightSource *)light;
		if (DotProd(spotlight->direction, NegVec(l)) < 1 - spotlight->spread) {
			return false;
		}
	}

	return true;
}

D src2/3d/world/light.h => src2/3d/world/light.h +0 -27
@@ 1,27 0,0 @@
#pragma once

#include <stdint.h>
#include "../abstract.h"

typedef enum LightType {
	POINT_LIGHT, SPOT_LIGHT
} LightType;

typedef struct LightSource {
	LightType type;
	Vertex position;
	double intensity;
} LightSource;

typedef struct SpotLightSource {
	LightSource light;
	Vector direction;
	double spread;
} SpotLightSource;

void AmbientLight(double amb);
LightSource *PointLight(Vector position, double intensity);
SpotLightSource *SpotLight(Vector position, Vector direction, double spread, double intensity);
double LightPolygon(Polygon *polygon, Color color, double glow, LightSource **lights);
double DiffuseLight(Polygon *polygon, LightSource **lights);
bool IsLit(LightSource *light, double incidence, Vector l);

D src2/3d/world/mesh.c => src2/3d/world/mesh.c +0 -158
@@ 1,158 0,0 @@
#include "mesh.h"
#include "../../arraylist.h"
#include <stdio.h>
#include <string.h>

Mesh *NewMesh(void)
{
	Mesh *obj = malloc(sizeof(Mesh));
	obj->vertices = NULL;
	obj->facets = NULL;
	return obj;
}

void AddFacet(Mesh *mesh, Polygon *polygon)
{
	int i;
	vIndex vi;
	Vertex *vert;

	/* replace facet's vList with indices into mesh's vertices;
	   also make sure mesh has the same verts */
	for (i = 0; i < NumVertices(polygon); i++) {
		vert = PolygonVertex(polygon, i);

		vi = FindVertex(mesh->vertices, vert);
		if (vi == VERTEX_NOT_FOUND) {
			vi = list_count(mesh->vertices);
			/* this could change the vertices pointer, which won't automatically
			 * be reflected in each of the mesh's facets */
			list_push(mesh->vertices, polygon->vertices[i]);
		}
		polygon->vList[i] = vi;
	}

	/* replace facet's vertex list with mesh's */
	list_free(polygon->vertices);
	polygon->vertices = mesh->vertices;

	/* also ensure facets have correct ref to vertices, since the list_push
	 * may have changed the pointer */
	for (i = 0; i < list_count(mesh->facets); i++) {
		mesh->facets[i]->vertices = mesh->vertices;
	}

	list_push(mesh->facets, polygon);
}

int NumFacets(Mesh *mesh)
{
	return list_count(mesh->facets);
}

Mesh *TransformMesh(Mesh *obj, Transform t)
{
	for (int i = 0; i < list_count(obj->vertices); i++) {
		CommitTransform(t, obj->vertices[i]);
	}

	return obj;
}

Mesh *LoadMesh(const char *filename)
{
	FILE *fp;
	Mesh *m = NewMesh();
	bool fileOk = true;
	int line = 0;

	fp = fopen(filename, "r");
	while (!feof(fp) && fileOk) {
		line++;
		fileOk = ReadObjLine(fp, m);
	}
	fclose(fp);

	if (!fileOk) {
		printf("Error reading \"%s\" on line %d\n", filename, line+1);
		return NULL;
	}

	return m;
}

bool ReadObjLine(FILE *fp, Mesh *mesh)
{
	char itemType[8];
	if (fscanf(fp, "%8s", itemType) == 1) {
		switch (ParseLineType(itemType)) {
			case OBJ_VERTEX: 	return ReadObjVertex(fp, mesh);
			case OBJ_FACE: 		return ReadObjFace(fp, mesh);
			case OBJ_EMPTY: 	return true;
			case OBJ_COMMENT:
				fscanf(fp, "%*[^\n]\n"); // skip the rest of the line
				return true;
			case OBJ_UNKNOWN:
				printf("Unknown \"%s\"\n", itemType);
				return false;
			default:
				fscanf(fp, "%*[^\n]\n"); // skip the rest of the line
				return true;
		}
	}
	// no match, probably EOF
	return true;
}

bool ReadObjVertex(FILE *fp, Mesh *mesh)
{
	float x, y, z, w;
	bool match;

	match = fscanf(fp, "%f", &x);
	if (!match) return false;
	match = fscanf(fp, "%f", &y);
	if (!match) return false;
	match = fscanf(fp, "%f", &z);
	if (!match) return false;
	match = fscanf(fp, "%f", &w);
	if (!match) w = 1.0;

	Vertex *v = NewVector4D(x, y, z, w);
	list_push(mesh->vertices, v);
	return true;
}

bool ReadObjFace(FILE *fp, Mesh *mesh)
{
	int vIndex;
	Polygon *p = NewPolygon();
	p->vertices = mesh->vertices;

	while (fscanf(fp, "%d", &vIndex) == 1) {
		list_push(p->vList, vIndex-1);
		fscanf(fp, "/%*d"); // skip vt
		fscanf(fp, "/%*d"); // skip vn
	}

	list_push(mesh->facets, p);
	return true;
}

ObjLineType ParseLineType(const char *type)
{
	if (0 == strcmp(type, "v"))			return OBJ_VERTEX;
	if (0 == strcmp(type, "f"))			return OBJ_FACE;
	if (0 == strcmp(type, "vn"))		return OBJ_VNORMAL;
	if (0 == strcmp(type, "vt"))		return OBJ_VTEXTURE;
	if (0 == strcmp(type, "vp"))		return OBJ_VPARAM;
	if (0 == strcmp(type, "g"))			return OBJ_GROUP;
	if (0 == strcmp(type, "o"))			return OBJ_OBJECT;
	if (0 == strcmp(type, "s"))			return OBJ_SMOOTH;
	if (0 == strcmp(type, "mtllib"))	return OBJ_MTLLIB;
	if (0 == strcmp(type, "usemtl"))	return OBJ_USEMTL;
	if (0 == strcmp(type, "#"))			return OBJ_COMMENT;
	if (0 == strcmp(type, " "))			return OBJ_EMPTY;
	if (0 == strcmp(type, "\n"))		return OBJ_EMPTY;
	return OBJ_UNKNOWN;
}

D src2/3d/world/mesh.h => src2/3d/world/mesh.h +0 -35
@@ 1,35 0,0 @@
#pragma once

#include "../abstract.h"
#include <stdio.h>

typedef struct Mesh {
	Vertex **vertices;	// list of vertices in all of the mesh's facets
	Polygon **facets;
} Mesh;

typedef enum ObjLineType {
	OBJ_VERTEX,
	OBJ_FACE,
	OBJ_VNORMAL,
	OBJ_VTEXTURE,
	OBJ_VPARAM,
	OBJ_GROUP,
	OBJ_OBJECT,
	OBJ_SMOOTH,
	OBJ_MTLLIB,
	OBJ_USEMTL,
	OBJ_COMMENT,
	OBJ_EMPTY,
	OBJ_UNKNOWN
} ObjLineType;

Mesh *NewMesh(void);
void AddFacet(Mesh *object, Polygon *facet);
int NumFacets(Mesh *mesh);
Mesh *TransformMesh(Mesh *obj, Transform t);
Mesh *LoadMesh(const char *filename);
bool ReadObjLine(FILE *fp, Mesh *mesh);
bool ReadObjVertex(FILE *fp, Mesh *mesh);
bool ReadObjFace(FILE *fp, Mesh *mesh);
ObjLineType ParseLineType(const char *type);

D src2/3d/world/object.c => src2/3d/world/object.c +0 -53
@@ 1,53 0,0 @@
#include "object.h"
#include <stdlib.h>

Object *NewCube(Color color)
{
	Mesh *geometry = LoadMesh("assets/cube.obj");
	return NewObject(geometry, color);
}

Object *NewGround(void)
{
	Mesh *geom = NewMesh();
	Polygon *ground = NewPolygon();
	AddVertex(ground, NewVector(1000, -1, 1000));
	AddVertex(ground, NewVector(1000, -1, -1000));
	AddVertex(ground, NewVector(-1000, -1, -1000));
	AddVertex(ground, NewVector(-1000, -1, 1000));
	AddFacet(geom, ground);
	return NewObject(geom, GRAY);
}

Object *NewObject(Mesh *geom, Color color)
{
	Object *o = malloc(sizeof(Object));
	o->geometry = geom;
	o->transform = Identity();
	o->position = V(0, 0, 0);
	o->color = color;
	o->glow = 0;
	return o;
}

void RotateObj(Object *obj, double rx, double ry, double rz)
{
	RotateX(&obj->transform, rx);
	RotateY(&obj->transform, ry);
	RotateZ(&obj->transform, rz);
}

void ScaleObj(Object *obj, double sx, double sy, double sz)
{
	Scale(&obj->transform, sx, sy, sz);
}

void TranslateObj(Object *obj, Vector dv)
{
	Translate(&obj->transform, dv);
}

void PlaceObj(Object *obj, Vector p)
{
	obj->position = p;
}

D src2/3d/world/object.h => src2/3d/world/object.h +0 -20
@@ 1,20 0,0 @@
#pragma once

#include "mesh.h"
#include "../abstract.h"

typedef struct Object {
	Mesh *geometry;
	Transform transform;
	Vector position;
	double color;
	double glow;
} Object;

Object *NewObject(Mesh *geom, Color color);
Object *NewCube(Color color);
Object *NewGround(void);
void RotateObj(Object *obj, double rx, double ry, double rz);
void ScaleObj(Object *obj, double sx, double sy, double sz);
void TranslateObj(Object *obj, Vector dv);
void PlaceObj(Object *obj, Vector p);

D src2/arraylist.h => src2/arraylist.h +0 -35
@@ 1,35 0,0 @@
#pragma once

#define list_free(a)         ((a) ? free(list__sbraw(a)),0 : 0)
#define list_push(a,v)        (list__sbmaybegrow(a,1), (a)[list__sbn(a)++] = (v))
#define list_count(a)        ((a) ? list__sbn(a) : 0)
#define list_last(a)         ((a)[list__sbn(a)-1])

#define list__sbraw(a) ((int *) (void *) (a) - 2)
#define list__sbm(a)   list__sbraw(a)[0]
#define list__sbn(a)   list__sbraw(a)[1]

#define list__sbneedgrow(a,n)  ((a)==0 || list__sbn(a)+(n) >= list__sbm(a))
#define list__sbmaybegrow(a,n) (list__sbneedgrow(a,(n)) ? list__sbgrow(a,n) : 0)
#define list__sbgrow(a,n)      (*((void **)&(a)) = list__sbgrowf((a), (n), sizeof(*(a))))

#include <stdlib.h>

static void *list__sbgrowf(void *arr, int increment, int itemsize)
{
	int dbl_cur = arr ? 2*list__sbm(arr) : 0;
	int min_needed = list_count(arr) + increment;
	int m = dbl_cur > min_needed ? dbl_cur : min_needed;
	int *p = (int *) realloc(arr ? list__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
	if (p) {
		if (!arr)
			p[1] = 0;
		p[0] = m;
		return p+2;
	} else {
		#ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
		STRETCHY_BUFFER_OUT_OF_MEMORY ;
		#endif
		return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
	}
}

D src2/input.c => src2/input.c +0 -16
@@ 1,16 0,0 @@
#include "input.h"

InputState KeyPressed(InputState input, KeySym key)
{
	return (input & key) == key;
}

InputState KeyDown(InputState input, KeySym key)
{
	return input | key;
}

InputState KeyUp(InputState input, KeySym key)
{
	return input & ~key;
}

D src2/input.h => src2/input.h +0 -16
@@ 1,16 0,0 @@
#pragma once

#define NO_INPUT	0b00000000
#define KEY_LEFT	0b00000001
#define KEY_RIGHT	0b00000010
#define KEY_FWD		0b00000100
#define KEY_BACK	0b00001000
#define KEY_JUMP	0b00010000

typedef char KeySym;
typedef char InputState;

InputState KeyPressed(InputState input, KeySym key);
InputState KeyDown(InputState input, KeySym key);
InputState KeyUp(InputState input, KeySym key);


D src2/main.c => src2/main.c +0 -111
@@ 1,111 0,0 @@
#include <SDL2/SDL.h>
#include <stdio.h>

#include "input.h"
#include "sketch.h"

unsigned int SCREEN_WIDTH = 400;
unsigned int SCREEN_HEIGHT = 240;
uint32_t *pixels;

#define TICK_INTERVAL    30
static Uint32 next_time;
uint32_t time_left(void)
{
    uint32_t now;

    now = SDL_GetTicks();
    if(next_time <= now)
        return 0;
    else
        return next_time - now;
}

InputState MapSDLInput(SDL_Keycode keyCode)
{
	switch (keyCode) {
		case SDLK_LEFT:		return KEY_LEFT;
		case SDLK_RIGHT:	return KEY_RIGHT;
		case SDLK_UP:		return KEY_FWD;
		case SDLK_DOWN:		return KEY_BACK;
		case SDLK_SPACE:	return KEY_JUMP;
		default:			return NO_INPUT;
	}
}

#ifndef TEST
int main(void)
{
	if (SDL_Init(SDL_INIT_VIDEO) == 0) {
		SDL_Window* window = NULL;
		SDL_Renderer* renderer = NULL;
		SDL_Texture *texture = NULL;

		if (SDL_CreateWindowAndRenderer(SCREEN_WIDTH, SCREEN_HEIGHT, 0, &window, &renderer) == 0) {
		    texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);
		    pixels = malloc(sizeof(uint32_t)*SCREEN_WIDTH*SCREEN_HEIGHT);
			SDL_bool done = SDL_FALSE;
			SDL_Event event;

			sketch_setup(pixels);
			sketch_draw(SDL_GetTicks());

			SDL_UpdateTexture(texture, NULL, pixels, SCREEN_WIDTH*sizeof(uint32_t));
			SDL_RenderCopy(renderer, texture, NULL, NULL);
			SDL_RenderPresent(renderer);

		    next_time = SDL_GetTicks() + TICK_INTERVAL;

			long frames = 0;
			long start = SDL_GetTicks();
			InputState input = NO_INPUT;
			while (!done) {

        		if (time_left() == 0) {
			        next_time += TICK_INTERVAL;
					frames++;
					sketch_draw(SDL_GetTicks());
					SDL_UpdateTexture(texture, NULL, pixels, SCREEN_WIDTH*sizeof(uint32_t));
					SDL_RenderCopy(renderer, texture, NULL, NULL);
					SDL_RenderPresent(renderer);

				// close after 1 min
				// if (SDL_GetTicks() > 1000*60) {
				// 	done = SDL_TRUE;
				// }

					while (SDL_PollEvent(&event)) {
						if (event.type == SDL_QUIT) {
							done = SDL_TRUE;
						} else if (event.type == SDL_KEYDOWN) {
							input = KeyDown(input, MapSDLInput(event.key.keysym.sym));

							// sketch_draw(SDL_GetTicks());
							// SDL_UpdateTexture(texture, NULL, pixels, SCREEN_WIDTH*sizeof(uint32_t));
							// SDL_RenderCopy(renderer, texture, NULL, NULL);
							// SDL_RenderPresent(renderer);
						} else if (event.type == SDL_KEYUP) {
							input = KeyUp(input, MapSDLInput(event.key.keysym.sym));
						}
					}

					sketch_input(input);
				}
			}
			long time = (SDL_GetTicks() - start)/1000;
			double fps = (double)frames/(double)time;
			printf("%f fps\n", fps);
		}

		if (renderer) {
			SDL_DestroyRenderer(renderer);
		}
		if (window) {
			SDL_DestroyWindow(window);
		}
	}
	SDL_Quit();
	return 0;
}

#endif

D src2/player.c => src2/player.c +0 -42
@@ 1,42 0,0 @@
#include "player.h"
#include <stdlib.h>

Player *NewPlayer(Vertex position)
{
	Player *player = malloc(sizeof(Player));

	player->camera = NewCamera(position);
	LookAt(V(0, position.y, 0), player->camera);

	return player;
}

void PlayerTurnLeft(Player *player, double angle)
{
	RotateCamera(0, angle, 0, player->camera);
}

void PlayerTurnRight(Player *player, double angle)
{
	RotateCamera(0, -angle, 0, player->camera);
}

void PlayerMoveForward(Player *player, double amount)
{
	MoveCamera(VecMul(player->camera->direction, amount), player->camera);
}

void PlayerMoveBackward(Player *player, double amount)
{
	MoveCamera(VecMul(player->camera->direction, -amount), player->camera);
}

Vector PlayerDirection(Player *player)
{
	return player->camera->direction;
}

Vertex PlayerPosition(Player *player)
{
	return player->camera->position;
}

D src2/player.h => src2/player.h +0 -15
@@ 1,15 0,0 @@
#pragma once

#include "3d.h"

typedef struct Player {
	Camera *camera;
} Player;

Player *NewPlayer(Vertex position);
void PlayerTurnLeft(Player *player, double angle);
void PlayerTurnRight(Player *player, double angle);
void PlayerMoveForward(Player *player, double amount);
void PlayerMoveBackward(Player *player, double amount);
Vector PlayerDirection(Player *player);
Vertex PlayerPosition(Player *player);

D src2/scene.c => src2/scene.c +0 -39
@@ 1,39 0,0 @@
#include "scene.h"
#include "arraylist.h"

Scene *NewScene(void)
{
	Scene *s = malloc(sizeof(Scene));
	s->objects = NULL;
	s->lights = NULL;
	s->points = NULL;
	return s;
}

void AddObject(Scene *scene, Object *obj)
{
	list_push(scene->objects, obj);
}

void AddLight(Scene *scene, LightSource *light)
{
	list_push(scene->lights, light);
}

void AddPoint(Scene *scene, Point *point)
{
	list_push(scene->points, point);
}

void RenderScene(Scene *scene)
{
	Transform imageTransform = ComposeTransform(scene->context.projectionTransform, CameraTransform(scene->context.camera));

	for (int i = 0; i < list_count(scene->objects); i++) {
		DrawObject(scene->objects[i], scene->lights, scene->context, imageTransform);
	}

	for (int i = 0; i < list_count(scene->points); i++) {
		DrawPoint(scene->points[i], scene->context, imageTransform);
	}
}

D src2/scene.h => src2/scene.h +0 -22
@@ 1,22 0,0 @@
#pragma once

#include "3d.h"
#include "player.h"
#include "input.h"

typedef void (*SceneUpdate)(struct Scene *scene, int dt);

typedef struct Scene {
	Object **objects;
	Point **points;
	LightSource **lights;
	Player *player;
	RenderingContext context;
	SceneUpdate onUpdate;
} Scene;

Scene *NewScene(void);
void AddObject(Scene *scene, Object *obj);
void AddLight(Scene *scene, LightSource *light);
void AddPoint(Scene *scene, Point *point);
void RenderScene(Scene *scene);

D src2/scenes/cubeworld.c => src2/scenes/cubeworld.c +0 -61
@@ 1,61 0,0 @@
#include "cubeworld.h"

#include "../player.h"
#include <stdlib.h>
#include <math.h>

static Object *masterCube;

Scene *CubeWorld(void)
{
	Scene *scene = NewScene();
	scene->onUpdate = &UpdateCubeWorld;

	AmbientLight(0.1);
	LightSource *cubeLight = PointLight(V(0, 2, 0), 1);
	AddLight(scene, cubeLight);

	AddObject(scene, NewGround());

	for (int i = 0; i < 500; i++) {
		Object *cube = NewCube(WHITE);
		RotateObj(cube, 0, rand() % 360, 0);
		int azimuth = rand() % 360;
		int dist = rand() % 100+10;
		double x = dist*cos(Rad(azimuth));
		double z = dist*sin(Rad(azimuth));
		PlaceObj(cube, V(x, 0, z));
		AddObject(scene, cube);
	}

	for (int i = 0; i < 500; i++) {
		int azimuth = rand() % 360;
		int altitude = rand() % 90;
		int dist = 1000;
		double x = dist*cos(Rad(azimuth));
		double z = dist*sin(Rad(azimuth));
		double y = dist*sin(Rad(altitude));
		Point *p = NewPoint(V(x, y, z), WHITE);
		AddPoint(scene, p);
	}

	masterCube = NewCube(DK_GRAY);
	masterCube->glow = 1;
	PlaceObj(masterCube, V(0, 2, 0));
	AddObject(scene, masterCube);

	Object *ob = NewObject(LoadMesh("assets/obelisk.obj"), WHITE);
	TranslateObj(ob, V(100, -1, 0));
	RotateObj(ob, 0, rand() % 360, 0);
	AddObject(scene, ob);

	scene->player = NewPlayer(V(0, 0, -100));

	return scene;
}

void UpdateCubeWorld(Scene *scene, int dt)
{
	RotateObj(masterCube, 0, 0.01*dt, 0);
	RotateObj(masterCube, 0.01*dt, 0, 0);
}

D src2/scenes/cubeworld.h => src2/scenes/cubeworld.h +0 -7
@@ 1,7 0,0 @@
#pragma once

#include "../3d.h"
#include "../scene.h"

Scene *CubeWorld(void);
void UpdateCubeWorld(Scene *scene, int dt);

D src2/scenes/moonworld.c => src2/scenes/moonworld.c +0 -65
@@ 1,65 0,0 @@
#include "moonworld.h"

#include "../player.h"
#include <stdlib.h>
#include <math.h>

// static Object *masterCube;

Scene *MoonWorld(void)
{
	Scene *scene = NewScene();
	scene->onUpdate = &UpdateMoonWorld;

	AmbientLight(0.1);
	LightSource *cubeLight = PointLight(V(0, 2, 0), 1);
	AddLight(scene, cubeLight);

	// AddObject(scene, NewGround());
  Object *moon = NewObject(LoadMesh("assets/moon.obj"), WHITE);
  ScaleObj(moon, 10, 10, 10);
  AddObject(scene, moon);

	// for (int i = 0; i < 500; i++) {
	// 	Object *cube = NewCube(WHITE);
	// 	RotateObj(cube, 0, rand() % 360, 0);
	// 	int azimuth = rand() % 360;
	// 	int dist = rand() % 100+10;
	// 	double x = dist*cos(Rad(azimuth));
	// 	double z = dist*sin(Rad(azimuth));
	// 	PlaceObj(cube, V(x, 0, z));
	// 	AddObject(scene, cube);
	// }

	for (int i = 0; i < 500; i++) {
		int azimuth = rand() % 360;
		int altitude = rand() % 90;
		int dist = 1000;
		double x = dist*cos(Rad(azimuth));
		double z = dist*sin(Rad(azimuth));
		double y = dist*sin(Rad(altitude));
		Point *p = NewPoint(V(x, y, z), WHITE);
		AddPoint(scene, p);
	}

	// masterCube = NewCube(DK_GRAY);
	// masterCube->glow = 1;
	// PlaceObj(masterCube, V(0, 2, 0));
	// AddObject(scene, masterCube);

	// Object *ob = NewObject(LoadMesh("assets/obelisk.obj"), WHITE);
	// TranslateObj(ob, V(100, -1, 0));
	// RotateObj(ob, 0, rand() % 360, 0);
	// AddObject(scene, ob);

	scene->player = NewPlayer(V(0, 10, -1));

	return scene;
}

void UpdateMoonWorld(Scene *scene, int dt)
{
	StandOn(scene->camera)
	// RotateObj(masterCube, 0, 0.01*dt, 0);
	// RotateObj(masterCube, 0.01*dt, 0, 0);
}

D src2/scenes/moonworld.h => src2/scenes/moonworld.h +0 -7
@@ 1,7 0,0 @@
#pragma once

#include "../3d.h"
#include "../scene.h"

Scene *MoonWorld(void);
void UpdateMoonWorld(Scene *scene, int dt);

D src2/sketch.c => src2/sketch.c +0 -68
@@ 1,68 0,0 @@
#include "sketch.h"
#include "arraylist.h"
#include "scenes/moonworld.h"
#include "scenes/cubeworld.h"
#include "player.h"
#include <assert.h>

int debounce = 0;
bool dither = false;
Scene *scene;
static SpotLightSource *flashlight;
int now, lastTime;
RenderingContext context;

void sketch_setup(Pixel *pixels)
{
	Viewport *viewport = NewViewport(pixels, BLACK, 400, 240);
	RasterSettings settings = DefaultRasterSettings();
	settings.showWireframes = true;
	scene = MoonWorld();
	Camera *camera = scene->player->camera;
	Projection *projection = NewPerspectiveProjection(60, 400/240.f, 1, 100);
	context = MakeRenderingContext(viewport, camera, PerspectiveTransform(projection), settings);

	flashlight = SpotLight(camera->position, camera->direction, 0.1, 0);
	AddLight(scene, (LightSource *)flashlight);
}

void sketch_draw(long ticks)
{
	int dt = ticks - lastTime;
	lastTime = ticks;
	ClearViewport(context.viewport);

	(*(scene->onUpdate))(scene, dt);
	RenderScene(scene, context);

	if (dither) {
		Dither(context.viewport);
	}
}

void sketch_input(InputState input)
{
	if (KeyPressed(input, KEY_LEFT)) {
		PlayerTurnLeft(scene->player, 5);
		flashlight->direction = PlayerDirection(scene->player);
	}
	if (KeyPressed(input, KEY_RIGHT)) {
		PlayerTurnRight(scene->player, 5);
		flashlight->direction = PlayerDirection(scene->player);
	}
	if (KeyPressed(input, KEY_FWD)) {
		PlayerMoveForward(scene->player, 0.5);
		((LightSource *)flashlight)->position = PlayerPosition(scene->player);
	}
	if (KeyPressed(input, KEY_BACK)) {
		PlayerMoveBackward(scene->player, 0.5);
		((LightSource *)flashlight)->position = PlayerPosition(scene->player);
	}
	if (debounce == 0 && KeyPressed(input, KEY_JUMP)) {
		debounce = 10;
		// dither = !dither;
		((LightSource *)flashlight)->intensity = (((LightSource *)flashlight)->intensity) ? 0 : 0.5;
	}

	if (debounce > 0) debounce--;
}

D src2/sketch.h => src2/sketch.h +0 -8
@@ 1,8 0,0 @@
#pragma once

#include "input.h"
#include "3d.h"

void sketch_setup(Pixel *pixels);
void sketch_draw(long ticks);
void sketch_input(InputState input);

D test2/assertions.h => test2/assertions.h +0 -27
@@ 1,27 0,0 @@
#pragma once

#include <stdbool.h>

extern bool gTestPassed;

void __AssertEqual(const char *a, const char *b, double aVal, double bVal,
	const char *func, const char *file, int line);
#define AssertEqual(A, B)	(void)(gTestPassed && (((A) == (B)) || (__AssertEqual(#A, #B, (double)A, (double)B, __func__, __FILE__, __LINE__),0)))

void __AssertPixel(const char *x, const char *y, const char *c,
	int xVal, int yVal,
	const char *func, const char *file, int line);
#define AssertPixel(X, Y, C, V)	(void)(gTestPassed && (((GetPixel(X, Y, V)) == (C)) || (__AssertPixel(#X, #Y, #C, X, Y, __func__, __FILE__, __LINE__),0)))

void __AssertTrue(const char *ex,
	const char *func, const char *file, int line);
#define AssertTrue(A)		(void)(gTestPassed && (((A)) || (__AssertTrue(#A, __func__, __FILE__, __LINE__),0)))

void __AssertFalse(const char *ex,
	const char *func, const char *file, int line);
#define AssertFalse(A)		(void)(gTestPassed && ((!(A)) || (__AssertFalse(#A, __func__, __FILE__, __LINE__),0)))

void __AssertWithin(const char *ex, const char *min, const char *max,
	double exVal, double minVal, double maxVal,
	const char *func, const char *file, int line);
#define AssertWithin(EX, MIN, MAX)	(void)(gTestPassed && (((EX) >= (MIN) && (EX) <= (MAX)) || (__AssertWithin(#EX, #MIN, #MAX, EX, MIN, MAX, __func__, __FILE__, __LINE__),0)))

D test2/assertions.test.c => test2/assertions.test.c +0 -48
@@ 1,48 0,0 @@
#include "assertions.h"
#include <stdio.h>

extern bool gTestPassed;

void __AssertEqual(const char *a, const char *b, double aVal, double bVal,
	const char *func, const char *file, int line)
{
	gTestPassed = false;
	printf("*** In %s (%s:%d):\n", func, file, line);
	printf("        AssertEqual(%s, %s)\n", a, b);
	printf("    Expected %.2f to equal %.2f.\n\n", aVal, bVal);
}

void __AssertPixel(const char *x, const char *y, const char *c,
	int xVal, int yVal,
	const char *func, const char *file, int line)
{
	gTestPassed = false;
	printf("*** In %s (%s:%d):\n", func, file, line);
	printf("        AssertPixel(%s, %s, %s)\n", x, y, c);
	printf("    Expected pixel at (%d, %d) to be %s.\n\n", xVal, yVal, c);
}

void __AssertTrue(const char *ex, const char *func, const char *file, int line)
{
	gTestPassed = false;
	printf("*** In %s (%s:%d):\n", func, file, line);
	printf("    Expected %s to be true.\n\n", ex);
}

void __AssertFalse(const char *ex, const char *func, const char *file, int line)
{
	gTestPassed = false;
	printf("*** In %s (%s:%d):\n", func, file, line);
	printf("    Expected %s to be false.\n\n", ex);
}

void __AssertWithin(
	const char *ex, const char *min, const char *max,
	double exVal, double minVal, double maxVal,
	const char *func, const char *file, int line)
{
	gTestPassed = false;
	printf("*** In %s (%s:%d):\n", func, file, line);
	printf("        AssertWithin(%s, %s, %s)\n", ex, min, max);
	printf("    Expected %.2f to be within %.2f and %.2f.\n\n", exVal, minVal, maxVal);
}
\ No newline at end of file

D test2/clip.test.c => test2/clip.test.c +0 -193
@@ 1,193 0,0 @@
#include "tests.h"

void ClippingTests(void)
{
	DoTest(&TestClipLine);
	DoTest(&TestVertexInside);
	DoTest(&TestClipIntersect);
	DoTest(&TestClipPolygonPlane);
	DoTest(&TestClipPolygon);
}

void TestClipLine(void)
{
	bool visible;
	Vector a;
	Vector b;

	a = V4D(  0.18,   0.00,  -0.97, 1);
	b = V4D(  0.18,   5.55,  -0.97, 1);
	visible = ClipLine(&a, &b);
	AssertTrue(visible);
	AssertWithin(a.x, -1, 1);
	AssertWithin(a.y, -1, 1);
	AssertWithin(a.z, -1, 1);
	AssertWithin(b.x, -1, 1);
	AssertWithin(b.y, -1, 1);
	AssertWithin(b.z, -1, 1);

	a = V4D(  3.02,   0.00,  -0.45, 1);
	b = V4D(  3.02,  95.18,  -0.45, 1);
	visible = ClipLine(&a, &b);
	AssertFalse(visible);

	a = V4D(  0.00,   0.00,  -0.99, 1);
	b = V4D(  0.00,  -1.43,  -0.97, 1);
	visible = ClipLine(&a, &b);
	AssertTrue(visible);
	AssertWithin(a.x, -1, 1);
	AssertWithin(a.y, -1, 1);
	AssertWithin(a.z, -1, 1);
	AssertWithin(b.x, -1, 1);
	AssertWithin(b.y, -1, 1);
	AssertWithin(b.z, -1, 1);

	a = V4D(10.929176, 0.000000, 0.569507, 0.431063);
	b = V4D(11.047228, 0.000000, 1.568198, -0.566630);
	visible = ClipLine(&a, &b);
	AssertFalse(visible);

	a = V4D(  0.00,  -3.46,  -5.01, 6);
	b = V4D(  0.00,  -3.46,   996, -994);
	visible = ClipLine(&a, &b);
	AssertTrue(visible);
	AssertWithin(a.x, -a.w, a.w);
	AssertWithin(a.y, -a.w, a.w);
	AssertWithin(a.z, -a.w, a.w);
	AssertWithin(b.x, -b.w, b.w);
	AssertWithin(b.y, -b.w, b.w);
	AssertWithin(b.z, -b.w, b.w);
}

void TestVertexInside(void)
{
	Vertex p = V4D(1, 0, 0, 2);
	Vertex q = V4D(2.5, 0, 0, 2);
	Vertex r = V4D(-2, 0, 0, -1);
	Vertex s = V4D(0, 0, 0, -2);
	Plane x_pos = Pl(V4D(0, 0, 0, 0), V4D(1, 0, 0, -1));
	Plane x_neg = Pl(V4D(0, 0, 0, 0), V4D(-1, 0, 0, -1));
	AssertTrue(VertexInside(&p, x_pos));
	AssertTrue(VertexInside(&p, x_neg));
	AssertFalse(VertexInside(&q, x_pos));
	AssertTrue(VertexInside(&q, x_neg));
	AssertTrue(VertexInside(&r, x_pos));
	AssertFalse(VertexInside(&r, x_neg));
	AssertFalse(VertexInside(&s, x_pos));
	AssertFalse(VertexInside(&s, x_neg));
}

void TestClipIntersect(void)
{
	Vertex s = V4D(4, 0, 0, 2);
	Vertex p = V4D(1, 0, 0, 2);
	Plane x_pos = Pl(V4D(0, 0, 0, 0), V4D(1, 0, 0, -1));
	Vertex i = ClipIntersect(&s, &p, x_pos);
	AssertTrue(VectorEqual(i, V4D(2, 0, 0, 2)));

	s = V(-1.3, 0, 0);
	p = V( 0.1, 0, 0);
	Plane x_neg = Pl(V4D(0, 0, 0, 0), V4D(-1,  0,  0, -1));
	i = ClipIntersect(&s, &p, x_neg);
	AssertTrue(VectorEqual(i, V4D(-1, 0, 0, 1)));

	s = V4D(+8.9960098267e-02, +1.7320507765e+00, +2.6407814026e-01, +1.7344551086e+00);
	p = V4D(-3.2262287140e+00, +1.7320507765e+00, +8.4350967407e-01, +1.1561794281e+00);
	i = ClipIntersect(&s, &p, x_neg);
	Vertex v = Homogenize(i);
	AssertTrue(v.x >= -1);
}

void TestClipPolygonPlane(void)
{
	Plane x_neg = Pl(V4D(0, 0, 0, 0), V4D(-1, 0, 0, -1));
	Plane x_pos = Pl(V4D(0, 0, 0, 0), V4D(1, 0, 0, -1));
	Polygon *p = NewPolygon();
	AddVertex(p, NewVector4D(1.2247448713915885, 1.7320508075688776, 0.77071482817625591, 1.2247448713915903));
	AddVertex(p, NewVector4D(4.5708100863428234, 1.7320508075688776, 0.24261940281555505, 1.7423829615966344));
	AddVertex(p, NewVector4D(4.5708100863428234, -1.7320508075688776, 0.24261940281555505, 1.7423829615966344));
	AddVertex(p, NewVector4D(1.2247448713915885, -1.7320508075688776, 0.77071482817625591, 1.2247448713915903));
	ClipPolygonPlane(&p, x_neg);
	ClipPolygonPlane(&p, x_pos);
	for (int i = 0; i < NumVertices(p); i++) {
		Vertex *v = PolygonVertex(p, i);
		AssertTrue(v->x <= v->w);
		AssertTrue(v->x >= -v->w);
	}
}

void TestClipPolygon(void)
/* Clips polygons to be within -1 <= x, y <= 1; -1 <= z <= 1 */
{
	Polygon *p = NewPolygon();
	AddVertex(p, NewVector(0.1, 0.1, 0));
	AddVertex(p, NewVector(0.1, 0.9, 0));
	AddVertex(p, NewVector(-1.3, 0.5, 0));
	AssertEqual(NumVertices(p), 3);
	bool visible = ClipPolygon(&p);
	AssertTrue(visible);
	AssertEqual(NumVertices(p), 4);
	FreePolygon(p);

	p = NewPolygon();
	AddVertex(p, NewVector(0.2, 0.2, 0));
	AddVertex(p, NewVector(0.3, 0.2, 0));
	AddVertex(p, NewVector(0.2, 0.3, 0));
	AssertEqual(NumVertices(p), 3);
	visible = ClipPolygon(&p);
	AssertTrue(visible);
	AssertEqual(NumVertices(p), 3);
	FreePolygon(p);

	p = NewPolygon();
	AddVertex(p, NewVector( -4.76,   0.75,  -1.22));
	AddVertex(p, NewVector(-37.34,   3.38,  -1.98));
	AddVertex(p, NewVector(  3.86,  -0.42,  -1.88));
	AddVertex(p, NewVector(  3.39,  -0.74,  -1.79));
	visible = ClipPolygon(&p);
	AssertFalse(visible);
	FreePolygon(p);

	p = NewPolygon();
	AddVertex(p, NewVector4D(+8.9960098267e-02, +1.7320507765e+00, +2.6407814026e-01, +1.7344551086e+00));
	AddVertex(p, NewVector4D(-3.2262287140e+00, +1.7320507765e+00, +8.4350967407e-01, +1.1561794281e+00));
	AddVertex(p, NewVector4D(-3.2262287140e+00, -1.7320507765e+00, +8.4350967407e-01, +1.1561794281e+00));
	AddVertex(p, NewVector4D(+8.9960098267e-02, -1.7320507765e+00, +2.6407814026e-01, +1.7344551086e+00));
	visible = ClipPolygon(&p);
	AssertTrue(visible);
	for (int i = 0; i < NumVertices(p); i++) {
		Vertex v = Homogenize(*PolygonVertex(p, i));
		AssertEqual(v.w, 1);
		AssertWithin(v.x, -1, 1);
		AssertWithin(v.y, -1, 1);
		AssertWithin(v.z, -1, 1);
	}
	FreePolygon(p);

	p = NewPolygon();
	AddVertex(p, NewVector4D( +2.1238925109378464e+00, +1.7320508075688776e+00, +9.4117255305726744e-01, +1.0587099095862040e+00 ));
	AddVertex(p, NewVector4D( -1.2739654549598711e+00, +1.7320508075688776e+00, +5.5113702891196237e-01, +1.4479661419749759e+00 ));
	AddVertex(p, NewVector4D( -1.2739654549598711e+00, -1.7320508075688776e+00, +5.5113702891196237e-01, +1.4479661419749759e+00 ));
	AddVertex(p, NewVector4D( +2.1238925109378464e+00, -1.7320508075688776e+00, +9.4117255305726744e-01, +1.0587099095862040e+00 ));
	visible = ClipPolygon(&p);
	AssertTrue(visible);
	for (int i = 0; i < NumVertices(p); i++) {
		Vertex v = Homogenize(*PolygonVertex(p, i));
		AssertEqual(v.w, 1);
		AssertWithin(v.x, -1, 1);
		AssertWithin(v.y, -1, 1);
		AssertWithin(v.z, -1, 1);
	}
	FreePolygon(p);

	// weird test case
	p = NewPolygon();
	AddVertex(p, NewVector4D(1.2247448713915885, 1.7320508075688776, 0.77071482817625591, 1.2247448713915903));
	AddVertex(p, NewVector4D(4.5708100863428234, 1.7320508075688776, 0.24261940281555505, 1.7423829615966344));
	AddVertex(p, NewVector4D(4.5708100863428234, -1.7320508075688776, 0.24261940281555505, 1.7423829615966344));
	AddVertex(p, NewVector4D(1.2247448713915885, -1.7320508075688776, 0.77071482817625591, 1.2247448713915903));
	visible = ClipPolygon(&p);
	AssertFalse(visible);
	// AssertTrue(NumVertices(p) >= 3);
	FreePolygon(p);
}

D test2/color.test.c => test2/color.test.c +0 -23
@@ 1,23 0,0 @@
#include "tests.h"

void ColorTests(void)
{
	DoTest(&TestColorPixel);
	DoTest(&TestPixelColor);
}

void TestColorPixel(void)
{
	AssertEqual(ColorPixel(WHITE), 0xFFFFFFFF);
	AssertEqual(ColorPixel(BLACK), 0xFF000000);
	AssertEqual(ColorPixel(GRAY),  0xFF7F7F7F);
	AssertEqual(ColorPixel(DK_GRAY), 0xFF545454);
	AssertEqual(ColorPixel(LT_GRAY), 0xFFAAAAAA);
}

void TestPixelColor(void)
{
	AssertEqual(PixelColor(0xFFFFFFFF), WHITE);
	AssertEqual(PixelColor(0x00000000), BLACK);
	AssertWithin(PixelColor(0xFF7F7F7F), GRAY-0.01, GRAY+0.01);
}

D test2/endpoint.test.c => test2/endpoint.test.c +0 -106
@@ 1,106 0,0 @@
#include "tests.h"

void EndpointTests(void)
{
	DoTest(&TestMakeEdgeTable);
	DoTest(&TestInsertEndpoints);
	DoTest(&TestRemoveEndpoints);
	DoTest(&TestIncrementEndpoints);
}

void TestMakeEdgeTable(void)
{
	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(84.5299454, 124.948715, 0));
	AddVertex(p, NewVertex(78.7564544, 125.196152, 0));
	AddVertex(p, NewVertex(78.7564544, 114.803848, 0));
	AddVertex(p, NewVertex(84.5299454, 115.051285, 0));
	Endpoint **table = MakeEdgeTable(240, p);
	AssertTrue(table);
	FreeEdgeTable(table, 240);
	FreePolygon(p);
}

void TestInsertEndpoints(void)
{
	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(84.5299454, 124.948715, 0));
	AddVertex(p, NewVertex(78.7564544, 125.196152, 0));
	AddVertex(p, NewVertex(78.7564544, 114.803848, 0));
	AddVertex(p, NewVertex(84.5299454, 115.051285, 0));
	Endpoint **table = MakeEdgeTable(240, p);
	Endpoint *list = NULL;

	for (int i = 0; i < 114; i++) {
		InsertEndpoints(&list, table[i]);
		AssertEqual(ListLength(list), 0);
	}
	InsertEndpoints(&list, table[114]);
	AssertEqual(ListLength(list), 2);
	InsertEndpoints(&list, table[115]);
	AssertEqual(ListLength(list), 3);
	for (int i = 116; i < 124; i++) {
		InsertEndpoints(&list, table[i]);
		AssertEqual(ListLength(list), 3);
	}
	InsertEndpoints(&list, table[124]);
	AssertEqual(ListLength(list), 4);
	for (int i = 125; i < 240; i++) {
		InsertEndpoints(&list, table[i]);
		AssertEqual(ListLength(list), 4);
	}

	FreeEdgeTable(table, 240);
	FreePolygon(p);
}

void TestRemoveEndpoints(void)
{
	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(84.5299454, 124.948715, 0));
	AddVertex(p, NewVertex(78.7564544, 125.196152, 0));
	AddVertex(p, NewVertex(78.7564544, 114.803848, 0));
	AddVertex(p, NewVertex(84.5299454, 115.051285, 0));
	Endpoint **table = MakeEdgeTable(240, p);
	Endpoint *list = NULL;
	InsertEndpoints(&list, table[114]);
	InsertEndpoints(&list, table[115]);
	InsertEndpoints(&list, table[124]);

	AssertEqual(ListLength(list), 4);
	RemoveEndpoints(&list, 115);
	AssertEqual(ListLength(list), 3);
	RemoveEndpoints(&list, 124);
	AssertEqual(ListLength(list), 2);
	RemoveEndpoints(&list, 125);
	AssertEqual(ListLength(list), 0);

	FreeEdgeTable(table, 240);
	FreePolygon(p);
}

void TestIncrementEndpoints(void)
{
	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(84.5299454, 124.948715, 0));
	AddVertex(p, NewVertex(78.7564544, 125.196152, 0));
	AddVertex(p, NewVertex(78.7564544, 114.803848, 0));
	AddVertex(p, NewVertex(84.5299454, 115.051285, 0));
	Endpoint **table = MakeEdgeTable(240, p);
	Endpoint *list = NULL;
	InsertEndpoints(&list, table[114]);
	AssertEqual(ListLength(list), 2);
	AssertWithin(list->x, 78, 79);
	AssertEqual(list->xinc, 0);
	AssertWithin(list->next->x, 78, 79);
	AssertWithin(list->next->xinc, 23, 24);

	AssertWithin(list->next->ymax, 115, 116);

	IncrementEndpoints(&list, 114);
	AssertWithin(list->x, 78, 79);
	AssertWithin(list->next->x, 78, 85);

	FreeEdgeTable(table, 240);
	FreePolygon(p);
}

D test2/light.test.c => test2/light.test.c +0 -32
@@ 1,32 0,0 @@
#include "tests.h"
#include <math.h>

void LightTests(void)
{
	DoTest(&TestDiffuseLight);
}

void TestDiffuseLight(void)
{
	LightSource **lights = NULL;
	LightSource *zlight = PointLight(V(0, 0, INFINITY), 1);
	list_push(lights, zlight);

	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(1, 1, 0));
	AddVertex(p, NewVertex(-1, 1, 0));
	AddVertex(p, NewVertex(-1, -1, 0));
	AddVertex(p, NewVertex(1, -1, 0));

	double shade = DiffuseLight(p, lights);
	// AssertEqual(shade, 1.0);

	Polygon *q = FromPolygon(p, RotationY(90));
	shade = DiffuseLight(q, lights);
	// AssertEqual(shade, 0);

	FreePolygon(p);
	FreePolygon(q);
	free(zlight);
	list_free(lights);
}

D test2/main.test.c => test2/main.test.c +0 -35
@@ 1,35 0,0 @@
#include "tests.h"
#include "assertions.h"
#include <stdio.h>

static int gTestFailures = 0;
static int gTestSuccesses = 0;
bool gTestPassed = true;

void DoTest(TestFunc test)
{
	(*test)();
	if (gTestPassed) {
		gTestSuccesses++;
	} else {
		gTestFailures++;
	}
	gTestPassed = true;
}

int main(void)
{
	ColorTests();
	TransformTests();
	ProjectionTests();
	ClippingTests();
	RenderTests();
	EndpointTests();
	RasterizationTests();
	LightTests();
	ModelTests();
	PerformanceTests();

	printf("=================================\n");
	printf("%d passed, %d failed.\n", gTestSuccesses, gTestFailures);
}

D test2/model.test.c => test2/model.test.c +0 -12
@@ 1,12 0,0 @@
#include "tests.h"

void ModelTests(void)
{
	DoTest(&TestLoadMesh);
}

void TestLoadMesh(void)
{
	Mesh *m = LoadMesh("assets/teapot.obj");
	AssertTrue(m != NULL);
}
\ No newline at end of file

D test2/performance.test.c => test2/performance.test.c +0 -53
@@ 1,53 0,0 @@
#include "tests.h"
#include <time.h>
#include <stdio.h>
#include <math.h>
#include "../src/scene.h"

void PerformanceTests(void)
{
	DoTest(&TestRandomTriangles);
}

void TestRandomTriangles(void)
{
	Scene *scene = NewScene();
	LightSource *light = PointLight(V(0, 2, 0), 1);
	AddLight(scene, light);

	for (int i = 0; i < 10000; ++i) {
		Polygon *tri = NewPolygon();
		AddVertex(tri, NewVector(0, 0, 0));
		AddVertex(tri, NewVector(0, 10, 0));
		AddVertex(tri, NewVector(10, 0, 0));
		Mesh *mesh = NewMesh();
		AddFacet(mesh, tri);
		Object *obj = NewObject(mesh, GRAY);

		RotateObj(obj, 0, rand() % 360, 0);
		int azimuth = rand() % 360;
		int dist = rand() % 100+10;
		double x = dist*cos(Rad(azimuth));
		double z = dist*sin(Rad(azimuth));
		PlaceObj(obj, V(x, 0, z));
		AddObject(scene, obj);
	}

	Pixel *pixels = malloc(sizeof(Pixel)*400*240);
	Viewport *viewport = NewViewport(pixels, BLACK, 400, 240);
	RasterSettings settings = DefaultRasterSettings();
	Camera *camera = NewCamera(V(0, 0, 0));
	LookAt(V(0, 0, -1), camera);
	Projection *projection = NewPerspectiveProjection(60, 400/240.f, 1, 100);
	RenderingContext context = MakeRenderingContext(viewport, camera, PerspectiveTransform(projection), settings);

	float clocksPerMs = CLOCKS_PER_SEC/1000;
	float startTime = (float)clock()/(clocksPerMs);
	RenderScene(scene, context);
	float endTime = (float)clock()/(clocksPerMs);
	float renderTime = (endTime - startTime);

	float targetTime = (1/30.f)*1000; // one frame, ~33 ms
	printf("Render time: %.2fms (target: %.2fms)\n", renderTime, targetTime);
	// AssertWithin(renderTime, 0, targetTime);
}

D test2/project.test.c => test2/project.test.c +0 -110
@@ 1,110 0,0 @@
#include "tests.h"
#include <math.h>

void ProjectionTests(void)
{
	DoTest(&TestPerspectiveProjection);
	DoTest(&TestViewmapPolygon);
	DoTest(&TestCameraPosition);
}

void TestPerspectiveProjection(void)
{
	Projection *projection = NewPerspectiveProjection(90, 1, 1, 2);
	Camera *camera = NewCamera(V(0, 0, 0));
	Transform t = GetProjectionTransform(camera, projection);

	Transform ct = CameraTransform(camera);
	AssertTrue(IsIdentity(ct));

	Vertex p = V(0, 1, -1);
	Vertex q = Homogenize(ProjectPoint(p, t));
	AssertTrue(VertexEqual(q, V(0, 1, 1)));

	p = V(0, -1, -1);
	q = Homogenize(ProjectPoint(p, t));
	AssertTrue(VertexEqual(q, V(0, -1, 1)));

	p = V(0, 2, -2);
	q = Homogenize(ProjectPoint(p, t));
	AssertTrue(VertexEqual(q, V(0, 1, -1)));

	p = V(0, -2, -2);
	q = Homogenize(ProjectPoint(p, t));
	AssertTrue(VertexEqual(q, V(0, -1, -1)));
}

void TestViewmapPolygon(void)
{
	InitDeviceTransform(400, 240);
	Polygon *p = NewPolygon();
	AddVertex(p, NewVector(-0.921176, -1.000000, 1.000000));
	AddVertex(p, NewVector(1.000000, -1.000000, 0.559785));
	AddVertex(p, NewVector(1.000000, 1.000000, 0.559785));
	AddVertex(p, NewVector(-0.921176, 1.000000, 1.000000));

	ViewmapPolygon(p);
	for (int i = 0; i < NumVertices(p); i++) {
		Vertex *v = PolygonVertex(p, i);
		AssertTrue(v->x >= 0);
		AssertTrue(v->x < 400);
		AssertTrue(v->y >= 0);
		AssertTrue(v->y < 240);
	}
	FreePolygon(p);

	p = NewPolygon();
	AddVertex(p, NewVector(0.757628, -1.000000, 1.000000));
	AddVertex(p, NewVector(0.757628, 1.000000, 1.000000));
	AddVertex(p, NewVector(-1.000000, 1.000000, 0.574397));
	AddVertex(p, NewVector(-1.000000, -1.000000, 0.574397));

	ViewmapPolygon(p);
	for (int i = 0; i < NumVertices(p); i++) {
		Vertex *v = PolygonVertex(p, i);
		AssertTrue(v->x >= 0);
		AssertTrue(v->x < 400);
		AssertTrue(v->y >= 0);
		AssertTrue(v->y < 240);
	}
	FreePolygon(p);


	p = NewPolygon();
	AddVertex(p, NewVector(0.90350350893137943, 1.7320508075688776, 1.1582046473758254));
	AddVertex(p, NewVector(-2.0841785003064004, 1.7320508075688776, 0.14396012337173758));
	AddVertex(p, NewVector(-2.0841785003064004, -1.7320508075688776, 0.14396012337173758));
	AddVertex(p, NewVector(0.90350350893137943, -1.7320508075688776, 1.1582046473758254));
	ClipPolygon(&p);
	HomogenizePolygon(p);
	ViewmapPolygon(p);
	for (int i = 0; i < NumVertices(p); i++) {
		Vertex *v = PolygonVertex(p, i);
		AssertTrue(v->x >= 0);
		AssertTrue(v->x < 400);
		AssertTrue(v->y >= 0);
		AssertTrue(v->y < 240);
	}
	FreePolygon(p);
}

void TestCameraPosition(void)
{
	Camera *camera = NewCamera(V(10, 20, -3));
	Vector p = camera->position;
	AssertEqual(p.x, 10);
	AssertEqual(p.y, 20);
	AssertEqual(p.z, -3);

	MoveCamera(V(1, 1, 1), camera);
	p = camera->position;
	AssertEqual(p.x, 11);
	AssertEqual(p.y, 21);
	AssertEqual(p.z, -2);

	RotateCamera(0, 130, 0, camera);
	p = camera->position;
	AssertEqual(p.x, 11);
	AssertEqual(p.y, 21);
	AssertEqual(p.z, -2);
}

D test2/rasterize.test.c => test2/rasterize.test.c +0 -116
@@ 1,116 0,0 @@
#include "tests.h"
#include <stdlib.h>

void RasterizationTests(void)
{
	DoTest(&TestRasterLine);
	DoTest(&TestRasterPolygon);
	DoTest(&TestRasterTriangle);
}

void TestRasterLine(void)
{
	Pixel *pixels = malloc(sizeof(Pixel)*100*100);
	Viewport *viewport = NewViewport(pixels, WHITE, 100, 100);
	DisableDepthBuffer(viewport);

	ClearViewport(viewport);
	Vertex a = V(5, 8, 0);
	Vertex b = V(9, 11, 0);
	RasterLine(a, b, BLACK, viewport);
	AssertPixel(4, 7, WHITE, viewport);
	AssertPixel(4, 8, WHITE, viewport);
	AssertPixel(5, 8, BLACK, viewport);
	AssertPixel(6, 9, BLACK, viewport);
	AssertPixel(7, 9, BLACK, viewport);
	AssertPixel(7, 8, WHITE, viewport);
	AssertPixel(8, 9, WHITE, viewport);
	AssertPixel(8, 10, BLACK, viewport);
	AssertPixel(9, 11, BLACK, viewport);
	AssertPixel(10, 11, WHITE, viewport);
	AssertPixel(10, 12, WHITE, viewport);

	ClearViewport(viewport);

	RasterLine(b, a, BLACK, viewport);
	AssertPixel(4, 7, WHITE, viewport);
	AssertPixel(4, 8, WHITE, viewport);
	AssertPixel(5, 8, BLACK, viewport);
	AssertPixel(6, 9, BLACK, viewport);
	AssertPixel(7, 9, BLACK, viewport);
	AssertPixel(7, 8, WHITE, viewport);
	AssertPixel(8, 9, WHITE, viewport);
	AssertPixel(8, 10, BLACK, viewport);
	AssertPixel(9, 11, BLACK, viewport);
	AssertPixel(10, 11, WHITE, viewport);
	AssertPixel(10, 12, WHITE, viewport);

	ClearViewport(viewport);

	b = V(7, 13, 0);
	RasterLine(a, b, BLACK, viewport);
	AssertPixel(5, 7, WHITE, viewport);
	AssertPixel(5, 8, BLACK, viewport);
	AssertPixel(5, 9, BLACK, viewport);
	AssertPixel(6, 10, BLACK, viewport);
	AssertPixel(6, 11, BLACK, viewport);
	AssertPixel(7, 12, BLACK, viewport);
	AssertPixel(7, 13, BLACK, viewport);
	AssertPixel(7, 14, WHITE, viewport);

	ClearViewport(viewport);

	RasterLine(b, a, BLACK, viewport);
	AssertPixel(5, 7, WHITE, viewport);
	AssertPixel(5, 8, BLACK, viewport);
	AssertPixel(5, 9, BLACK, viewport);
	AssertPixel(6, 10, BLACK, viewport);
	AssertPixel(6, 11, BLACK, viewport);
	AssertPixel(7, 12, BLACK, viewport);
	AssertPixel(7, 13, BLACK, viewport);
	AssertPixel(7, 14, WHITE, viewport);

	free(pixels);
}

void TestRasterPolygon(void)
{
	Pixel *pixels = malloc(sizeof(Pixel)*400*240);
	Viewport *viewport = NewViewport(pixels, WHITE, 400, 240);
	RasterSettings settings = { false };
	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(84.5299454, 124.948715, 0));
	AddVertex(p, NewVertex(78.7564544, 125.196152, 0));
	AddVertex(p, NewVertex(78.7564544, 114.803848, 0));
	AddVertex(p, NewVertex(84.5299454, 115.051285, 0));
	RasterPolygon(p, BLACK, viewport, settings);

	// bounding box should stay white
	for (int x = 78; x < 85; x++) {
		AssertPixel(x, 113, WHITE, viewport);
		AssertPixel(x, 126, WHITE, viewport);
	}
	for (int y = 114; y < 126; y++) {
		AssertPixel(77, y, WHITE, viewport);
		AssertPixel(85, y, WHITE, viewport);
	}

	free(pixels);
	FreePolygon(p);
}

void TestRasterTriangle(void)
{
	Pixel *pixels = malloc(sizeof(Pixel)*400*240);
	Viewport *viewport = NewViewport(pixels, WHITE, 400, 240);

	Polygon *t = NewPolygon();
	AddVertex(t, NewVertex(1, 1, 0));
	AddVertex(t, NewVertex(2, 10, 0));
	AddVertex(t, NewVertex(7, 3, 0));

	RasterTriangle(t, BLACK);
	// TODO: assert pixels

	FreePolygon(t);
}

D test2/render.test.c => test2/render.test.c +0 -57
@@ 1,57 0,0 @@
#include "tests.h"

void RenderTests(void)
{
	// DoTest(&TestSetViewport);
	DoTest(&TestClearViewport);
	DoTest(&TestBackfaceCulled);
}

void TestSetViewport(void)
{
	Pixel *pixels = malloc(sizeof(Pixel)*100*100);
	Viewport *viewport = NewViewport(pixels, WHITE, 100, 100);
	for (int x = 0; x < 100; x++) {
		for (int y = 0; y < 100; y++) {
			AssertPixel(x, y, WHITE, viewport);
		}
	}
	free(pixels);
}

void TestClearViewport(void)
{
	Pixel *pixels = malloc(sizeof(Pixel)*100*100);
	Viewport *viewport = NewViewport(pixels, WHITE, 100, 100);
	WritePixel(0, 0, BLACK, viewport);
	WritePixel(50, 30, BLACK, viewport);
	WritePixel(99, 99, BLACK, viewport);
	ClearViewport(viewport);

	for (int x = 0; x < 100; x++) {
		for (int y = 0; y < 100; y++) {
			AssertPixel(x, y, WHITE, viewport);
		}
	}
	free(pixels);
}

void TestBackfaceCulled(void)
/* Culls polygons defined by clockwise vertices */
{
	/* CCW, not culled */
	Polygon *p = NewPolygon();
	AddVertex(p, NewVertex(0, 0, 0));
	AddVertex(p, NewVertex(1, 0, 0));
	AddVertex(p, NewVertex(0, 1, 0));
	AssertFalse(BackfaceCulled(p));
	FreePolygon(p);

	/* CW, culled */
	p = NewPolygon();
	AddVertex(p, NewVertex(0, 0, 0));
	AddVertex(p, NewVertex(0, 1, 0));
	AddVertex(p, NewVertex(1, 0, 0));
	AssertTrue(BackfaceCulled(p));
	FreePolygon(p);
}

D test2/tests.h => test2/tests.h +0 -78
@@ 1,78 0,0 @@
#pragma once

#include "assertions.h"
#include "../src/3d.h"
#include "../src/arraylist.h"

typedef void (*TestFunc)(void);
void DoTest(TestFunc test);

void ColorTests(void);
void TestColorPixel(void);
void TestPixelColor(void);

void VectorTests(void);
void TestNegVec(void);
void TestVecMul(void);
void TestVecAdd(void);
void TestVecSub(void);
void TestVecLen(void);
void TestDotProd(void);
void TestCrossProd(void);
void TestNormalizeVector(void);

void TransformTests(void);
void TestSetIdentity(void);
void TestFromTransform(void);
void TestAddTransform(void);
void TestApplyTransform(void);
void TestCommitTransform(void);
void TestRotationX(void);
void TestRotateX(void);
void TestRotationY(void);
void TestRotateY(void);
void TestRotationZ(void);
void TestRotateZ(void);
void TestTranslation(void);
void TestTranslate(void);
void TestScalation(void);
void TestScale(void);
void TestDeterminant(void);

void RenderTests(void);
void TestSetViewport(void);
void TestClearViewport(void);
void TestBackfaceCulled(void);
void TestDrawPolygon(void);

void ProjectionTests(void);
void TestPerspectiveProjection(void);
void TestViewmapPolygon(void);
void TestCameraPosition(void);

void ClippingTests(void);
void TestClipLine(void);
void TestVertexInside(void);
void TestClipIntersect(void);
void TestClipPolygonPlane(void);
void TestClipPolygon(void);

void EndpointTests(void);
void TestMakeEdgeTable(void);
void TestInsertEndpoints(void);
void TestRemoveEndpoints(void);
void TestIncrementEndpoints(void);

void RasterizationTests(void);
void TestRasterLine(void);
void TestRasterPolygon(void);
void TestRasterTriangle(void);

void LightTests(void);
void TestDiffuseLight(void);

void PerformanceTests(void);
void TestRandomTriangles(void);

void ModelTests(void);
void TestLoadMesh(void);

D test2/transform.test.c => test2/transform.test.c +0 -69
@@ 1,69 0,0 @@
#include <stdlib.h>
#include "tests.h"
#include <stdio.h>

void TransformTests(void)
{
	DoTest(&TestSetIdentity);
	DoTest(&TestFromTransform);
	DoTest(&TestAddTransform);
	DoTest(&TestApplyTransform);
}

void TestSetIdentity(void)
{
	Transform t = Identity();
	for(int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			if (i == j) AssertEqual(t.m[i][j], 1);
			else AssertEqual(t.m[i][j], 0);
		}
	}
}

void TestFromTransform(void)
{
	Transform t = Identity();
	t.m[2][3] = 3;
	t.m[0][3] = 9;
	t.m[2][1] = 7;
	Transform u = FromTransform(t);
	t.m[2][2] = 0;

	AssertEqual(u.m[2][3], 3);
	AssertEqual(u.m[0][3], 9);
	AssertEqual(u.m[2][1], 7);
	AssertEqual(u.m[2][2], 1);
}

void TestAddTransform(void)
{
	Transform t = Identity();
	t.m[0][3] = 1;
	Transform u = Identity();
	u.m[1][3] = 2;
	AddTransform(u, &t);
	AssertEqual(t.m[0][3], 1);
	AssertEqual(t.m[1][3], 2);
}

void TestApplyTransform(void)
{
	Transform t;
	Vertex v0 = V(  0.00,   0.00,   0.00);
	Vertex v1 = V(  1.00,   0.00,   0.00);
	t.m[0][0] =  0.34; t.m[0][1] = -0.54; t.m[0][2] = -1.61; t.m[0][3] =  10.74;
	t.m[1][0] =  0.00; t.m[1][1] =  1.64; t.m[1][2] = -0.55; t.m[1][3] =   0.00;
	t.m[2][0] =  0.98; t.m[2][1] =  0.06; t.m[2][2] =  0.19; t.m[2][3] =  -0.24;
	t.m[3][0] = -0.98; t.m[3][1] = -0.06; t.m[3][2] = -0.19; t.m[3][3] =   1.24;

	Vertex r0 = Homogenize(ApplyTransform(t, v0));
	Vertex r1 = Homogenize(ApplyTransform(t, v1));
	AssertTrue(r0.x > 1);
	AssertTrue(r1.x > r0.x);

	t.m[0][0] =  0.23; t.m[0][1] = -0.54; t.m[0][2] = -1.63; t.m[0][3] =  10.86;
	t.m[1][0] =  0.00; t.m[1][1] =  1.64; t.m[1][2] = -0.55; t.m[1][3] =   0.00;
	t.m[2][0] =  0.99; t.m[2][1] =  0.04; t.m[2][2] =  0.13; t.m[2][3] =   0.16;
	t.m[3][0] = -0.99; t.m[3][1] = -0.04; t.m[3][2] = -0.13; t.m[3][3] =   0.84;
}

D test2/vector.test.c => test2/vector.test.c +0 -92
@@ 1,92 0,0 @@
#include "tests.h"

void VectorTests(void)
{
	DoTest(&TestNegVec);
	DoTest(&TestVecMul);
	DoTest(&TestVecAdd);
	DoTest(&TestVecSub);
	DoTest(&TestVecLen);
	DoTest(&TestDotProd);
	DoTest(&TestCrossProd);
	DoTest(&TestNormalizeVector);
}

void TestNegVec(void)
{
	Vector a = { 1, 3, 2, 1 };
	Vector b = NegVec(a);
	AssertEqual(b.x, -1);
	AssertEqual(b.y, -3);
	AssertEqual(b.z, -2);
}

void TestVecMul(void)
{
	Vector a = { 1, 3, 2, 1 };
	Vector b = VecMul(a, 3);
	AssertEqual(b.x, 3);
	AssertEqual(b.y, 9);
	AssertEqual(b.z, 6);
	b = VecMul(a, -1.5);
	AssertEqual(b.x, -1.5);
	AssertEqual(b.y, -4.5);
	AssertEqual(b.z, -3);
}

void TestVecAdd(void)
{
	Vector a = { 1, 3, 2, 1 };
	Vector b = { -3, 13, 1, 1 };
	Vector c = VecAdd(a, b);
	AssertEqual(c.x, -2);
	AssertEqual(c.y, 16);
	AssertEqual(c.z, 3);
}

void TestVecSub(void)
{
	Vector a = { -23, 14, 1, 1 };
	Vector b = { 52, 3, 14, 1 };
	Vector c = VecSub(a, b);
	AssertEqual(c.x, -75);
	AssertEqual(c.y, 11);
	AssertEqual(c.z, -13);
	Vector d = VecSub(b, a);
	AssertEqual(d.x, 75);
	AssertEqual(d.y, -11);
	AssertEqual(d.z, 13);
}

void TestVecLen(void)
{
	Vector a = { 4, 0, 0, 1 };
	double len = VecLen(a);
	AssertEqual(len, 4);
}

void TestDotProd(void)
{
	Vector a = { 1, 2, 3, 1 };
	Vector b = { 3, 2, 4, 1 };
	AssertEqual(DotProd(a, b), 19);
}

void TestCrossProd(void)
{
	Vector a = { 0, 0, 4, 1 };
	Vector b = { 0, -3, 0, 1 };
	Vector c = CrossProd(a, b);
	AssertEqual(c.x, 12);
	AssertEqual(c.y, 0);
	AssertEqual(c.z, 0);
}

void TestNormalizeVector(void)
{
	Vector a = { 27, 0, 0, 1 };
	Vector b = NormalizeVector(a);
	AssertEqual(b.x, 1);
	AssertEqual(b.y, 0);
	AssertEqual(b.z, 0);
}