~zjm/Moon3D

6be92002954d4c08caed6cde1e53d5491df96c2f — Zack Michener a month ago e8d32b5
update physics to use transforms
7 files changed, 672 insertions(+), 242 deletions(-)

A src/arraylist.h
M src/everything.h
M src/game.c
A src/transform.c
A src/transform.h
A src/vector.c
A src/vector.h
A src/arraylist.h => src/arraylist.h +35 -0
@@ 0,0 1,35 @@
#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
	}
}

M src/everything.h => src/everything.h +6 -18
@@ 1,6 1,8 @@
#pragma once

#include "stdint.h"
#include <stdint.h>
#include "vector.h"
#include "transform.h"

#define Rad(x) ((x)*M_PI/180)



@@ 25,14 27,6 @@ typedef double Color;
typedef uint32_t Pixel;
typedef double DepthVal;

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

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

typedef struct Polygon {
	Vertex *vertices;
	int *vList;


@@ 57,7 51,7 @@ typedef struct Renderable {
} Renderable;

typedef struct Physics {
	Vector position;
	Transform position;
	Vector velocity;
	Vector acceleration;
} Physics;


@@ 78,8 72,6 @@ typedef struct Scene {
	Particle *particles;
} Scene;

Vector V(double x, double y, double z);
Vector VecAdd(Vector a, Vector b);
Scene *MoonWorld(void);
Scene *NewScene(void);
Mesh *LoadMesh(const char *filename);


@@ 87,7 79,7 @@ SceneObject *CreateSceneObject(Scene *scene, Mesh *mesh, Color color);
Renderable *NewRenderablePool(void);
Renderable *NewRenderable(Renderable *pool, Mesh *mesh, Color color);
Physics *NewPhysicsPool(void);
Physics *NewPhysics(Physics *pool, Vector position, Vector velocity, Vector acceleration);
Physics *NewPhysics(Physics *pool, Transform position, Vector velocity, Vector acceleration);
SceneObject *NewSceneObjectPool(void);
SceneObject *NewSceneObject(SceneObject *pool, Renderable *renderable, Physics *physics);
void InitParticleSystem(Scene *scene);


@@ 95,13 87,9 @@ Particle *NewParticlePool(void);
Particle *AddParticle(Particle *pool, Vertex v);
Viewport *New3DViewport(Pixel *pixels, int width, int height);
DepthVal *NewDepthBuffer(int width, int height);
void SetIdentity(Transform *m);
Transform Identity(void);
Transform PerspectiveTransform(double fov, double aspect, double near, double far);
Transform CopyTransform(Transform transform);
void AddTransform(Transform A, Transform *subject);
void UpdateScene(Scene *scene, int dt);
void UpdatePhysics(Physics physics, int dt);
void UpdateParticle(Particle particle, int dt);
void RenderScene(Scene *scene, Viewport *viewport);
void Render(Renderable renderable, Viewport *viewport);
void Render(Renderable renderable, Transform modelingTransform, Viewport *viewport);

M src/game.c => src/game.c +157 -224
@@ 24,316 24,249 @@

int main(void)
{
  unsigned int SCREEN_WIDTH = 400;
  unsigned int SCREEN_HEIGHT = 240;

  if (SDL_Init(SDL_INIT_VIDEO) == 0) {
    SDL_Window* window = NULL;
    SDL_Renderer* renderer = NULL;

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

      Scene *scene = MoonWorld();
      Viewport *viewport = New3DViewport(pixels, SCREEN_WIDTH, SCREEN_HEIGHT);

      long frames = 0;
      long start = SDL_GetTicks();
      long dt = 0;
      long lastTime = SDL_GetTicks();
      while (!done) {
        frames++;

        dt = SDL_GetTicks() - lastTime;
        lastTime = SDL_GetTicks();
        UpdateScene(scene, dt);
        RenderScene(scene, viewport);

        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;
          }
        }
      }
      long time = (SDL_GetTicks() - start)/1000;
      double fps = (double)frames/(double)time;
      printf("%f fps\n", fps);

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

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

Vector VecAdd(Vector a, Vector b)
{
  return V(a.x + b.x, a.y + b.y, a.z + b.z);
	unsigned int SCREEN_WIDTH = 400;
	unsigned int SCREEN_HEIGHT = 240;

	if (SDL_Init(SDL_INIT_VIDEO) == 0) {
		SDL_Window* window = NULL;
		SDL_Renderer* renderer = NULL;

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

			Scene *scene = MoonWorld();
			Viewport *viewport = New3DViewport(pixels, SCREEN_WIDTH, SCREEN_HEIGHT);

			long frames = 0;
			long start = SDL_GetTicks();
			long dt = 0;
			long lastTime = SDL_GetTicks();
			while (!done) {
				frames++;

				dt = SDL_GetTicks() - lastTime;
				lastTime = SDL_GetTicks();
				UpdateScene(scene, dt);
				RenderScene(scene, viewport);

				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;
					}
				}
			}
			long time = (SDL_GetTicks() - start)/1000;
			double fps = (double)frames/(double)time;
			printf("%f fps\n", fps);

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

Scene *MoonWorld(void)
{
  Scene *scene = NewScene();
  CreateSceneObject(scene, LoadMesh("assets/moon.obj"), GRAY);

  InitParticleSystem(scene);
  for (int i = 0; i < 1000; i++) {
    double azimuth = rand() % 360;
    double altitude = rand() % 90;
    double dist = 1000;
    double x = dist*cos(Rad(azimuth));
    double z = dist*sin(Rad(azimuth));
    double y = dist*sin(Rad(altitude));
    AddParticle(scene->particles, V(x, y, z));
  }

  return scene;
	Scene *scene = NewScene();
	CreateSceneObject(scene, LoadMesh("assets/moon.obj"), GRAY);

	InitParticleSystem(scene);
	for (int i = 0; i < 1000; i++) {
		double azimuth = rand() % 360;
		double altitude = rand() % 90;
		double dist = 1000;
		double x = dist*cos(Rad(azimuth));
		double z = dist*sin(Rad(azimuth));
		double y = dist*sin(Rad(altitude));
		AddParticle(scene->particles, V(x, y, z));
	}

	return scene;
}

Scene *NewScene(void)
{
  Scene *scene = malloc(sizeof(Scene));
  scene->objects = NewSceneObjectPool();
  scene->renderable_pool = NewRenderablePool();
  scene->physics_pool = NewPhysicsPool();
  scene->particles = NULL;
  return scene;
	Scene *scene = malloc(sizeof(Scene));
	scene->objects = NewSceneObjectPool();
	scene->renderable_pool = NewRenderablePool();
	scene->physics_pool = NewPhysicsPool();
	scene->particles = NULL;
	return scene;
}

Mesh *LoadMesh(const char *filename)
{
  return (Mesh *)0;
	return (Mesh *)0;
}

SceneObject *CreateSceneObject(Scene *scene, Mesh *mesh, Color color)
{
  Renderable *renderable = NewRenderable(scene->renderable_pool, mesh, color);
  Physics *physics = NewPhysics(scene->physics_pool, V(0, 0, 0), V(0, 0, 0), V(0, 0, 0));
  SceneObject *o = NewSceneObject(scene->objects, renderable, physics);
  return o;
	Renderable *renderable = NewRenderable(scene->renderable_pool, mesh, color);
	Physics *physics = NewPhysics(scene->physics_pool, Identity(), V(0, 0, 0), V(0, 0, 0));
	SceneObject *o = NewSceneObject(scene->objects, renderable, physics);
	return o;
}

Renderable *NewRenderablePool(void)
{
  int *raw_pool = malloc(sizeof(Renderable)*MAX_SCENE_OBJECTS + sizeof(int));
  raw_pool[0] = 0;
  return (Renderable *)(raw_pool + 1);
	int *raw_pool = malloc(sizeof(Renderable)*MAX_SCENE_OBJECTS + sizeof(int));
	raw_pool[0] = 0;
	return (Renderable *)(raw_pool + 1);
}

Renderable *NewRenderable(Renderable *pool, Mesh *mesh, Color color)
{
  assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
  Renderable *renderable = PoolNext(pool);
  renderable->mesh = mesh;
  renderable->color = color;
  return renderable;
	assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
	Renderable *renderable = PoolNext(pool);
	renderable->mesh = mesh;
	renderable->color = color;
	return renderable;
}

Physics *NewPhysicsPool(void)
{
  int *raw_pool = malloc(sizeof(Physics)*MAX_SCENE_OBJECTS + sizeof(int));
  raw_pool[0] = 0;
  return (Physics *)(raw_pool + 1);
	int *raw_pool = malloc(sizeof(Physics)*MAX_SCENE_OBJECTS + sizeof(int));
	raw_pool[0] = 0;
	return (Physics *)(raw_pool + 1);
}

Physics *NewPhysics(Physics *pool, Vector position, Vector velocity, Vector acceleration)
Physics *NewPhysics(Physics *pool, Transform position, Vector velocity, Vector acceleration)
{
  assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
  Physics *physics = PoolNext(pool);
  physics->position = position;
  physics->velocity = velocity;
  physics->acceleration = acceleration;
  return physics;
	assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
	Physics *physics = PoolNext(pool);
	physics->position = position;
	physics->velocity = velocity;
	physics->acceleration = acceleration;
	return physics;
}

SceneObject *NewSceneObjectPool(void)
{
  int *raw_pool = malloc(sizeof(SceneObject)*MAX_SCENE_OBJECTS + sizeof(int));
  raw_pool[0] = 0;
  return (SceneObject *)(raw_pool + 1);
	int *raw_pool = malloc(sizeof(SceneObject)*MAX_SCENE_OBJECTS + sizeof(int));
	raw_pool[0] = 0;
	return (SceneObject *)(raw_pool + 1);
}

SceneObject *NewSceneObject(SceneObject *pool, Renderable *renderable, Physics *physics)
{
  assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
  SceneObject *obj = PoolNext(pool);
  obj->renderable = renderable;
  obj->physics = physics;
  return obj;
	assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
	SceneObject *obj = PoolNext(pool);
	obj->renderable = renderable;
	obj->physics = physics;
	return obj;
}

void InitParticleSystem(Scene *scene)
{
  scene->particles = NewParticlePool();
	scene->particles = NewParticlePool();
}

Particle *NewParticlePool(void)
{
  int *raw_pool = malloc(sizeof(Particle)*MAX_PARTICLES + sizeof(int));
  raw_pool[0] = 0;
  return (Particle *)(raw_pool + 1);
	int *raw_pool = malloc(sizeof(Particle)*MAX_PARTICLES + sizeof(int));
	raw_pool[0] = 0;
	return (Particle *)(raw_pool + 1);
}

Particle *AddParticle(Particle *pool, Vertex v)
{
  assert(PoolTotal(pool) < MAX_PARTICLES);
  Particle *p = PoolNext(pool);
  p->physics.position = v;
  p->physics.velocity = V(0, 0, 0);
  p->physics.acceleration = V(0, 0, 0);
  return p;
	assert(PoolTotal(pool) < MAX_PARTICLES);
	Particle *p = PoolNext(pool);
	p->physics.position = Translation(v);
	p->physics.velocity = V(0, 0, 0);
	p->physics.acceleration = V(0, 0, 0);
	return p;
}

Viewport *New3DViewport(Pixel *pixels, int width, int height)
{
  Viewport *v = malloc(sizeof(Viewport));
  v->pixels = pixels;
  v->depthBuffer = NewDepthBuffer(width, height);
  v->width = width;
  v->height = height;
  v->projectionTransform = PerspectiveTransform(60, (double)width/(double)height, 1, 100);
  return v;
	Viewport *v = malloc(sizeof(Viewport));
	v->pixels = pixels;
	v->depthBuffer = NewDepthBuffer(width, height);
	v->width = width;
	v->height = height;
	v->projectionTransform = PerspectiveTransform(60, (double)width/(double)height, 1, 100);
	return v;
}

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

Transform PerspectiveTransform(double fov, double aspect, double near, double far)
{
  Transform op, t;

  t = Identity();

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

  return t;
}

Transform Identity(void)
{
  Transform t;
  SetIdentity(&t);
  return t;
}
	Transform op, 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;
}
	t = Identity();

Transform CopyTransform(Transform transform)
{
  int i, j;
  Transform newTransform;
  for (i = 0; i < 4; i++) {
    for (j = 0; j < 4; j++) {
      newTransform.m[i][j] = transform.m[i][j];
    }
  }
  return newTransform;
}
	// perspective transform into canonical view volume
	op = Identity();
	op.m[0][0] = 1/(aspect * tan(fov/2));
	op.m[1][1] = 1/tan(fov/2);
	op.m[2][2] = (far + near) / (far - near);
	op.m[3][3] = 0;
	op.m[2][3] = (2*far*near) / (far - near);
	op.m[3][2] = -1;
	AddTransform(op, &t);

void AddTransform(Transform A, Transform *subject)
{
  int i, j, s;
  Transform B = CopyTransform(*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];
      }
    }
  }
	return t;
}

void UpdateScene(Scene *scene, int dt)
{
  int i;
	int i;

  for (i = 0; i < PoolTotal(scene->physics_pool); i++) {
    UpdatePhysics(scene->physics_pool[i], dt);
  }
	for (i = 0; i < PoolTotal(scene->physics_pool); i++) {
		UpdatePhysics(scene->physics_pool[i], dt);
	}

  if (scene->particles != NULL) {
    for (i = 0; i < PoolTotal(scene->particles); i++) {
      UpdateParticle(scene->particles[i], dt);
    }
  }
	if (scene->particles != NULL) {
		for (i = 0; i < PoolTotal(scene->particles); i++) {
			UpdateParticle(scene->particles[i], dt);
		}
	}
}

void UpdatePhysics(Physics physics, int dt)
{
  VecAdd(physics.position, physics.velocity);
  VecAdd(physics.velocity, physics.acceleration);
	Translate(&physics.position, VecMul(physics.velocity, dt));
	VecAdd(physics.velocity, physics.acceleration);
}

void UpdateParticle(Particle particle, int dt)
{
  UpdatePhysics(particle.physics, dt);
	UpdatePhysics(particle.physics, dt);
}

void RenderScene(Scene *scene, Viewport *viewport)
{
  for (int i = 0; i < PoolTotal(scene->renderable_pool); i++) {
    Render(scene->renderable_pool[i], viewport);
  }
	for (int i = 0; i < PoolTotal(scene->renderable_pool); i++) {
		Render(scene->renderable_pool[i], scene->physics_pool[i].transform viewport);
	}
}

void Render(Renderable renderable, Viewport *viewport)
void Render(Renderable renderable, Transform modelingTransform, Viewport *viewport)
{

}

A src/transform.c => src/transform.c +251 -0
@@ 0,0 1,251 @@
#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);
}

A src/transform.h => src/transform.h +34 -0
@@ 0,0 1,34 @@
#pragma once

#include "vector.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);

A src/vector.c => src/vector.c +150 -0
@@ 0,0 1,150 @@
#include "vector.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);
}

A src/vector.h => src/vector.h +39 -0
@@ 0,0 1,39 @@
#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);
#define FromVertex(v) FromVector(v)
Vector *FromVector(Vector v);
bool VectorEqual(Vector a, Vector b);
#define VertexEqual(a, b) VectorEqual(a, b)
#define NewVertex(x, y, z) NewVector(x, y, z)
Vector *NewVector(double x, double y, double z);
Vector *NewVector4D(double x, double y, double z, double w);
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);