@@ 42,10 42,11 @@ int main(void)
Renderer *gameRenderer = NewRenderer(pixels, SCREEN_WIDTH, SCREEN_HEIGHT, scene->camera);
// one-shot
- UpdateScene(scene, 1);
- ClearScreen(gameRenderer);
- RenderScene(scene, gameRenderer);
+ // UpdateScene(scene, 1);
+ // ClearScreen(gameRenderer);
+ // RenderScene(scene, gameRenderer);
+ /* variables for perf statistics */
long frames = 0;
long start = SDL_GetTicks();
long dt = 0;
@@ 55,10 56,13 @@ int main(void)
dt = SDL_GetTicks() - lastTime;
lastTime = SDL_GetTicks();
- // UpdateScene(scene, dt);
- // ClearScreen(gameRenderer);
- // RenderScene(scene, gameRenderer);
+ /* main game loop */
+ UpdateScene(scene, dt);
+ ClearScreen(gameRenderer);
+ RenderScene(scene, gameRenderer);
+
+ /* copy our framebuffer to SDL for display */
SDL_UpdateTexture(texture, NULL, pixels, SCREEN_WIDTH*sizeof(uint32_t));
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
@@ 68,12 72,14 @@ int main(void)
// done = SDL_TRUE;
// }
+ /* listen for a quit event */
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
done = SDL_TRUE;
}
}
}
+ /* display perf stats */
long time = (SDL_GetTicks() - start)/1000;
double fps = (double)frames/(double)time;
printf("%f fps\n", fps);
@@ 88,22 94,11 @@ int main(void)
#endif
Scene *MoonWorld(void)
+/* A wrapper for creating and setting up a moon scene */
{
Scene *scene = NewScene();
- scene->camera->far = 50;
- scene->camera->near = 1;
- // scene->camera->physics->rotation = V(0, 0.1, 0);
- // PlaceModel(scene->camera->model, V(0, 0, 5));
- RotateModel(scene->camera->model, 0, -90, 0);
SceneObject *obj = CreateSceneObject(scene, "assets/cube.obj", BLACK);
PlaceModel(obj->model, V(0, 0, -10));
- // CommitTransform(RotationY(10), &obj->model->direction);
- // obj->physics->velocity = V(-0.001, 0, 0);
- // PlaceModel(obj->model, V(-1, 0, 0));
- // ScaleObject(obj, 10, 10, 10);
- // SpinObject(obj, 0, 1, 0);
-
- // DumpMesh(cube->renderable->mesh);
InitParticleSystem(scene);
for (int i = 0; i < 1000; i++) {
@@ 120,6 115,15 @@ Scene *MoonWorld(void)
}
Scene *NewScene(void)
+/* Allocates and initializes a Scene.
+A scene tracks all SceneObjects and all of their component parts. It owns global
+memory pools (to which other objects may have a reference). Pools exist for
+SceneObject, Renderable, Physics, Model, and Particle. The Particle pool is
+initially NULL, and must be enabled separately.
+
+Note that Scene does not track vertex or polygon pools. Those are owned
+individually by each Mesh, as well as temporary rendering pools in Renderer.
+*/
{
Scene *scene = malloc(sizeof(Scene));
scene->objects = NewPool(SceneObject, MAX_SCENE_OBJECTS);
@@ 134,6 138,7 @@ Scene *NewScene(void)
}
bool LoadObj(const char *filename, Mesh *mesh)
+/* Loads an OBJ file into an empty Mesh. Returns success or failure. */
{
FILE *fp;
bool fileOk = true;
@@ 155,6 160,9 @@ bool LoadObj(const char *filename, Mesh *mesh)
}
bool ReadObjLine(FILE *fp, Mesh *mesh)
+/* Reads the line type based on the first few characters and delegates parsing
+to subroutines. Right now we only support OBJ_VERTEX and OBJ_FACE.
+*/
{
char itemType[8];
if (fscanf(fp, "%8s", itemType) == 1) {
@@ 178,6 186,7 @@ bool ReadObjLine(FILE *fp, Mesh *mesh)
}
bool ReadObjVertex(FILE *fp, Mesh *mesh)
+/* Reads and appends a vertex to the mesh's vertex pool */
{
float x, y, z, w;
bool match;
@@ 191,13 200,13 @@ bool ReadObjVertex(FILE *fp, Mesh *mesh)
match = fscanf(fp, "%f", &w);
if (!match) w = 1.0;
- /* mesh->vertices should be at the beginning of a pool */
Vertex *vertex = NewVertex(mesh->vertices);
*vertex = V4D(x, y, z, w);
return true;
}
bool ReadObjFace(FILE *fp, Mesh *mesh)
+/* Reads and appends a face to the mesh's polygon pool */
{
int *polygon = NewPolygon(mesh->faces);
int vIndex;
@@ 231,6 240,7 @@ ObjLineType ParseLineType(const char *type)
}
SceneObject *CreateSceneObject(Scene *scene, const char *meshFile, Color color)
+/* Convenience function to load and create a SceneObject with an OBJ file */
{
Model *model = NewModel(scene->model_pool);
Renderable *renderable = NewRenderable(scene->renderable_pool, model, color);
@@ 241,6 251,7 @@ SceneObject *CreateSceneObject(Scene *scene, const char *meshFile, Color color)
}
Renderable *NewRenderable(Renderable *pool, Model *model, Color color)
+/* Creates and initializes a Renderable in a memory pool */
{
assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
Renderable *renderable = PoolNext(pool);
@@ 251,6 262,7 @@ Renderable *NewRenderable(Renderable *pool, Model *model, Color color)
}
Physics *NewPhysics(Physics *pool, Model *model)
+/* Creates and initializes a Physics in a memory pool */
{
assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
Physics *physics = PoolNext(pool);
@@ 262,6 274,16 @@ Physics *NewPhysics(Physics *pool, Model *model)
}
int *NewRawPool(int item_size, int max_items)
+/* Allocates and initializes a memory pool. Used by the `NewPool` macro.
+A memory pool is just a plain array of some item, except that immediately
+preceding it in memory is an int tracking the number of current items. Access
+is done by array subscripts, e.g. `Thing x = my_pool[12];`, but modifying the
+pool should be done through pool macros.
+
+`NewRawPool` allocates the memory for a pool and returns a pointer to the
+counter int. `NewPool` uses this, but returns a pointer to the first element,
+which is usually more useful.
+*/
{
int *raw_pool = malloc(item_size*(max_items) + sizeof(int));
raw_pool[0] = 0;
@@ 269,6 291,7 @@ int *NewRawPool(int item_size, int max_items)
}
SceneObject *NewSceneObject(SceneObject *pool, Model *model, Renderable *renderable, Physics *physics)
+/* Creates and initialized a new SceneObject */
{
assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
SceneObject *obj = PoolNext(pool);
@@ 279,11 302,15 @@ SceneObject *NewSceneObject(SceneObject *pool, Model *model, Renderable *rendera
}
void InitParticleSystem(Scene *scene)
+/* Activates a Scene's particle pool. Presence of this pool will also enable
+particle processing.
+*/
{
scene->particles = NewPool(Particle, MAX_PARTICLES);
}
Particle *AddParticle(Particle *pool, Vertex v)
+/* Creates and initializes a Particle */
{
assert(PoolTotal(pool) < MAX_PARTICLES);
Particle *p = PoolNext(pool);
@@ 294,6 321,11 @@ Particle *AddParticle(Particle *pool, Vertex v)
}
Renderer *NewRenderer(Pixel *pixels, int width, int height, Camera *camera)
+/* Creates and initializes a Renderer.
+The camera is owned and initialized by Scene, but Scene doesn't know the aspect
+ratio of the screen, so this will also update the camera with the screen's
+aspect ratio.
+*/
{
Renderer *r = malloc(sizeof(Renderer));
r->pixels = pixels;
@@ 325,6 357,7 @@ Renderer *NewRenderer(Pixel *pixels, int width, int height, Camera *camera)
}
void ClearScreen(Renderer *renderer)
+/* Clears the frame buffer and depth buffer */
{
for (int y = 0; y < renderer->height; y++) {
for (int x = 0; x < renderer->width; x++) {
@@ 335,6 368,7 @@ void ClearScreen(Renderer *renderer)
}
DepthVal *NewDepthBuffer(int width, int height)
+/* Allocates and initializes a depth buffer */
{
DepthVal *values = malloc(sizeof(DepthVal)*width*height);
for(int y = 0; y < height; y++) {
@@ 346,6 380,7 @@ DepthVal *NewDepthBuffer(int width, int height)
}
Camera *NewCamera(double fov, double aspect, double near, double far, Model *model, Physics *physics)
+/* Allocates and initializes a Camera */
{
Camera *camera = malloc(sizeof(Camera));
camera->model = model;
@@ 360,16 395,20 @@ Camera *NewCamera(double fov, double aspect, double near, double far, Model *mod
}
void SetCamera(Camera *camera, double fov, double aspect, double near, double far)
+/* Updates a Camera's properties */
{
camera->fov = fov;
camera->aspect = aspect;
- camera->x_cotan = 1/tan(Rad(fov)/2);
+ camera->x_cotan = 1/tan(Rad(fov)/2); /* precalculated for efficiency */
camera->y_cotan = aspect/tan(Rad(fov)/2);
camera->near = near;
camera->far = far;
}
Transform CameraTransform(Camera *camera)
+/* Returns the transform to move world vertices into the camera's frame of
+reference, and project them into image space
+*/
{
Transform camera_transform = Identity();
@@ 386,16 425,16 @@ Transform CameraTransform(Camera *camera)
perspective_transform.m[3][3] = 0;
perspective_transform.m[2][3] = (2*camera->far*camera->near) / (camera->far - camera->near);
perspective_transform.m[3][2] = -1;
- // PrintMat(camera_transform);
- // printf("persp\n");
- // PrintMat(perspective_transform);
AddTransform(perspective_transform, &camera_transform);
-// PrintMat(camera_transform);
return camera_transform;
}
+// TODO: consider using positive Z-axis & make a special case for the camera
Transform AlignToAxis(Model *model)
+/* Returns the transform to move vertices into the model's rotational frame of
+reference — the model will be facing the negative Z-axis, and up will be the
+positive Y-axis. Assumes the model is at the origin. */
{
Vector Rx, Ry, Rz;
Transform op = Identity();
@@ 416,7 455,9 @@ Transform AlignToAxis(Model *model)
return op;
}
+// TODO: test this, seems sus
Transform Orient(Model *model)
+/* Same as AlignToAxis, but positive Z-axis? I think? */
{
Vector Rx, Ry, Rz;
Transform op = Identity();
@@ 438,13 479,16 @@ Transform Orient(Model *model)
}
void ZoomIn(Camera *camera, double amt)
+/* Updates a Camera's params for a narrower field of view (zoom in) */
{
camera->fov *= 1/(amt/100000 + 1);
camera->x_cotan = 1/(camera->aspect * tan(camera->fov/2));
camera->y_cotan = 1/tan(camera->fov/2);
}
+// TODO: also make ZoomOut
Vertex *NewVertex(Vertex *pool)
+/* Creates an uninitialized vertex in a vertex pool */
{
assert(PoolTotal(pool) < MAX_RENDER_VERTICES);
Vertex *v = PoolNext(pool);
@@ 452,6 496,7 @@ Vertex *NewVertex(Vertex *pool)
}
void UpdateScene(Scene *scene, int dt)
+/* Game loop update for a scene—all logical updates for one frame in a scene */
{
int i;
@@ 466,7 511,9 @@ void UpdateScene(Scene *scene, int dt)
// }
}
+// TODO: rethink how rotation is handled
void UpdatePhysics(Physics physics, int dt)
+/* Applies Physics updates for a frame—follows Newton's laws */
{
physics.model->position = VecAdd(physics.model->position, VecMul(physics.velocity, dt));
physics.velocity = VecAdd(physics.velocity, VecMul(physics.acceleration, dt));
@@ 476,12 523,15 @@ void UpdatePhysics(Physics physics, int dt)
}
void UpdateParticle(Particle particle, int dt)
+/* Applies physics updates to a particle. Particles don't use the Physics
+component because they don't have rotation, etc */
{
VecAdd(particle.position, VecMul(particle.velocity, dt));
VecAdd(particle.velocity, VecMul(particle.acceleration, dt));
}
void RenderScene(Scene *scene, Renderer *renderer)
+/* Top-level function to render all SceneObjects and Particles in a scene */
{
for (int i = 0; i < PoolTotal(scene->renderable_pool); i++) {
Render(scene->renderable_pool[i], renderer);
@@ 492,6 542,7 @@ void RenderScene(Scene *scene, Renderer *renderer)
}
void DrainRenderPools(Renderer *renderer)
+/* Clears the temporary rendering pools in a Renderer */
{
DrainPool(renderer->vertex_pool);
DrainPool(renderer->polygon_pool);
@@ 499,6 550,7 @@ void DrainRenderPools(Renderer *renderer)
}
void Render(Renderable renderable, Renderer *renderer)
+/* Main rendering pipeline for a mesh. Beware. */
{
int i;
int *polygon;
@@ 508,15 560,14 @@ void Render(Renderable renderable, Renderer *renderer)
renderMesh.vertices = renderer->vertex_pool;
renderMesh.faces = renderer->polygon_pool;
+ // TODO: calculate camera transform once per-frame instead
+ /* construct the image transform */
Transform mesh_transform = ComposeTransform(
CameraTransform(renderer->camera),
ModelTransform(renderable.model)
);
- // PrintMat(CameraTransform(renderer->camera));
- // PrintMat(ModelTransform(renderable.model));
- // PrintMat(mesh_transform);
- /* model and project all vertices */
+ /* model and project all vertices into image space */
for (i = 0; i < PoolTotal(renderable.mesh->vertices); i++) {
Vertex *vertex = NewVertex(renderMesh.vertices);
*vertex = renderable.mesh->vertices[i];
@@ 527,24 578,13 @@ void Render(Renderable renderable, Renderer *renderer)
for (i = 0, polygon = renderable.mesh->faces;
i < PoolTotal(renderable.mesh->faces);
i += NumPolygonVertices(polygon)+1, NextPolygon(polygon)) {
- // printf("Original polygon:\n");
- // PrintPolygon(polygon, renderable.mesh->vertices);
- int *clipped = ClipPolygon(polygon, &renderMesh, renderer);
- if (NumPolygonVertices(clipped) > 0) {
- // printf("Polygon %d\n", i);
- // printf("Original\n");
- // PrintPolygon(polygon, renderable.mesh->vertices);
- // printf("Projected\n");
- // PrintPolygon(polygon, renderMesh.vertices);
- // printf("Clipped\n");
- // PrintPolygon(clipped, renderMesh.vertices);
- }
+ ClipPolygon(polygon, &renderMesh, renderer);
}
/* viewmap all vertices */
// TODO: skip clipped vertices
for (i = 0; i < PoolTotal(renderMesh.vertices); i++) {
- Homogenize(renderMesh.vertices[i]);
+ Homogenize(&renderMesh.vertices[i]);
CommitTransform(renderer->deviceTransform, &renderMesh.vertices[i]);
}
@@ 560,7 600,11 @@ void Render(Renderable renderable, Renderer *renderer)
}
Transform ModelTransform(Model *model)
+/* Returns the transform to move vertices from a model's coordinate space into
+world space
+*/
{
+ // TODO: figure out this rotation business
/* rotate to align up and direction */
// Transform t = Orient(model);
Transform t = Identity();
@@ 569,6 613,7 @@ Transform ModelTransform(Model *model)
}
int *NewPolygon(int *pool)
+/* Creates and initializes a 0-gon in a polygon pool */
{
assert(PoolTotal(pool) < MAX_RENDER_POLYGONS);
int *polygon = PoolNext(pool);
@@ 577,49 622,44 @@ int *NewPolygon(int *pool)
}
int *ClipPolygon(int *polygon, Mesh *mesh, Renderer *renderer)
+/* Clips a polygon against a Renderer's clip planes. For each plane, the polygon
+is copied (and clipped) back and forth between the renderer's two polygon pools.
+The temporary polygon is erased each time after it's used to keep memory use low
+*/
{
int *polygonA = NewPolygon(renderer->polygon_pool);
int *polygonB = NewPolygon(renderer->alt_polygon_pool);
- // ClipPolygonPlane(polygon, polygonA, mesh, renderer->clip_planes.w_pos, renderer->polygon_pool);
+ // TODO: test which order of clipping these planes is fastest. (probably clip y-neg first, since most of the moon's verts will be below the screen)
-// printf("Clipping this polygon:\n");
-// PrintPolygon(polygon, mesh->vertices);
+ // ClipPolygonPlane(polygon, polygonA, mesh, renderer->clip_planes.w_pos, renderer->polygon_pool);
-// printf("Clipping on x_neg\n");
ClipPolygonPlane(polygon, polygonB, mesh, renderer->clip_planes.x_neg, renderer->alt_polygon_pool);
- // ClearPolygon(polygonA, renderer->polygon_pool);
-// PrintPolygon(polygonB, mesh->vertices);
+ // ClearPolygon(polygonA, renderer->polygon_pool); // only for w_pos clipping
-// printf("Clipping on x_pos\n");
ClipPolygonPlane(polygonB, polygonA, mesh, renderer->clip_planes.x_pos, renderer->polygon_pool);
ClearPolygon(polygonB, renderer->alt_polygon_pool);
-// PrintPolygon(polygonA, mesh->vertices);
-// printf("Clipping on y_neg\n");
ClipPolygonPlane(polygonA, polygonB, mesh, renderer->clip_planes.y_neg, renderer->alt_polygon_pool);
ClearPolygon(polygonA, renderer->polygon_pool);
-// PrintPolygon(polygonB, mesh->vertices);
-// printf("Clipping on y_pos\n");
ClipPolygonPlane(polygonB, polygonA, mesh, renderer->clip_planes.y_pos, renderer->polygon_pool);
ClearPolygon(polygonB, renderer->alt_polygon_pool);
-// PrintPolygon(polygonA, mesh->vertices);
-// printf("Clipping on z_neg\n");
ClipPolygonPlane(polygonA, polygonB, mesh, renderer->clip_planes.z_neg, renderer->alt_polygon_pool);
ClearPolygon(polygonA, renderer->polygon_pool);
-// PrintPolygon(polygonB, mesh->vertices);
-// printf("Clipping on z_pos\n");
ClipPolygonPlane(polygonB, polygonA, mesh, renderer->clip_planes.z_pos, renderer->polygon_pool);
ClearPolygon(polygonB, renderer->alt_polygon_pool);
-// PrintPolygon(polygonA, mesh->vertices);
return polygonA;
}
void ClipPolygonPlane(int *srcPolygon, int *dstPolygon, Mesh *mesh, Plane clipBoundary, int *polygon_pool)
+/* Sutherland-Hodgeman clipping of a polygon against a plane.
+srcPolygon and dstPolygon both reference vertices in mesh->vertices. Vertices
+are added to the mesh as needed, and v-indices are added to dstPolygon as needed
+*/
{
Vertex *intersection, *current, *previous;
@@ 648,13 688,16 @@ void ClipPolygonPlane(int *srcPolygon, int *dstPolygon, Mesh *mesh, Plane clipBo
}
}
+// TODO: profile & optimize
bool VertexInside(Vertex *v, Plane clipBoundary)
+/* Tests whether a vertex is inside a plane. Planes have outward-facing normals */
{
Vector n = clipBoundary.normal;
return DotProd4D(n, *v) <= 0;
}
Vertex ClipIntersect(Vertex *s, Vertex *p, Plane clipBoundary)
+/* Calculates the intersectino of a line segment with a plane */
{
double d1 = DotProd4D(clipBoundary.normal, *s);
double d2 = DotProd4D(clipBoundary.normal, *p);
@@ 670,7 713,9 @@ Vertex ClipIntersect(Vertex *s, Vertex *p, Plane clipBoundary)
// // TODO
// }
+// TODO: find a more efficient way to wireframe a mesh without duplicating edges
void WireframePolygon(int *polygon, Vertex *vertices, Renderer *renderer)
+/* Rasterizes lines between each edge of a polygon */
{
Vertex *current, *previous;
previous = LastPolygonVertex(polygon, vertices);
@@ 755,6 800,7 @@ void RasterLineSteep(Vertex a, Vertex b, Color color, Renderer *renderer)
}
void WritePixel(int x, int y, Color color, Renderer *renderer)
+/* Writes a pixel into a renderer's frame buffer */
{
Pixel p = ColorPixel(color);
if (x >= 0 && x < renderer->width && y >= 0 && y < renderer->height) {
@@ 763,12 809,14 @@ void WritePixel(int x, int y, Color color, Renderer *renderer)
}
Color GetPixel(int x, int y, Renderer *renderer)
+/* Reads a pixel from a renderer's frame buffer */
{
Pixel p = renderer->pixels[(renderer->height - 1 - y)*renderer->width+x];
return PixelColor(p);
}
void WriteDepth(int x, int y, DepthVal value, Renderer *renderer)
+/* Writes a depth value into a renderer's depth buffer */
{
if (x >= 0 && x < renderer->width && y >= 0 && y < renderer->height) {
renderer->depthBuffer[(renderer->height - 1 - y)*renderer->width+x] = value;
@@ 776,6 824,8 @@ void WriteDepth(int x, int y, DepthVal value, Renderer *renderer)
}
bool UpdateDepthBuffer(int x, int y, double z, Renderer *renderer)
+/* Returns whether z is in front of a depth value at an x, y position. Updates
+the depth buffer with the z value if it is in front */
{
if (z > renderer->depthBuffer[y*renderer->width+x]) {
renderer->depthBuffer[y*renderer->width+x] = z;
@@ 791,6 841,7 @@ bool UpdateDepthBuffer(int x, int y, double z, Renderer *renderer)
// }
Model *NewModel(Model *pool)
+/* Creates and initializes a Model */
{
assert(PoolTotal(pool) < MAX_SCENE_OBJECTS);
Model *model = PoolNext(pool);
@@ 802,6 853,8 @@ Model *NewModel(Model *pool)
}
Mesh *NewMesh(void)
+/* Allocates and initializes a Mesh. A Mesh owns a private vertex pool and
+polygon pool to track its vertices and polygons */
{
Mesh *mesh = malloc(sizeof(Mesh));
mesh->vertices = NewPool(Vertex, MAX_MESH_VERTICES);
@@ 3,6 3,7 @@
#include <string.h>
#include <math.h>
#include <stdio.h>
+#include <assert.h>
Transform Identity(void)
{
@@ 50,10 51,10 @@ Transform *NewTransform(void)
return t;
}
-Transform FromTransform(Transform transform)
+Transform CloneTransform(Transform transform)
{
int i, j;
- Transform newTransform = Identity();
+ Transform newTransform;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
newTransform.m[i][j] = transform.m[i][j];
@@ 66,7 67,7 @@ void AddTransform(Transform A, Transform *subject)
/* combine two transforms that happen in subject, then A order */
{
int i, j, s;
- Transform B = FromTransform(*subject);
+ Transform B = CloneTransform(*subject);
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
@@ 118,7 119,6 @@ Vector ApplyTransform(Transform m, Vector v)
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;
@@ 132,6 132,7 @@ void CommitTransform(Transform t, Vector *v)
}
Transform RotationX(double angle)
+/* angle in degrees */
{
Transform t = Identity();
t.m[1][1] = cos(Rad(angle));
@@ 142,6 143,7 @@ Transform RotationX(double angle)
}
Transform *RotateX(Transform *t, double angle)
+/* angle in degrees */
{
Transform rotation = RotationX(angle);
AddTransform(rotation, t);
@@ 149,6 151,7 @@ Transform *RotateX(Transform *t, double angle)
}
Transform RotationY(double angle)
+/* angle in degrees */
{
Transform t = Identity();
t.m[0][0] = cos(Rad(angle));
@@ 159,6 162,7 @@ Transform RotationY(double angle)
}
Transform *RotateY(Transform *t, double angle)
+/* angle in degrees */
{
Transform rotation = RotationY(angle);
AddTransform(rotation, t);
@@ 166,6 170,7 @@ Transform *RotateY(Transform *t, double angle)
}
Transform RotationZ(double angle)
+/* angle in degrees */
{
Transform t = Identity();
t.m[0][0] = cos(Rad(angle));
@@ 176,6 181,7 @@ Transform RotationZ(double angle)
}
Transform *RotateZ(Transform *t, double angle)
+/* angle in degrees */
{
Transform rotation = RotationZ(angle);
AddTransform(rotation, t);
@@ 183,6 189,7 @@ Transform *RotateZ(Transform *t, double angle)
}
Transform Rotation(double rx, double ry, double rz)
+/* angle in degrees */
{
Transform t = RotationX(rx);
AddTransform(RotationY(ry), &t);
@@ 191,6 198,7 @@ Transform Rotation(double rx, double ry, double rz)
}
Transform *Rotate(Transform *t, double rx, double ry, double rz)
+/* angle in degrees */
{
Transform rotation = Rotation(rx, ry, rz);
AddTransform(rotation, t);
@@ 200,7 208,7 @@ Transform *Rotate(Transform *t, double rx, double ry, double rz)
Transform Translation(Vector dv)
{
Transform T = Identity();
- dv = Homogenize(dv);
+ assert(dv.w == 1);
T.m[0][3] = dv.x;
T.m[1][3] = dv.y;
T.m[2][3] = dv.z;
@@ 237,7 245,7 @@ double Determinant(Transform mat)
double result;
n = 4;
- Transform M = FromTransform(mat);
+ Transform M = CloneTransform(mat);
for (i = 0; i < n-1; i++) {
for (j = i + 1; j < n; j++)