add scene lighting constructs; real stdin support
authorCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 13 Feb 2012 09:40:37 +0000 (02:40 -0700)
committerCharles McGarvey <chazmcgarvey@brokenzipper.com>
Mon, 13 Feb 2012 09:46:12 +0000 (02:46 -0700)
15 files changed:
Makefile
README
animate.lua [new file with mode: 0755]
color.h
common.h
config.h [new file with mode: 0644]
light.h
list.h
main.c
raster.c
raster.h
rbtree.h
rotate.lua [deleted file]
scene.c
scene.h

index 024bb482ab202cc43adbc2ad0baff74d6fb4ac6d..7d119e4061dbf22bde738697f72d7b1a1f010e76 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,6 @@
 
-PROJECT = project2
-IUSE    = CLIPPING DEPTH_TEST SMOOTH_COLOR \
-                 EXTRA_INLINE NDEBUG RASTER_STATS RENDER_PROGRESS RENDER_TIMER
-
-
-# BACKFACE_CULLING  prevent drawing triangles that are not facing forward
-# CLIPPING          turn on clipping (you really always want this)
-# DEPTH_TEST        enable the z buffer for depth testing
-# EXPORT_BMP        save scene as bitmap (enabled by default)
-# EXPORT_PPM        save scene as a PPM file (also enabled by default)
-# EXTRA_INLINE      allow the compiler to inline even more aggressively
-# LIGHTING          turn the lights on (experimental)
-# NDEBUG            disable assertions and other nonessential checks
-# PRE_NORMALS[=n]   pre-compute mesh normals at scene-loading time;
-#                   set to 1 for per-face normals, 2 for averaged normals,
-#                   or 3 to also enable caching the computed normals
-# RASTER_STATS      print triangle count and optimization savings at the end
-# RENDER_PROGRESS   print progress while drawing the scene
-# RENDER_TIMER      add a timer to see how long the renderer takes
-# SMOOTH_COLOR      interpolate colors smoothly between triangle vertices
+PROJECT = rasterize
+IUSE    = DEPTH_TEST EXTRA_INLINE SMOOTH_COLOR
 
 VIEWER = feh
 
@@ -40,10 +22,10 @@ clean:
        rm -f $(PROJECT) $(OBJS) $(DEPS)
 
 distclean: clean
-       rm -f tags gmon.out scene.ppm scene.bmp
+       rm -f tags gmon.out
 
 dist:
-       git archive $(PROJECT) --prefix=$(PROJECT)/ --output=$(PROJECT).zip
+       git archive HEAD --prefix=$(PROJECT)/ --output=$(PROJECT).zip
 
 run: $(PROJECT)
        ./$< && $(VIEWER) scene.ppm
diff --git a/README b/README
index 01670d93d33fbe74c97a5236e95e5132227d547f..b5f29c8640b4648b764b326eae98823f01ee57b2 100644 (file)
--- a/README
+++ b/README
@@ -6,11 +6,11 @@ mcgarvey@eng.utah.edu
 Project 2 Notes
 
 To build, just `make' it.  You will need GNU make; it won't work well with any
-other kind of make(1).  Once built, the executable is called project2.  The
+other kind of make(1).  Once built, the executable is called rasterize.  The
 default options should meet the assignment specification.
 
 Known to run on:
  * Linux 3.2.1-gentoo
- * NetBSD 5.1
+ * NetBSD 5.1.2
  * Darwin 10.8.0 (in the Mac lab)
 
diff --git a/animate.lua b/animate.lua
new file mode 100755 (executable)
index 0000000..d04f5df
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env lua
+
+--
+-- CS5600 University of Utah
+-- Charles McGarvey
+-- mcgarvey@eng.utah.edu
+--
+
+-- This program that draws a scene multiple times from different camera angles
+-- and positions.  The rasters are saved in the `frames' directory, and if
+-- ffmpeg is installed, they will also be combined into a video file.  The
+-- camera will make a full rotation along the X and Z axes, around a specified
+-- point (center), all the while looking at some other point (look).
+-- This must be called from the same directory where the rasterize program is.
+
+local size      = {w = 640, h = 480}    -- width and height of the viewport
+local frames    = 512                   -- number of animation frames
+
+local look      = {x = 2, y = 1, z = 2} -- the point to look at
+local center    = {x = 0, y = 1, z = 0} -- center of rotation
+local distance  = 4                     -- the distance from the center
+local start     = math.pi               -- where is the first frame
+
+local jobs      = 6                     -- number of concurrent renderings
+
+-- the actual objects of the scene that will be drawn:
+local scene = [[
+L 0 1000000 0 1 1 1 1 1 1
+L 0 0 1000000 1 1 1 1 1 1
+g triangle.raw
+c 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1
+t 2 0 -2
+s 1 1 1
+g dragon.raw
+c 0.7 0.3 0.2 0.8 0.2 0.1 0.9 0.2 0.2
+t -2 -1 -2
+s 2 2 2
+g budda.raw
+c 0.1 0.2 0.7 0.1 0.3 0.9 0.2 0.1 0.8
+t 2 -1.5 2
+s 10 10 10
+g bunny.raw
+c 0.9 0.8 0.9 0.8 0.7 0.9 0.9 0.8 0.7
+t -2 -1 2
+s 10 10 10
+g teapot2.raw
+c 0 1 0 0 1 0 0 1 0
+t 0 -1 0
+s 0.6 0.6 0.6
+]]
+
+
+function vec_add(a, b)
+    return {x = a.x + b.x, y = a.y + b.y, z = a.z + b.z}
+end
+
+function vec_scale(v, s)
+    return {x = v.x * s, y = v.y * s, z = v.z * s}
+end
+
+local fmt   = string.format
+local write = function(...) io.write(fmt(...)) end
+
+function render(i, v)
+    while i and v do
+        local filename  = fmt("frames/anim%04d.bmp", i)
+        local command   = fmt("./rasterize -o %s >/dev/null", filename)
+        local out = io.popen(command, "w")
+        write("\27[80D\27[2Kframe\t %4d / %d", i + 1, frames)
+        out:write(fmt([[
+U3
+%d %d
+%f %f %f
+%f %f %f
+0 1 0
+1.57 %f 0.1 1000
+%s
+X
+]], size.w, size.h, v.x, v.y, v.z, look.x, look.y, look.z, size.w/size.h, scene))
+        i, v = coroutine.yield()
+        out:close()
+    end
+end
+
+
+print("Animating scene...")
+
+local threads = {}
+for i = 1,jobs do
+    table.insert(threads, coroutine.wrap(render))
+end
+
+os.execute("rm -rf frames && mkdir frames >/dev/null 2>&1")
+for i = 0,(frames-1) do
+    local t = start + 2 * math.pi * i / frames
+    local v = {
+        x = math.cos(t),
+        y = 0,
+        z = math.sin(t),
+    }
+    v = vec_add(vec_scale(v, distance), center)
+    threads[1 + (i % jobs)](i, v)
+end
+for _,thread in ipairs(threads) do thread(null) end
+print()
+
+if os.execute("ffmpeg -i frames/anim%04d.bmp -b 1024k -y -an scene.avi") == 0 then
+    print("Animation written to scene.avi.")
+end
+
diff --git a/color.h b/color.h
index 26a6390d2adf70c598652bc33183e5b15a421c23..2c5bf8040cc2ee0ff7cbd9ecf7665902a930fdb0 100644 (file)
--- a/color.h
+++ b/color.h
@@ -80,7 +80,7 @@ void color_print(color_t c)
 
 
 /*
- * Add two colors together.  Color may need to be clamped afterward.
+ * Add two colors together.
  */
 INLINE_MAYBE
 color_t color_add(color_t c1, color_t c2)
@@ -89,7 +89,7 @@ color_t color_add(color_t c1, color_t c2)
 }
 
 /*
- * Add three colors together.  Color may need to be clamped afterward.
+ * Add three colors together.
  */
 INLINE_MAYBE
 color_t color_add2(color_t c1, color_t c2, color_t c3)
@@ -97,6 +97,41 @@ color_t color_add2(color_t c1, color_t c2, color_t c3)
     return color_add(color_add(c1, c2), c3);
 }
 
+/*
+ * Multiply two colors together.
+ */
+INLINE_MAYBE
+color_t color_mult(color_t c1, color_t c2)
+{
+    c1.r *= c2.r;
+    c1.g *= c2.g;
+    c1.b *= c2.b;
+    c1.a *= c2.a;
+    return c1;
+}
+
+/*
+ * Scale a color by some scalar coefficient.
+ */
+INLINE_MAYBE
+color_t color_scale(color_t c, scal_t k)
+{
+    c.r *= k;
+    c.g *= k;
+    c.b *= k;
+    c.a *= k;
+    return c;
+}
+
+/*
+ * Scale a color by another color and some scalar coefficient.
+ */
+INLINE_MAYBE
+color_t color_scale2(color_t c1, color_t c2, scal_t k)
+{
+    return color_scale(color_mult(c1, c2), k);
+}
+
 /*
  * Clamp a color's channels to the normal range of 0.0 to 1.0.
  */
index dd6f034bc9171fa9dd67e5d73d52e0ef7550de13..172b8af5507fd700766cf071b606bf05c9aeb7d9 100644 (file)
--- a/common.h
+++ b/common.h
@@ -15,6 +15,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "config.h"
+
 
 /*
  * Define a keyword for use while defining small and fast functions.
@@ -132,11 +134,16 @@ int imax(int a, int b)
  * an error condition and an abort(3) occurs.
  * return.
  */
+#if VERBOSITY >= 1
 #define TRY_DO(L, K, ARGS...) \
 printf("* " L "... ", ##ARGS); \
 fflush(stdout); \
 if ((K) == 0) printf("done!\n"); \
 else abort()
+#else
+#define TRY_DO(L, K, ARGS...) \
+if ((K) != 0) abort()
+#endif
 
 
 /*
diff --git a/config.h b/config.h
new file mode 100644 (file)
index 0000000..9637dea
--- /dev/null
+++ b/config.h
@@ -0,0 +1,194 @@
+
+/*
+ * CS5600 University of Utah
+ * Charles McGarvey
+ * mcgarvey@eng.utah.edu
+ */
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/*
+ * BACKFACE_CULLING
+ * If enabled, triangles that are facing away from the viewer, according to
+ * the right-hand rule and counter-clockwise winding of the verticies, will
+ * not be drawn.  This option can improve performance in some situations, but
+ * it may also cause visual problems for models that aren't entirely closed.
+ */
+#if BACKFACE_CULLING
+#define IF_BACKFACE_CULLING(X) X
+#else
+#define IF_BACKFACE_CULLING(X)
+#endif
+
+/*
+ * CLIPPING
+ * If enabled, triangles will be not be drawn if they are entirely outside of
+ * the viewing volume.  The number of pixels tested while rasterizing a
+ * triangle can also be minimized, which leads to a huge performance boost.
+ * For that reason, this is on unless explicitly disabled, and you really
+ * always want to leave it enabled.  Note that this does not have anything to
+ * do with viewport clipping, which is always performed.  Also note that any
+ * triangle which is partially within the viewing volume will be entirely
+ * drawn, which may be somewhat unexpected.
+ */
+#ifndef CLIPPING
+#define CLIPPING 1
+#endif
+#if CLIPPING
+#define IF_CLIPPING(X) X
+#else
+#define IF_CLIPPING(X)
+#endif
+
+/*
+ * DEPTH_TEST
+ * If enabled, a z-buffer will be used to perform some sort of depth testing,
+ * resulting in the generally desirable situation where triangles that are
+ * further away will not appear in front of triangles that are closer.  There
+ * is a performance penalty for this, so it must be enabled.
+ */
+#if DEPTH_TEST
+#define IF_DEPTH_TEST(X) X
+#else
+#define IF_DEPTH_TEST(X)
+#endif
+
+/*
+ * EXPORT_BMP
+ * If enabled, each scene rasterization will be saved as a BMP image file.
+ * This is on unless explicitly disabled.
+ */
+#ifndef EXPORT_BMP
+#define EXPORT_BMP 1
+#endif
+#if EXPORT_BMP
+#define IF_EXPORT_BMP(X) X
+#else
+#define IF_EXPORT_BMP(X)
+#endif
+
+/*
+ * EXPORT_PPM
+ * If enabled, each scene rasterization will be saved as a PPM image file.
+ * This is on unless explicitly disabled.
+ */
+#ifndef EXPORT_PPM
+#define EXPORT_PPM 1
+#endif
+#if EXPORT_PPM
+#define IF_EXPORT_PPM(X) X
+#else
+#define IF_EXPORT_PPM(X)
+#endif
+
+/*
+ * EXTRA_INLINE
+ * If enabled, functions that are defined in interface files will be marked as
+ * inline.  The compiler will generally inline functions according to its own
+ * optimization heuristics, and this inline marking may persuade the compiler
+ * to inline a function that it otherwise would not.  This option may bring a
+ * small performance boost, but it can also increase the size of the program
+ * executable.
+ */
+#if EXTRA_INLINE
+#define IF_EXTRA_INLINE(X) X
+#else
+#define IF_EXTRA_INLINE(X)
+#endif
+
+/*
+ * LIGHTING
+ * If enabled, local lighting will be used to increase realism of the scene.
+ * This option has a performance cost, but it can produce interesting visuals.
+ * The behavior of this option also depends on the SMOOTH_COLOR option and
+ * whether or not the model has unique vertex normals; if SMOOTH_COLOR is
+ * disabled, each triangle will be shaded a flat color.  If it is enabled,
+ * Gouraud interpolation is used to smooth the lighting across the face of
+ * each triangle.  See the PRE_NORMALS and SMOOTH_COLOR options.
+ */
+#if LIGHTING
+#define IF_LIGHTING(X) X
+#else
+#define IF_LIGHTING(X)
+#endif
+
+/*
+ * NDEBUG
+ * If enabled, assertions and other nonessential checks will not be compiled
+ * into the programs.
+ */
+#if NDEBUG
+#define IF_NDEBUG(X) X
+#else
+#define IF_NDEBUG(X)
+#endif
+
+/*
+ * PRE_NORMALS
+ * If enabled, normals are pre-computed while the triangles are loading.
+ * Otherwise, the normals are computed during rasterization.  The behavior of
+ * this option is effected by its precise value:
+ *   1 Normals are computed per-face, according to the right-hand rule and
+ *     counter-clockwise winding of the verticies.
+ *   2 Normals are computed per-vertex; the normal for a vertex that is shared
+ *     between two or more faces will be the average of the normals of the
+ *     faces.  There is a performance penalty for this setting.
+ *   3 Same as 2, but the normals will be cached so that they will not need to
+ *     be computed the next time the mesh is loaded.
+ */
+#if PRE_NORMALS
+#define IF_PRE_NORMALS(X) X
+#else
+#define IF_PRE_NORMALS(X)
+#endif
+
+/*
+ * SMOOTH_COLOR
+ * If enabled, color will be interpolated across the face of a triangle.
+ * Otherwise, the color will flat, and the average color of the colors
+ * associated with each vertex will be used.
+ */
+#if SMOOTH_COLOR
+#define IF_SMOOTH_COLOR(X) X
+#else
+#define IF_SMOOTH_COLOR(X)
+#endif
+
+/*
+ * VERBOSITY
+ * If enabled, a description of what is happening will be printed to stdout.
+ * Otherwise, nothing is printed.  The behavior of this option is effected by
+ * its precise value:
+ *   1 Print just a few very general descriptions.
+ *   2 After rasterization, also print the triangle count and other
+ *     information that may be interesting.
+ *   3 Also print the number of seconds it took to render the entire scene,
+ *     according to wall time.
+ *   4 Also print the number of triangles as they are being rastered.  This
+ *     uses ANSI escape codes which may not be supported on all terminals.
+ *     It also causes a lot to be printed, so it can actually decrease render
+ *     performance, especially on a slow (or remote) terminal.
+ * The default setting for this option is 3.
+ */
+#ifndef VERBOSITY
+#define VERBOSITY 3
+#endif
+#if VERBOSITY >= 2
+#define IF_RASTER_STATS(X) X
+#else
+#define IF_RASTER_STATS(X)
+#endif
+#if VERBOSITY >= 3
+#define IF_RENDER_TIMER(X) X
+#else
+#define IF_RENDER_TIMER(X)
+#endif
+#if VERBOSITY >= 4
+#define IF_RENDER_PROGRESS(X) X
+#else
+#define IF_RENDER_PROGRESS(X)
+#endif
+
+#endif // _CONFIG_H_
+
diff --git a/light.h b/light.h
index 99c0d7c3abfa1aee87c0911066e4f142c0eb3302..2518927117d50d43590bb3d78df132e2aaff7a9e 100644 (file)
--- a/light.h
+++ b/light.h
  */
 struct light
 {
-    color_t color;
-    vec_t   position;
+    vec_t   v;  // position
+    color_t d;  // diffuse
+    color_t s;  // specular
 };
 typedef struct light light_t;
 
-
 /*
- * Initialize a light with a color and position.
+ * Initialize a light with a position and color.
  */
 INLINE_MAYBE
-void light_init(light_t* l, color_t color, vec_t position)
+void light_init(light_t* l, vec_t position, color_t diffuse, color_t specular)
 {
-    l->color = color;
-    l->position = position;
+    l->v = position;
+    l->d = diffuse;
+    l->s = specular;
 }
 
 
 /*
- * Create a new light with a color and position.
+ * Create a new light with a position and color.
  */
 INLINE_MAYBE
-light_t light_new(color_t color, vec_t position)
+light_t light_new(vec_t position, color_t diffuse, color_t specular)
 {
     light_t l;
-    light_init(&l, color, position);
+    light_init(&l, position, diffuse, specular);
     return l;
 }
 
 
+/*
+ * Create a new light on the heap.
+ */
+INLINE_MAYBE
+light_t* light_alloc(vec_t position, color_t diffuse, color_t specular)
+{
+    light_t* l = (light_t*)mem_alloc(sizeof(light_t));
+    light_init(l, position, diffuse, specular);
+    return l;
+}
+
+INLINE_MAYBE
+light_t* light_copy(light_t l)
+{
+    light_t* n = (light_t*)mem_alloc(sizeof(light_t));
+    memcpy(n, &l, sizeof(light_t));
+    return n;
+}
+
+
 #endif // _LIGHT_H_
 
diff --git a/list.h b/list.h
index 5adf22874fede7db45e32a99ad7d77a3ef078b1a..91f176d50325afb1153c829223697cbe19203a8b 100644 (file)
--- a/list.h
+++ b/list.h
@@ -94,10 +94,10 @@ void list_pop(list_t** l)
  * This is a O(n) operation.
  */
 INLINE_MAYBE
-void list_destroy(list_t* l)
+void list_destroy(list_t** l)
 {
-    while (l) {
-        list_pop(&l);
+    while (*l) {
+        list_pop(l);
     }
 }
 
diff --git a/main.c b/main.c
index 83878bf5e70e0dc29dddbfc93460d96312c20f3e..8f4ca4d4d7ab52636fcb4d3ed66ffe186a668b03 100644 (file)
--- a/main.c
+++ b/main.c
@@ -5,52 +5,60 @@
  * mcgarvey@eng.utah.edu
  */
 
-#include <stdio.h>
+#define _POSIX_C_SOURCE 2
+#include <errno.h>
+#include <unistd.h>
 
 #include "raster.h"
 #include "scene.h"
 
 
-#ifndef EXPORT_PPM
-#define EXPORT_PPM 1
-#endif
-#ifndef EXPORT_BMP
-#define EXPORT_BMP 1
-#endif
-
-
 /*
  * Load a scene from a file.
  */
 static int load(scene_t** scene, const char* filename)
 {
-    *scene = scene_alloc(filename);
+    FILE* file = fopen(filename, "r");
+    if (file == NULL) {
+        fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
+        return -1;
+    }
+
+    *scene = scene_alloc(file);
     if (*scene == NULL) {
-        return 1;
+        fclose(file);
+        return -1;
     }
+
+    fclose(file);
     return 0;
 }
 
 /*
- * Load a scene file, render it, and export it to PPM and BMP formats.
+ * Load a scene from standard input.
  */
-static void draw(const char* filename)
+static int load_from_stdin(scene_t** scene)
 {
-    char* u3d;
-    if (strcmp(filename, "-") == 0) {
-        u3d = mem_strdup("stdin");
-    }
-    else {
-        u3d = mem_strdup(filename);
+    *scene = scene_alloc(stdin);
+    if (*scene == NULL) {
+        return -1;
     }
+    return 0;
+}
 
+/*
+ * Load a scene file, render it, and export it.
+ */
+static void draw(const char* filename)
+{
     scene_t* scene;
-    TRY_DO("Loading %s", load(&scene, filename), u3d);
+    TRY_DO("Loading %s", load(&scene, filename), filename);
 
     raster_t* raster = scene_render(scene);
     scene_destroy(scene);
     raster_printstats(raster);
 
+    char* u3d = mem_strdup(filename);
     strcut(u3d, '.');
 #if EXPORT_PPM
     char* ppm = mem_strcat(u3d, ".ppm");
@@ -67,16 +75,43 @@ static void draw(const char* filename)
     raster_destroy(raster);
 }
 
+/*
+ * Render a scene that is read from standard input, and export to BMP.
+ */
+static void draw_from_stdin(const char* filename)
+{
+    scene_t* scene;
+    TRY_DO("Loading from stdin", load_from_stdin(&scene));
+
+    raster_t* raster = scene_render(scene);
+    scene_destroy(scene);
+    raster_printstats(raster);
+
+    TRY_DO("Exporting to %s", raster_export_bmp(raster, filename), filename);
+
+    raster_destroy(raster);
+}
+
 /*
  * Render one or more scenes from 3D scene files.
  */
 int main(int argc, char* argv[])
 {
-    if (argc <= 1) {
+    int out = 0;
+    int c;
+    while ((c = getopt(argc, argv, "o:")) != -1) {
+        switch (c) {
+        case 'o':
+            ++out;
+            draw_from_stdin(optarg);
+        }
+    }
+
+    if (out == 0 && argc <= 1) {
         draw("scene.u3d");
     }
     else {
-        for (int i = 1; i < argc; ++i) {
+        for (int i = optind; i < argc; ++i) {
             draw(argv[i]);
         }
     }
index 59d3f462c00b7580f515037580a8af714d54c07c..aca2c5a860315c0cc70f44c8cab3069fb1ce0441 100644 (file)
--- a/raster.c
+++ b/raster.c
@@ -30,14 +30,13 @@ struct raster
     list_t*     lights;
     color_t     ambient;
     vec_t       eye;
+    color_t     specular;
+    scal_t      shininess;
 #endif
-#if RASTER_STATS
+#if VERBOSITY >= 2
     unsigned    total;
     unsigned    clipped;
     unsigned    culled;
-#define IF_RASTER_STATS(X) X
-#else
-#define IF_RASTER_STATS(X)
 #endif
 };
 
@@ -57,8 +56,10 @@ raster_t* raster_alloc(int width, int height, color_t fill)
     p->dirty = false;
 
 #if LIGHTING
-    p->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0));
+    p->ambient = color_new(S(0.2), S(0.2), S(0.2), S(1.0));
     p->lights = NULL;
+    p->specular = COLOR_WHITE;
+    p->shininess = S(1.0);
 #endif
  /*= light_new(COLOR_WHITE, vec_new(S(-2.0), S(4.0), S(0.0)));*/
     p->zbuf = (scal_t*)mem_alloc(sizeof(scal_t) * size);
@@ -73,13 +74,16 @@ void raster_destroy(raster_t* p)
 {
     mem_free(p->pixels);
     mem_free(p->zbuf);
+#if LIGHTING
+    list_destroy(&p->lights);
+#endif
     mem_free(p);
 }
 
 
 void raster_printstats(raster_t* p)
 {
-#if RASTER_STATS
+#if VERBOSITY >= 2
     unsigned drawn = p->total - p->clipped - p->culled;
     float percent = 100.0f * (float)drawn / (float)p->total;
     printf("culled\t%u\n"
@@ -96,7 +100,7 @@ void raster_clear(raster_t* p, color_t fill)
     for (int i = 0; i < size; ++i) {
         p->pixels[i] = fill;
     }
-#if RASTER_STATS
+#if VERBOSITY >= 2
     p->total = 0;
     p->clipped = 0;
     p->culled = 0;
@@ -139,15 +143,29 @@ void raster_eye(raster_t* p, vec_t eye)
 #endif
 }
 
+void raster_ambient(raster_t* p, color_t ambient)
+{
+#if LIGHTING
+    p->ambient = ambient;
+#endif
+}
+
 void raster_light(raster_t* p, light_t light)
 {
 #if LIGHTING
-    light_t* l = (light_t*)mem_alloc(sizeof(light_t));
-    memcpy(l, &light, sizeof(light_t));
+    light_t* l = light_copy(light);
     list_push2(&p->lights, l, mem_free);
 #endif
 }
 
+void raster_material(raster_t* p, color_t specular, scal_t shininess)
+{
+#if LIGHTING
+    p->specular = specular;
+    p->shininess = shininess;
+#endif
+}
+
 
 #define _CHECK_WRITE(X) if ((X) <= 0) goto fail
 
@@ -293,7 +311,7 @@ color_t _get_vertex_color(raster_t* p, vert_t vert)
     for (list_t* i = p->lights; i; i = i->link) {
         light_t light = *(light_t*)i->val;
         vec_t   mpos = vert.v;
-        vec_t   lpos = light.position;
+        vec_t   lpos = light.v;
         vec_t   vpos = p->eye;
         vec_t   n = vert.n;
         vec_t   l = vec_normalize(vec_sub(lpos, mpos));
@@ -301,23 +319,13 @@ color_t _get_vertex_color(raster_t* p, vert_t vert)
         vec_t   v = vec_normalize(vec_sub(vpos, mpos));
 
         scal_t  kd = scal_max(vec_dot(l, n), S(0.0));
-        color_t Id = color_new(
-            light.color.r * vert.c.r * kd,
-            light.color.g * vert.c.g * kd,
-            light.color.b * vert.c.b * kd,
-            S(1.0)
-        );
-        scal_t  ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), S(64.0));
-        color_t Is = color_new(
-            light.color.r * COLOR_WHITE.r * ks,
-            light.color.g * COLOR_WHITE.g * ks,
-            light.color.b * COLOR_WHITE.b * ks,
-            S(1.0)
-        );
+        color_t Id = color_scale2(light.d, vert.c, kd);
+        scal_t  ks = scal_pow(scal_max(vec_dot(r, v), S(0.0)), p->shininess);
+        color_t Is = color_scale2(light.s, p->specular, ks);
 
         color = color_add2(color, Id, Is);
     }
-    color_t Ia = p->ambient;
+    color_t Ia = color_mult(p->ambient, vert.c);
     return color_clamp(color_add(color, Ia));
 #else
     return vert.c;
index ef358e71855b96d05eaef150f6a526e0d12dac12..1d43132c6ce7fe40a62b5c8566d589ba195656ea 100644 (file)
--- a/raster.h
+++ b/raster.h
@@ -69,11 +69,21 @@ void raster_projection(raster_t* p, const mat_t* transform);
  */
 void raster_eye(raster_t* p, vec_t eye);
 
+/*
+ * Set the ambient light for the scene.
+ */
+void raster_ambient(raster_t* p, color_t ambient);
+
 /*
  * Add a light to the scene.
  */
 void raster_light(raster_t* p, light_t light);
 
+/*
+ * Set the material properties for the scene.
+ */
+void raster_material(raster_t* p, color_t specular, scal_t shininess);
+
 
 /*
  * Save the raster to a PPM file.
index 4f7263282e980390f4d3d547ccb0c659b99b053b..e23cd13712e97c5ebaf1286a89a5f0793658b893 100644 (file)
--- a/rbtree.h
+++ b/rbtree.h
@@ -92,7 +92,7 @@ bool rbtree_empty(rbtree_t* t)
 }
 
 /*
- * Clear the tree of all of it's nodes.
+ * Clear the tree of all of its nodes.
  */
 void rbtree_clear(rbtree_t* t);
 
diff --git a/rotate.lua b/rotate.lua
deleted file mode 100755 (executable)
index 4334dd8..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env lua
-
---
--- CS5600 University of Utah
--- Charles McGarvey
--- mcgarvey@eng.utah.edu
---
-
--- Render a scene multiple times from different angles, like a camera
--- rotating around the object of a scene.
-
-local size = {w = 640, h = 480}
-local slices = 512
-
-local center = {x = 0, y = 1, z = 0}
-local look = {x = -2, y = -1, z = -2}
-local distance = 4
-local start = math.pi
-
-local scene = [[
-g teapot2.raw
-c 1.0 1.0 0.0 1.0 1.0 0.0 1.0 1.0 0.0
-t 0.0 0.0 0.0
-s 0.6 0.6 0.6
-g triangle.raw
-c 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1
-t 2.0 0.0 -2.0
-s 1.0 1.0 1.0
-g dragon.raw
-c 0.7 0.3 0.2 0.8 0.2 0.1 0.9 0.2 0.2
-t -2.0 -1.0 -2.0
-s 2.0 2.0 2.0
-g budda.raw
-c 0.1 0.2 0.7 0.1 0.3 0.9 0.2 0.1 0.8
-t 2.0 -1.5 2.0
-s 10.0 10.0 10.0
-g bunny.raw
-c 0.9 0.8 0.9 0.8 0.7 0.9 0.9 0.8 0.7
-t -2.0 -1.0 2.0
-s 10.0 10.0 10.0
-]]
-
-function vec_add(a, b)
-    return {x = a.x + b.x, y = a.y + b.y, z = a.z + b.z}
-end
-
-function vec_scale(v, s)
-    return {x = v.x * s, y = v.y * s, z = v.z * s}
-end
-
-function render(i, v)
-    while true do
-        local out = io.popen("./project2 -", "w")
-        out:write(string.format([[
-U3
-%d %d
-%f %f %f
-%f %f %f
-0.0 1.0 0.0
-1.57 %f 0.1 1000.0
-%s
-]], size.w, size.h, v.x, v.y, v.z, look.x, look.y, look.z, size.w/size.h, scene))
-        local a, b = coroutine.yield()
-        out:close()
-        os.rename("stdin.bmp", string.format("anim%04d.bmp", i))
-        if a == null then
-            break
-        else
-            i = a
-            v = b
-        end
-    end
-end
-
-local threads = {}
-local jobs = 3
-for i = 1,jobs do
-    table.insert(threads, coroutine.wrap(render))
-end
-
-for i = 0,(slices-1) do
-    local t = start + 2 * i * math.pi / slices
-    local v = {
-        x = math.cos(t),
-        y = 0,
-        z = math.sin(t),
-    }
-    v = vec_add(vec_scale(v, distance), center)
-    threads[(i % jobs) + 1](i, v)
-end
-
-for k,v in ipairs(threads) do
-    v(null)     -- clear up any leftover work
-end
-
diff --git a/scene.c b/scene.c
index 3e0010fa399cb4c016a365e88cfbd700785c845d..be185113988fc11ecc7e840763d22f9aa46c14f8 100644 (file)
--- a/scene.c
+++ b/scene.c
 #include "tri.h"
 
 
-#if RENDER_PROGRESS
-#define IF_RENDER_PROGRESS(X) X
-#else
-#define IF_RENDER_PROGRESS(X)
-#endif
-
 #if PRE_NORMALS >= 2
 
 #include "map.h"
@@ -91,6 +85,8 @@ struct _group
 {
     list_t*     triangles;
     mat_t       model;
+    color_t     specular;
+    scal_t      shininess;
     char*       name;
     int         count;
 };
@@ -107,7 +103,7 @@ static int _group_try_read_cache(const char* filename, list_t** l)
     char*   cachename  = mem_strcat(".", filename);
     FILE*   file = fopen(cachename, "rb");
     if (file == NULL) {
-        return 0;
+        goto fail;
     }
 
     int count = 0;
@@ -129,6 +125,7 @@ static int _group_try_read_cache(const char* filename, list_t** l)
             vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
             vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
         );
+        list_push2(l, t, mem_free);
         _CHECK_IO(fread(&x1, sizeof(float), 1, file));
         _CHECK_IO(fread(&y1, sizeof(float), 1, file));
         _CHECK_IO(fread(&z1, sizeof(float), 1, file));
@@ -141,13 +138,13 @@ static int _group_try_read_cache(const char* filename, list_t** l)
         t->a.n = vec_new((scal_t)x1, (scal_t)y1, (scal_t)z1);
         t->b.n = vec_new((scal_t)x2, (scal_t)y2, (scal_t)z2);
         t->c.n = vec_new((scal_t)x3, (scal_t)y3, (scal_t)z3);
-
-        list_push2(l, t, mem_free);
     }
 
 fail:
-    fclose(file);
     mem_free(cachename);
+    if (file != NULL) {
+        fclose(file);
+    }
 
     return count;
 }
@@ -161,7 +158,7 @@ static void _group_write_cache(const char* filename, int count, list_t* l)
     FILE*   file = fopen(cachename, "wb");
     if (file == NULL) {
         fprintf(stderr, "Cannot write %s: %s\n", cachename, strerror(errno));
-        return;
+        goto fail;
     }
 
     _CHECK_IO(fwrite(&count, sizeof(count), 1, file));
@@ -189,11 +186,24 @@ static void _group_write_cache(const char* filename, int count, list_t* l)
 
 fail:
     mem_free(cachename);
+    if (file != NULL) {
+        fclose(file);
+    }
 }
 
 #undef _CHECK_IO
 
 
+/*
+ * Destroy a group.
+ */
+static void _group_destroy(_group_t* g)
+{
+    mem_free(g->name);
+    list_destroy(&g->triangles);
+    mem_free(g);
+}
+
 /*
  * Allocate a group by reading raw triangle coordinates from a file.
  */
@@ -204,6 +214,8 @@ static _group_t* _group_alloc(const char* filename)
     g->model = MAT_IDENTITY;
     g->name = mem_strdup(filename);
     g->count = 0;
+    g->specular = COLOR_WHITE;
+    g->shininess = S(64.0);
 
 #if PRE_NORMALS == 3
     int r = _group_try_read_cache(filename, &g->triangles);
@@ -216,6 +228,7 @@ static _group_t* _group_alloc(const char* filename)
     FILE* file = fopen(filename, "r");
     if (file == NULL) {
         fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
+        _group_destroy(g);
         return NULL;
     }
 
@@ -256,24 +269,14 @@ static _group_t* _group_alloc(const char* filename)
     fclose(file);
 
     if (g->triangles == NULL) {
-        fprintf(stderr, "No triangles coordinates read from %s\n", filename);
-        mem_free(g);
+        fprintf(stderr, "No triangles read from %s\n", filename);
+        _group_destroy(g);
         return NULL;
     }
 
     return g;
 }
 
-/*
- * Destroy a group.
- */
-static void _group_destroy(_group_t* g)
-{
-    mem_free(g->name);
-    list_destroy(g->triangles);
-    mem_free(g);
-}
-
 
 /*
  * Set the colors of the triangles in the group as defined in a file.
@@ -283,7 +286,7 @@ static int _group_set_colors(_group_t* g, FILE* file)
     double r1, g1, b1, r2, g2, b2, r3, g3, b3;
     if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
                &r1, &g1, &b1, &r2, &g2, &b2, &r3, &g3, &b3) != 9) {
-        fprintf(stderr, "Cannot read color values from scene file.\n");
+        fprintf(stderr, "Cannot read color values from scene.\n");
         return -1;
     }
 
@@ -303,7 +306,7 @@ static int _group_add_translate(_group_t* g, FILE* file)
 {
     double tx, ty, tz;
     if (fscanf(file, " %lf %lf %lf", &tx, &ty, &tz) != 3) {
-        fprintf(stderr, "Cannot read translate coordinates from scene file.\n");
+        fprintf(stderr, "Cannot read translate coordinates from scene.\n");
         return -1;
     }
     g->model = mat_mult(g->model, MAT_TRANSLATE((scal_t)tx, (scal_t)ty, (scal_t)tz));
@@ -317,7 +320,7 @@ static int _group_add_rotate(_group_t* g, FILE* file)
 {
     double theta, ax, ay, az;
     if (fscanf(file, " %lf %lf %lf %lf", &theta, &ax, &ay, &az) != 4) {
-        fprintf(stderr, "Cannot read rotation angle from scene file.\n");
+        fprintf(stderr, "Cannot read rotation angle from scene.\n");
         return -1;
     }
     g->model = mat_mult(g->model, MAT_ROTATE((scal_t)theta, (scal_t)ax, (scal_t)ay, (scal_t)az));
@@ -331,38 +334,75 @@ static int _group_add_scale(_group_t* g, FILE* file)
 {
     double sx, sy, sz;
     if (fscanf(file, " %lf %lf %lf", &sx, &sy, &sz) != 3) {
-        fprintf(stderr, "Cannot read scale factors from scene file.\n");
+        fprintf(stderr, "Cannot read scale factors from scene.\n");
         return -1;
     }
     g->model = mat_mult(g->model, MAT_SCALE((scal_t)sx, (scal_t)sy, (scal_t)sz));
     return 0;
 }
 
+/*
+ * Set the specular highlight and shininess factor for the group.
+ */
+static int _group_set_material(_group_t* g, FILE* file)
+{
+    double sr, sg, sb, shininess;
+    if (fscanf(file, " %lf %lf %lf %lf", &sr, &sg, &sb, &shininess) != 4) {
+        fprintf(stderr, "Cannot read material information from scene.\n");
+        return -1;
+    }
+    g->specular = color_new((scal_t)sr, (scal_t)sg, (scal_t)sb, S(1.0));
+    g->shininess = (scal_t)shininess;
+    return 0;
+}
+
 
 struct scene
 {
     list_t* groups;
-    list_t* lights;
     int     w, h;
     mat_t   view;
     mat_t   projection;
     vec_t   eye;
+    list_t* lights;
+    color_t ambient;
 };
 
-scene_t* scene_alloc(const char* filename)
+
+/*
+ * Set the ambient light properties of a scene.
+ */
+static int _scene_set_ambient(scene_t* s, FILE* file)
 {
-    FILE* file;
-    if (strcmp(filename, "-") == 0) {
-        file = stdin;
-    }
-    else {
-        file = fopen(filename, "r");
+    double r, g, b;
+    if (fscanf(file, " %lf %lf %lf", &r, &g, &b) != 3) {
+        fprintf(stderr, "Cannot read ambient light from scene.\n");
+        return -1;
     }
-    if (file == NULL) {
-        fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
-        return NULL;
+    s->ambient = color_new((scal_t)r, (scal_t)g, (scal_t)b, S(1.0));
+    return 0;
+}
+
+static int _scene_add_light(scene_t* s, FILE* file)
+{
+    double lx, ly, lz, dr, dg, db, sr, sg, sb;
+    if (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
+               &lx, &ly, &lz, &dr, &dg, &db, &sr, &sg, &sb) != 9) {
+        fprintf(stderr, "Cannot read light values from scene.\n");
+        return -1;
     }
+    light_t* l = light_alloc(
+            vec_new(lx, ly, lz),
+            color_new(dr, dg, db, S(1.0)),
+            color_new(sr, sg, sb, S(1.0))
+    );
+    list_push2(&s->lights, l, mem_free);
+    return 0;
+}
 
+
+scene_t* scene_alloc(FILE* file)
+{
     int w, h;
     double eyeX, eyeY, eyeZ, spotX, spotY, spotZ, upX, upY, upZ;
     double fovy, aspect, near, far;
@@ -370,7 +410,7 @@ scene_t* scene_alloc(const char* filename)
                &w, &h,
                &eyeX, &eyeY, &eyeZ, &spotX, &spotY, &spotZ, &upX, &upY, &upZ,
                &fovy, &aspect, &near, &far) != 15) {
-        fprintf(stderr, "Cannot read scene file header.\n");
+        fprintf(stderr, "Cannot read scene header.\n");
         return NULL;
     }
 
@@ -383,6 +423,8 @@ scene_t* scene_alloc(const char* filename)
                          vec_new(  (scal_t)upX,   (scal_t)upY,   (scal_t)upZ));
     s->eye = vec_new(eyeX, eyeY, eyeZ);
     s->projection = MAT_PERSPECTIVE((scal_t)fovy, (scal_t)aspect, (scal_t)near, (scal_t)far);
+    s->lights = NULL;
+    s->ambient = color_new(S(0.05), S(0.05), S(0.05), S(1.0));
 
     char grp_filename[4096];
     _group_t* g = NULL;
@@ -435,14 +477,35 @@ if (g == NULL) { \
                 }
                 break;
 
+            case 'A':
+                if (_scene_set_ambient(s, file) != 0) {
+                    goto fail;
+                }
+                break;
+
+            case 'L':
+                if (_scene_add_light(s, file) != 0) {
+                    goto fail;
+                }
+                break;
+
+            case 'M':
+                _ASSERT_G;
+                if (_group_set_material(g, file) != 0) {
+                    goto fail;
+                }
+                break;
+
+            case 'X':
+                goto done;
+
             default:
                 fprintf(stderr, "Unknown identifier: %c\n", type);
         }
     }
-
 #undef _ASSERT_G
 
-    fclose(file);
+done:
     return s;
 
 fail:
@@ -452,14 +515,15 @@ fail:
 
 void scene_destroy(scene_t* s)
 {
-    list_destroy(s->groups);
+    list_destroy(&s->groups);
+    list_destroy(&s->lights);
     mem_free(s);
 }
 
 
 raster_t* scene_render(scene_t* s)
 {
-#if RENDER_TIMER
+#if VERBOSITY >= 3
     timer_start();
 #endif
 
@@ -467,11 +531,13 @@ raster_t* scene_render(scene_t* s)
     raster_view(p, &s->view);
     raster_projection(p, &s->projection);
     raster_eye(p, s->eye);
+    raster_ambient(p, s->ambient);
 
-    raster_light(p, light_new(COLOR_WHITE, vec_new(S(5.0), S(3.0), S(6.0))));
-    raster_light(p, light_new(COLOR_MAGENTA, vec_new(S(-2.0), S(2.0), S(-2.0))));
+    for (list_t* i = s->lights; i; i = i->link) {
+        raster_light(p, *(light_t*)i->val);
+    }
 
-#if RENDER_PROGRESS
+#if VERBOSITY >= 4
 #define PROGRESS_FMT "\033[80D\033[2K  %s\t %9d / %d"
     int tri;
     printf("render scene:\n");
@@ -480,9 +546,10 @@ raster_t* scene_render(scene_t* s)
     for (list_t* gi = s->groups; gi; gi = gi->link) {
         _group_t* g = (_group_t*)gi->val;
         raster_model(p, &g->model);
+        raster_material(p, g->specular, g->shininess);
         IF_RENDER_PROGRESS(tri = 0);
         for (list_t* ti = g->triangles; ti; ti = ti->link) {
-#if RENDER_PROGRESS
+#if VERBOSITY >= 4
             if (++tri % 100 == 0) {
                 printf(PROGRESS_FMT, g->name, tri, g->count);
                 fflush(stdout);
@@ -490,13 +557,13 @@ raster_t* scene_render(scene_t* s)
 #endif
             raster_draw_tri(p, (tri_t*)ti->val);
         }
-#if RENDER_PROGRESS
+#if VERBOSITY >= 4
         printf(PROGRESS_FMT"\n", g->name, tri, g->count);
 #endif
     }
     IF_RENDER_PROGRESS(printf("render complete!\n"));
 
-#if RENDER_TIMER
+#if VERBOSITY >= 3
     long dt = timer_stop();
     printf("time\t%.3fms\n", (float)dt / 1000.0f);
 #endif
diff --git a/scene.h b/scene.h
index 93f03e222118b824fae3105d788251e562611bd2..d198614a3b4ee5385bb3a4166f9aa51d54539f3f 100644 (file)
--- a/scene.h
+++ b/scene.h
@@ -19,7 +19,7 @@ typedef struct scene scene_t;
 /*
  * Allocate a scene by reading in data from a file.
  */
-scene_t* scene_alloc(const char* filename);
+scene_t* scene_alloc(FILE* file);
 
 /*
  * Destroy a scene.
This page took 0.075728 seconds and 4 git commands to generate.