~zjm/Moon3D

bb992515fc90aeeb1b1e33841fa2ffb9961d8f94 — Zack Michener a month ago 8f875a7
add Pineda rasterization!
9 files changed, 122 insertions(+), 54 deletions(-)

M README.md
M src/game.c
M src/mesh.c
M src/mesh.h
M src/renderable.c
M src/scene.c
M src/scene.h
M src/viewport.c
M src/viewport.h
M README.md => README.md +0 -1
@@ 10,7 10,6 @@ support the to-be-released Playdate handheld console.
## TODO

- Use ints for rasterization
- Implement [Pineda triangle raster algorithm][pineda]
- Use [guard-band clipping][clipping]
- Add sub-pixel precision
- Gouraud shading

M src/game.c => src/game.c +5 -1
@@ 43,7 43,7 @@ int main(void)

			// one-shot
			// UpdateScene(scene, 1);
			// ClearScreen(gameRenderer);
			// ClearScreen(gameRenderer->viewport);
			// RenderScene(scene, gameRenderer);

			/* variables for perf statistics */


@@ 103,10 103,14 @@ Scene *MoonWorld(void)
{
	Scene *scene = NewScene();
	SceneObject *obj = CreateSceneObject(scene, "assets/cube.obj", BLACK);
	obj->renderable->color = LT_GRAY;
	SceneObject *obj2 = CloneSceneObject(scene, obj);
	obj2->renderable->color = DK_GRAY;
	PlaceModel(obj->model, V(0, 0, -10));
	Spin(obj->physics, V(1, 1, 0), 0.1);
	Spin(obj->physics, V(0, 1, 1), 0.05);
	Spin(obj->physics, V(0, 1, 0), 0.1);
	PlaceModel(obj2->model, V(3, 0, -15));

	// InitParticleSystem(scene);
	// for (int i = 0; i < 1000; i++) {

M src/mesh.c => src/mesh.c +0 -21
@@ 78,27 78,6 @@ void PrintPolygon(int *polygon, Vertex *vertices)
		PrintVec(*PolygonVertex(polygon, vertices, i));
	}
	printf("\n");
	fflush(stdout);
}

Rect PolygonBounds(int *polygon, Vertex *vertices)
{
	Rect r = { vertices[0].x, vertices[0].y, vertices[0].x, vertices[0].y };
	for (int i = 0; i < NumPolygonVertices(polygon); i++) {
		Vertex *v = PolygonVertex(polygon, vertices, i);
		if (v->x < r.left)		r.left = v->x;
		if (v->x > r.right)		r.right = v->x;
		if (v->y < r.top)		r.top = v->y;
		if (v->y > r.bottom)	r.bottom = v->y;
	}
	return r;
}

void PrintRect(Rect r)
{
	printf("%6.2f, %6.2f┌───┐%6.2f, %6.2f\n",  r.top, r.left, r.top, r.right);
	printf("              │   │\n");
	printf("%6.2f, %6.2f└───┘%6.2f, %6.2f\n", r.bottom, r.left, r.bottom, r.right);
}

void Triangularize(int *polygon, Mesh *mesh)

M src/mesh.h => src/mesh.h +0 -9
@@ 3,13 3,6 @@
#include "vector.h"
#include "pool.h"

typedef struct Rect {
	float top;
	float left;
	float right;
	float bottom;
} Rect;

typedef struct Mesh {
  Vertex *vertices;
  int *faces;


@@ 33,6 26,4 @@ Vertex *NewVertex(Vertex *pool);

int *NewPolygon(int *pool);
void PrintPolygon(int *polygon, Vertex *vertices);
Rect PolygonBounds(int *polygon, Vertex *vertices);
void PrintRect(Rect r);
void Triangularize(int *polygon, Mesh *mesh);

M src/renderable.c => src/renderable.c +2 -2
@@ 59,8 59,8 @@ void Render(Renderable renderable, Renderer *renderer, Transform camera_transfor
		 i < PoolTotal(renderMesh.faces);
		 i += NumPolygonVertices(polygon)+1, NextPolygon(polygon)) {
		if (NumPolygonVertices(polygon) > 0) {
			RasterizePolygon(polygon, renderMesh.vertices, renderer->viewport);
			WireframePolygon(polygon, renderMesh.vertices, renderer->viewport);
			DrawPolygon(polygon, renderMesh.vertices, renderable.color, renderer->viewport);
			// WireframePolygon(polygon, renderMesh.vertices, renderer->viewport);
		}
	}
}

M src/scene.c => src/scene.c +10 -0
@@ 50,6 50,16 @@ SceneObject *NewSceneObject(SceneObject *pool, Model *model, Renderable *rendera
	return obj;
}

SceneObject *CloneSceneObject(Scene *scene, SceneObject *obj)
{
	Model *model = NewModel(scene->model_pool);
	Renderable *renderable = NewRenderable(scene->renderable_pool, model, obj->renderable->color);
	renderable->mesh = obj->renderable->mesh;
	Physics *physics = NewPhysics(scene->physics_pool, model);
	SceneObject *clone = NewSceneObject(scene->objects, model, renderable, physics);
	return clone;
}

void InitParticleSystem(Scene *scene)
/* Activates a Scene's particle pool. Presence of this pool will also enable
particle processing.

M src/scene.h => src/scene.h +1 -0
@@ 26,6 26,7 @@ Scene *NewScene(void);
SceneObject *CreateSceneObject(Scene *scene, const char *meshFile, Color color);
SceneObject *NewSceneObjectPool(void);
SceneObject *NewSceneObject(SceneObject *pool, Model *model, Renderable *renderable, Physics *physics);
SceneObject *CloneSceneObject(Scene *scene, SceneObject *obj);
void InitParticleSystem(Scene *scene);
void UpdateScene(Scene *scene, int dt);
void RenderScene(Scene *scene, Renderer *renderer);

M src/viewport.c => src/viewport.c +82 -15
@@ 2,6 2,8 @@
#include "mesh.h"
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>

Viewport *NewViewport(Pixel *pixels, Color background_color, int width, int height)
{


@@ 37,21 39,49 @@ void ClearScreen(Viewport *viewport)
	}
}

void RasterizePolygon(int *polygon, Vertex *vertices, Viewport *viewport)
void DrawPolygon(int *polygon, Vertex *vertices, Color color, Viewport *viewport)
{
	assert(NumPolygonVertices(polygon) == 3);
	Rect bounding_box = PolygonBounds(polygon, vertices);
	DrawBoundingBox(bounding_box, viewport);
	EdgeTracker a = TrackEdge(PolygonVertex(polygon, vertices, 0), PolygonVertex(polygon, vertices, 1), V(bounding_box.left, bounding_box.bottom, 0));
	EdgeTracker b = TrackEdge(PolygonVertex(polygon, vertices, 1), PolygonVertex(polygon, vertices, 2), V(bounding_box.left, bounding_box.bottom, 0));
	EdgeTracker c = TrackEdge(PolygonVertex(polygon, vertices, 2), PolygonVertex(polygon, vertices, 0), V(bounding_box.left, bounding_box.bottom, 0));

	for (int y = bounding_box.bottom; y < bounding_box.top; y += 2) {
		for (int x = bounding_box.left; x <= bounding_box.right; x++) {
			if (a.value < 0 && b.value < 0 && c.value < 0) {
				// TryWritePixel(x, y, z, color, viewport);
				WritePixel(x, y, color, viewport);
			}
			a.value += a.dy;
			b.value += b.dy;
			c.value += c.dy;
		}
		a.value -= a.dx;
		b.value -= b.dx;
		c.value -= c.dx;
		for (int x = bounding_box.right; x >= bounding_box.left; x--) {
			if (a.value < 0 && b.value < 0 && c.value < 0) {
				WritePixel(x, y + 1, color, viewport);
			}
			a.value -= a.dy;
			b.value -= b.dy;
			c.value -= c.dy;
		}
		a.value -= a.dx;
		b.value -= b.dx;
		c.value -= c.dx;
	}
}

void DrawBoundingBox(Rect bounding_box, Viewport *viewport)
{
	RasterLine(V(bounding_box.left, bounding_box.top, 0), V(bounding_box.right, bounding_box.top, 0), GRAY, viewport);
	RasterLine(V(bounding_box.right, bounding_box.top, 0), V(bounding_box.right, bounding_box.bottom, 0), GRAY, viewport);
	RasterLine(V(bounding_box.left, bounding_box.bottom, 0), V(bounding_box.right, bounding_box.bottom, 0), GRAY, viewport);
	RasterLine(V(bounding_box.left, bounding_box.top, 0), V(bounding_box.left, bounding_box.bottom, 0), GRAY, viewport);
	DrawLine(V(bounding_box.left, bounding_box.top, 0), V(bounding_box.right, bounding_box.top, 0), GRAY, viewport);
	DrawLine(V(bounding_box.right, bounding_box.top, 0), V(bounding_box.right, bounding_box.bottom, 0), GRAY, viewport);
	DrawLine(V(bounding_box.left, bounding_box.bottom, 0), V(bounding_box.right, bounding_box.bottom, 0), GRAY, viewport);
	DrawLine(V(bounding_box.left, bounding_box.top, 0), V(bounding_box.left, bounding_box.bottom, 0), GRAY, viewport);
}

// TODO: find a more efficient way to wireframe a mesh without duplicating edges
void WireframePolygon(int *polygon, Vertex *vertices, Viewport *viewport)
/* Rasterizes lines between each edge of a polygon */
{


@@ 59,31 89,31 @@ void WireframePolygon(int *polygon, Vertex *vertices, Viewport *viewport)
	previous = LastPolygonVertex(polygon, vertices);
	for (int i = 0; i < NumPolygonVertices(polygon); i++) {
		current = PolygonVertex(polygon, vertices, i);
		RasterLine(*previous, *current, BLACK, viewport);
		DrawLine(*previous, *current, BLACK, viewport);
		previous = current;
	}
}

void RasterLine(Vertex a, Vertex b, Color color, Viewport *viewport)
void DrawLine(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);
			DrawLineShallow(b, a, color, viewport);
		} else {
			RasterLineShallow(a, b, color, viewport);
			DrawLineShallow(a, b, color, viewport);
		}
	} else {
		if (a.y > b.y) {
			RasterLineSteep(b, a, color, viewport);
			DrawLineSteep(b, a, color, viewport);
		} else {
			RasterLineSteep(a, b, color, viewport);
			DrawLineSteep(a, b, color, viewport);
		}
	}
}

void RasterLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport)
void DrawLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport)
{
	double dx = b.x - a.x;
	double dy = b.y - a.y;


@@ 110,7 140,7 @@ void RasterLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport)
	}
}

void RasterLineSteep(Vertex a, Vertex b, Color color, Viewport *viewport)
void DrawLineSteep(Vertex a, Vertex b, Color color, Viewport *viewport)
{
	double dx = b.x - a.x;
	double dy = b.y - a.y;


@@ 146,6 176,13 @@ void WritePixel(int x, int y, Color color, Viewport *viewport)
	}
}

void TryWritePixel(int x, int y, double z, Color color, Viewport *viewport)
{
	if (UpdateDepthBuffer(x, y, z, viewport)) {
		WritePixel(x, y, color, viewport);
	}
}

Color GetPixel(int x, int y, Viewport *viewport)
/* Reads a pixel from a viewport's frame buffer */
{


@@ 179,3 216,33 @@ void DrawPoint(Vertex p, Color color, Viewport *viewport)
		WritePixel(p.x, p.y, color, viewport);
	}
}

Rect PolygonBounds(int *polygon, Vertex *vertices)
{
	Rect r = { floor(vertices[0].x), floor(vertices[0].y), floor(vertices[0].x), floor(vertices[0].y) };
	for (int i = 0; i < *polygon; i++) {
		Vertex v = vertices[polygon[i+1]];
		if (v.x < r.left)		r.left = floor(v.x);
		if (v.x > r.right)		r.right = floor(v.x);
		if (v.y < r.bottom)		r.bottom = floor(v.y);
		if (v.y > r.top)		r.top = floor(v.y);
	}
	return r;
}

void PrintRect(Rect r)
{
	printf("%3d, %3d┌───┐%3d, %3d\n",  r.left, r.top, r.right, r.top);
	printf("        │   │\n");
	printf("%3d, %3d└───┘%3d, %3d\n", r.left, r.bottom, r.right, r.bottom);
	fflush(stdout);
}

EdgeTracker TrackEdge(Vertex *a, Vertex *b, Vertex start)
{
	EdgeTracker et;
	et.dx = b->x - a->x;
	et.dy = b->y - a->y;
	et.value = (start.x - a->x)*et.dy - (start.y - a->y)*et.dx;
	return et;
}

M src/viewport.h => src/viewport.h +22 -5
@@ 29,17 29,34 @@ typedef struct Viewport {
	int height;
} Viewport;

typedef struct Rect {
	int left;
	int bottom;
	int right;
	int top;
} Rect;

typedef struct EdgeTracker {
	float dx;
	float dy;
	float value;
} EdgeTracker;

Viewport *NewViewport(Pixel *pixels, Color background_color, int width, int height);
DepthVal *NewDepthBuffer(int width, int height);
void ClearScreen(Viewport *viewport);
void RasterizePolygon(int *polygon, Vertex *vertices, Viewport *viewport);
void DrawPolygon(int *polygon, Vertex *vertices, Color color, Viewport *viewport);
void DrawBoundingBox(Rect boundingBox, Viewport *viewport);
void WireframePolygon(int *polygon, Vertex *vertices, Viewport *viewport);
void TryWritePixel(int x, int y, double z, Color color, Viewport *viewport);
void WritePixel(int x, int y, Color color, Viewport *viewport);
Color GetPixel(int x, int y, Viewport *viewport);
void WriteDepth(int x, int y, DepthVal value, Viewport *viewport);
void RasterLineSteep(Vertex a, Vertex b, Color color, Viewport *viewport);
void RasterLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport);
void RasterLine(Vertex a, Vertex b, Color color, Viewport *viewport);
void DrawLineSteep(Vertex a, Vertex b, Color color, Viewport *viewport);
void DrawLineShallow(Vertex a, Vertex b, Color color, Viewport *viewport);
void DrawLine(Vertex a, Vertex b, Color color, Viewport *viewport);
bool UpdateDepthBuffer(int x, int y, double z, Viewport *viewport);
void DrawPoint(Vertex p, Color color, Viewport *viewport);
\ No newline at end of file
void DrawPoint(Vertex p, Color color, Viewport *viewport);
Rect PolygonBounds(int *polygon, Vertex *vertices);
void PrintRect(Rect r);
EdgeTracker TrackEdge(Vertex *a, Vertex *b, Vertex start);