~zjm/Moon3D

0fe31db4998f636b8f65ca790b75c0585ba47d86 — Zack Michener 3 months ago 0e81b82
refactor camera/projection to avoid globals
M src/3d/image/camera.c => src/3d/image/camera.c +6 -35
@@ 2,50 2,27 @@
#include <stdlib.h>
#include <assert.h>

static Camera *camera = NULL;

void SetCamera(Vector position)
Camera *NewCamera(Vector position)
{
	if (camera != NULL) {
		free(camera);
	}
	camera = malloc(sizeof(Camera));
	Camera *camera = malloc(sizeof(Camera));
	camera->position = position;
	camera->direction = V(0, 0, -1);
	camera->up = V(0, 1, 0);
	camera->transform = Identity();
}

Camera *GetCamera(void)
{
	return camera;
}

Vector CameraPosition(void)
{
	assert(camera != NULL);
	return camera->position;
}

Vector CameraDirection(void)
void LookAt(Vector p, Camera *camera)
{
	assert(camera != NULL);
	return camera->direction;
}

void LookAt(Vector p)
{
	assert(camera != NULL);
	camera->direction = NormalizeVector(VecSub(p, camera->position));
}

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

void RotateCamera(double rx, double ry, double rz)
void RotateCamera(double rx, double ry, double rz, Camera *camera)
{
	Transform t = RotationX(rx);
	AddTransform(RotationY(ry), &t);


@@ 53,13 30,7 @@ void RotateCamera(double rx, double ry, double rz)
	camera->direction = NormalizeVector(ApplyTransform(t, camera->direction));
}

void PlaceCamera(Vector p)
{
	assert(camera != NULL);
	camera->position = p;
}

Transform AlignCameraToAxis(void)
Transform AlignCameraToAxis(Camera *camera)
{
	Vector Rx, Ry, Rz;
	Transform op = Identity();

M src/3d/image/camera.h => src/3d/image/camera.h +6 -9
@@ 9,12 9,9 @@ typedef struct Camera {
	Transform transform;
} Camera;

void SetCamera(Vector position);
Camera *GetCamera(void);
Vector CameraPosition(void);
Vector CameraDirection(void);
void LookAt(Vector p);
void MoveCamera(Vector dv);
void RotateCamera(double rx, double ry, double rz);
void PlaceCamera(Vector p);
Transform AlignCameraToAxis(void);
Camera *NewCamera(Vector position);
void LookAt(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);

M src/3d/image/project.c => src/3d/image/project.c +62 -61
@@ 7,9 7,6 @@
#include "camera.h"

static Transform *deviceTransform = NULL;
static AABBox *orthoVolume = NULL;
static Perspective *perspective = NULL;
static ProjectionMode mode = ORTHO;

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


@@ 21,100 18,104 @@ void InitDeviceTransform(unsigned int width, unsigned int height)
	Scale(deviceTransform, 0.5*(width-1), 0.5*(height-1), 1);
}

void SetOrtho(AABBox vol)
Projection *NewOrthoProjection(AABBox vol)
{
	if (orthoVolume != NULL) {
		free(orthoVolume);
	}
	orthoVolume = FromAABBox(&vol, Identity());
	mode = ORTHO;
	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;
}

void SetPerspective(double fov, double aspect, double near, double far)
Projection *NewPerspectiveProjection(double fov, double aspect, double near, double far)
/* field of view is in degrees */
{
	if (perspective != NULL) {
		free(perspective);
	}
	perspective = malloc(sizeof(Perspective));
	perspective->fov = Rad(fov);
	perspective->aspect = aspect;
	perspective->near = near;
	perspective->far = far;
	mode = PERSPECTIVE;
	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 OrthoProjection(void)
Transform OrthoTransform(Projection *projection)
{
	Transform projection;
	Transform t;

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

	projection = CameraTransform();
	t = Identity();

	// translate to canonical view volume
	Translate(&projection, V(
			-(orthoVolume->right + orthoVolume->left) / 2,
			-(orthoVolume->top + orthoVolume->bottom) / 2,
			-orthoVolume->near));
	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(&projection,
			2 / (orthoVolume->right - orthoVolume->left),
			2 / (orthoVolume->top - orthoVolume->bottom),
			1 / (orthoVolume->near - orthoVolume->far));
	Scale(&t,
			2 / (projection->ortho.right - projection->ortho.left),
			2 / (projection->ortho.top - projection->ortho.bottom),
			1 / (projection->ortho.near - projection->ortho.far));

	return projection;
	return t;
}

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

	assert(perspective != NULL);
	assert(perspective->near != perspective->far);
	assert(projection->perspective.type == PERSPECTIVE);

	projection = CameraTransform();
	t = Identity();

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

	return projection;
	return t;
}

Transform CameraTransform(void)
Transform CameraTransform(Camera *camera)
{
	Camera *camera = GetCamera();
	assert(camera != NULL);

	Transform projection = Translation(NegVec(camera->position));
	AddTransform(AlignCameraToAxis(), &projection);
	AddTransform(camera->transform, &projection);
	Transform t = Translation(NegVec(camera->position));
	AddTransform(AlignCameraToAxis(camera), &t);
	AddTransform(camera->transform, &t);

	return projection;
	return t;
}

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

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

Vertex ViewmapPoint(Vertex p)


@@ 123,9 124,9 @@ Vertex ViewmapPoint(Vertex p)
	return Homogenize(ApplyTransform(*deviceTransform, p));
}

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

void ViewmapPolygon(Polygon *polygon)

M src/3d/image/project.h => src/3d/image/project.h +30 -13
@@ 2,28 2,45 @@

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

typedef struct Perspective {
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;
} Perspective;
	double left;
	double right;
	double bottom;
	double top;
} OrthoProjection;

typedef enum ProjectionMode {
	ORTHO, PERSPECTIVE
} ProjectionMode;
typedef union Projection {
	PerspectiveProjection perspective;
	OrthoProjection ortho;
} Projection;

void InitDeviceTransform(unsigned int width, unsigned int height);
void SetOrtho(AABBox vol);
void SetPerspective(double fov, double aspect, double near, double far);

Transform OrthoProjection(void);
Transform PerspectiveProjection(void);
Transform CameraTransform(void);
Transform GetProjection(void);
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);
Vertex ProjectPoint(Vertex p, Transform t);
Vertex ViewmapPoint(Vertex p);
Polygon *ProjectPolygon(Polygon *polygon);
Polygon *ProjectPolygon(Polygon *polygon, Transform t);
void ViewmapPolygon(Polygon *polygon);

M src/3d/render.c => src/3d/render.c +19 -16
@@ 4,29 4,31 @@
#include "device.h"
#include "abstract.h"

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

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

void DrawPolygon(Polygon *polygon, Color color, RenderingContext context)
void DrawPolygon(Polygon *polygon, Color color, RenderingContext context, Transform imageTransform)
{
	Polygon *image = ProjectPolygon(polygon);
	Polygon *image = ProjectPolygon(polygon, imageTransform);
	if (ClipPolygon(&image)) {
		if (!BackfaceCulled(image)) {
			HomogenizePolygon(image);


@@ 50,9 52,9 @@ void WireframePolygon(Polygon *polygon, Color color, Viewport *viewport)
	}
}

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


@@ 61,10 63,10 @@ void DrawPoint(Point *p, RenderingContext context)
	}
}

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


@@ 74,11 76,12 @@ void DrawLine(Vertex a, Vertex b, Color color, Viewport *viewport)
	}
}

void DrawAxes(Viewport *viewport)
void DrawAxes(RenderingContext context)
{
	DrawLine(V(0, 0, 0), V(10, 0, 0), LT_GRAY, viewport);
	DrawLine(V(0, 0, 0), V(0, 10, 0), LT_GRAY, viewport);
	DrawLine(V(0, 0, 0), V(0, 0, 10), LT_GRAY, viewport);
	Transform imageTransform = GetProjectionTransform(context.camera, context.projection);
	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)

M src/3d/render.h => src/3d/render.h +7 -6
@@ 9,15 9,16 @@
typedef struct RenderingContext {
	Viewport *viewport;
	Camera *camera;
	Projection *projection;
	RasterSettings settings;
} RenderingContext;

RenderingContext MakeRenderingContext(Viewport *viewport, Camera *camera, RasterSettings settings);
void DrawObject(Object *object, LightSource **lights, RenderingContext context);
void DrawPolygon(Polygon *polygon, Color color, RenderingContext context);
RenderingContext MakeRenderingContext(Viewport *viewport, Camera *camera, Projection *projection, 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);
void DrawLine(Vertex a, Vertex b, Color color, Viewport *viewport);
void DrawAxes(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);


M src/player.c => src/player.c +8 -9
@@ 5,39 5,38 @@ Player *NewPlayer(Vertex position)
{
	Player *player = malloc(sizeof(Player));

	SetCamera(position);
	LookAt(V(0, 0, 0));
	SetPerspective(60, 400/240.f, 1, 100);
	player->camera = NewCamera(position);
	LookAt(V(0, 0, 0), player->camera);

	return player;
}

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

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

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

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

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

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

M src/player.h => src/player.h +1 -1
@@ 3,7 3,7 @@
#include "3d.h"

typedef struct Player {
	Camera camera;
	Camera *camera;
} Player;

Player *NewPlayer(Vertex position);

M src/scene.c => src/scene.c +4 -2
@@ 27,11 27,13 @@ void AddPoint(Scene *scene, Point *point)

void RenderScene(Scene *scene, RenderingContext context)
{
	Transform imageTransform = GetProjectionTransform(context.camera, context.projection);

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

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

M src/sketch.c => src/sketch.c +4 -2
@@ 15,9 15,11 @@ void sketch_setup(Pixel *pixels)
	Viewport *viewport = NewViewport(pixels, BLACK, 400, 240);
	RasterSettings settings = DefaultRasterSettings();
	scene = CubeWorld();
	context = MakeRenderingContext(viewport, &scene->player->camera, settings);
	Camera *camera = scene->player->camera;
	Projection *projection = NewPerspectiveProjection(60, 400/240.f, 1, 100);
	context = MakeRenderingContext(viewport, camera, projection, settings);

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


M test/project.test.c => test/project.test.c +14 -13
@@ 10,26 10,27 @@ void ProjectionTests(void)

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

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

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

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

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

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



@@ 89,20 90,20 @@ void TestViewmapPolygon(void)

void TestCameraPosition(void)
{
	SetCamera(V(10, 20, -3));
	Vector p = CameraPosition();
	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));
	p = CameraPosition();
	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);
	p = CameraPosition();
	RotateCamera(0, 130, 0, camera);
	p = camera->position;
	AssertEqual(p.x, 11);
	AssertEqual(p.y, 21);
	AssertEqual(p.z, -2);