mat_t modelviewprojection;
mat_t viewport;
bool dirty;
+ const model_t* current;
#if LIGHTING
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
};
raster_viewport(p, 0, 0, width, height);
p->model = p->view = p->projection = MAT_IDENTITY;
p->dirty = false;
+ p->current = NULL;
#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);
for (size_t i = 0; i < size; ++i) {
p->zbuf[i] = S(1.0);
{
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"
}
+color_t raster_color(const raster_t* p, vec_t pt)
+{
+ int u = (int)((scal_t)p->w * pt.x);
+ int v = (int)((scal_t)p->h * pt.y);
+ return p->pixels[p->w * (p->h - v - 1) + u];
+}
+
+int raster_width(const raster_t* p)
+{
+ return p->w;
+}
+
+int raster_height(const raster_t* p)
+{
+ return p->h;
+}
+
+void* raster_data(const raster_t* p)
+{
+ size_t size = p->w * p->h;
+ rgba_t* data = mem_alloc(size * sizeof(rgba_t));
+
+ for (int i = 0; i < size; ++i) {
+ data[i] = rgba_from_color(p->pixels[i]);
+ }
+ return data;
+}
+
+
void raster_clear(raster_t* p, color_t fill)
{
size_t size = p->w * p->h;
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;
#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
+#define _DO_OR_DIE(X) if ((X) <= 0) goto fail
int raster_export_ppm(const raster_t* p, const char* filename)
{
return -1;
}
- _CHECK_WRITE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h));
+ _DO_OR_DIE(fprintf(file, "P3\n%u %u\n255\n", p->w, p->h));
for (int y = (int)p->h - 1; y >= 0; --y) {
for (int x = 0; x < p->w; ++x) {
rgbachan_t r, g, b;
color_split(p->pixels[y * p->w + x], &r, &g, &b, NULL);
- _CHECK_WRITE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b));
+ _DO_OR_DIE(fprintf(file, "%hhu %hhu %hhu\n", r, g, b));
}
}
/*
* Check the return values to avoid loud warnings.
*/
- _CHECK_WRITE(fwrite(&magicNumber, sizeof(magicNumber), 1, file));
- _CHECK_WRITE(fwrite(&fileSize, sizeof(fileSize), 1, file));
- _CHECK_WRITE(fwrite(&reserved0, sizeof(reserved0), 1, file));
- _CHECK_WRITE(fwrite(&reserved1, sizeof(reserved1), 1, file));
- _CHECK_WRITE(fwrite(&dataOffset, sizeof(dataOffset), 1, file));
- _CHECK_WRITE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
- _CHECK_WRITE(fwrite(&width, sizeof(width), 1, file));
- _CHECK_WRITE(fwrite(&height, sizeof(height), 1, file));
- _CHECK_WRITE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file));
- _CHECK_WRITE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
- _CHECK_WRITE(fwrite(&compression, sizeof(compression), 1, file));
- _CHECK_WRITE(fwrite(&dataSize, sizeof(dataSize), 1, file));
- _CHECK_WRITE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file));
- _CHECK_WRITE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file));
- _CHECK_WRITE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file));
- _CHECK_WRITE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
+ _DO_OR_DIE(fwrite(&magicNumber, sizeof(magicNumber), 1, file));
+ _DO_OR_DIE(fwrite(&fileSize, sizeof(fileSize), 1, file));
+ _DO_OR_DIE(fwrite(&reserved0, sizeof(reserved0), 1, file));
+ _DO_OR_DIE(fwrite(&reserved1, sizeof(reserved1), 1, file));
+ _DO_OR_DIE(fwrite(&dataOffset, sizeof(dataOffset), 1, file));
+ _DO_OR_DIE(fwrite(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
+ _DO_OR_DIE(fwrite(&width, sizeof(width), 1, file));
+ _DO_OR_DIE(fwrite(&height, sizeof(height), 1, file));
+ _DO_OR_DIE(fwrite(&colorPlanes, sizeof(colorPlanes), 1, file));
+ _DO_OR_DIE(fwrite(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
+ _DO_OR_DIE(fwrite(&compression, sizeof(compression), 1, file));
+ _DO_OR_DIE(fwrite(&dataSize, sizeof(dataSize), 1, file));
+ _DO_OR_DIE(fwrite(&horizontalResolution, sizeof(horizontalResolution), 1, file));
+ _DO_OR_DIE(fwrite(&verticalResolution, sizeof(verticalResolution), 1, file));
+ _DO_OR_DIE(fwrite(&paletteColorCount, sizeof(paletteColorCount), 1, file));
+ _DO_OR_DIE(fwrite(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
size_t size = width * height;
for (int i = 0; i < size; ++i)
{
- rgbachan_t a, r, g, b;
- color_split(p->pixels[i], &r, &g, &b, &a);
- uint32_t argb = PACK(argb, 3, a);
- argb = PACK(argb, 2, r);
- argb = PACK(argb, 1, g);
- argb = PACK(argb, 0, b);
- _CHECK_WRITE(fwrite(&argb, sizeof(argb), 1, file));
+ struct {
+ rgbachan_t b, g, r, a;
+ } argb;
+ color_split(p->pixels[i], &argb.r, &argb.g, &argb.b, &argb.a);
+ _DO_OR_DIE(fwrite(&argb, sizeof(argb), 1, file));
}
fclose(file);
return 0;
}
-#undef _CHECK_WRITE
+
+raster_t* raster_import(const char* filename)
+{
+ int type = 0;
+ char* ext = strrchr(filename, '.');
+ if (ext == NULL) {
+ goto fail;
+ }
+ ++ext;
+
+ if (strcmp(ext, "bmp") == 0) {
+ return raster_import_bmp(filename);
+ }
+ if (strcmp(ext, "ppm") == 0) {
+ return raster_import_ppm(filename);
+ }
+
+fail:
+ fprintf(stderr, "Unknown file type: %s", filename);
+ return NULL;
+}
+
+raster_t* raster_import_ppm(const char* filename)
+{
+ FILE* file = fopen(filename, "r");
+ if (file == NULL) {
+ fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ int w, h;
+ if (fscanf(file, "P3 %d %d 255 ", &w, &h) != 2) {
+ fprintf(stderr, "Cannot read header from %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ raster_t* p = raster_alloc(w, h, COLOR_WHITE);
+
+ for (int y = h - 1; y >= 0; --y) {
+ for (int x = 0; x < w; ++x) {
+ uint16_t r, g, b;
+ /* mingw32 does not like %hhu conversion type */
+ if (fscanf(file, "%hu %hu %hu ", &r, &g, &b) != 3) {
+ fprintf(stderr, "Failed reading color values from %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+ union {
+ rgba_t rgba;
+ struct {
+ rgbachan_t r, g, b, a;
+ } chan;
+ } u;
+ u.chan.r = (rgbachan_t)r;
+ u.chan.g = (rgbachan_t)g;
+ u.chan.b = (rgbachan_t)b;
+ u.chan.a = 255;
+ p->pixels[y * w + x] = color_from_rgba(u.rgba);
+ }
+ }
+
+ fclose(file);
+ return p;
+
+fail:
+ raster_destroy(p);
+ fclose(file);
+ fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno));
+ return NULL;
+}
+
+raster_t* raster_import_bmp(const char* filename)
+{
+ FILE* file = fopen(filename, "rb");
+ if (file == NULL) {
+ fprintf(stderr, "Cannot read from %s: %s\n", filename, strerror(errno));
+ return NULL;
+ }
+
+ uint16_t magicNumber;
+ uint16_t reserved0;//0x4D41;
+ uint16_t reserved1;//0x5454;
+ uint32_t dataOffset;
+ uint32_t infoHeaderSize;
+ uint32_t width;
+ uint32_t height;
+ uint16_t colorPlanes;
+ uint16_t bitsPerPixel;
+ uint32_t compression;
+ uint32_t dataSize;
+ uint32_t horizontalResolution;
+ uint32_t verticalResolution;
+ uint32_t paletteColorCount;
+ uint32_t importantPaletteColorCount;
+ uint32_t fileSize;
+
+ raster_t* p = NULL;
+
+ _DO_OR_DIE(fread(&magicNumber, sizeof(magicNumber), 1, file));
+ _DO_OR_DIE(fread(&fileSize, sizeof(fileSize), 1, file));
+ _DO_OR_DIE(fread(&reserved0, sizeof(reserved0), 1, file));
+ _DO_OR_DIE(fread(&reserved1, sizeof(reserved1), 1, file));
+ _DO_OR_DIE(fread(&dataOffset, sizeof(dataOffset), 1, file));
+ _DO_OR_DIE(fread(&infoHeaderSize, sizeof(infoHeaderSize), 1, file));
+ _DO_OR_DIE(fread(&width, sizeof(width), 1, file));
+ _DO_OR_DIE(fread(&height, sizeof(height), 1, file));
+ _DO_OR_DIE(fread(&colorPlanes, sizeof(colorPlanes), 1, file));
+ _DO_OR_DIE(fread(&bitsPerPixel, sizeof(bitsPerPixel), 1, file));
+ _DO_OR_DIE(fread(&compression, sizeof(compression), 1, file));
+ _DO_OR_DIE(fread(&dataSize, sizeof(dataSize), 1, file));
+ _DO_OR_DIE(fread(&horizontalResolution, sizeof(horizontalResolution), 1, file));
+ _DO_OR_DIE(fread(&verticalResolution, sizeof(verticalResolution), 1, file));
+ _DO_OR_DIE(fread(&paletteColorCount, sizeof(paletteColorCount), 1, file));
+ _DO_OR_DIE(fread(&importantPaletteColorCount, sizeof(importantPaletteColorCount), 1, file));
+
+ p = raster_alloc((int)width, (int)height, COLOR_WHITE);
+
+ size_t size = width * height;
+ for (int i = 0; i < size; ++i)
+ {
+ union {
+ rgba_t rgba;
+ struct {
+ rgbachan_t r, g, b, a;
+ } chan;
+ } u;
+ _DO_OR_DIE(fread(&u, sizeof(u), 1, file));
+ rgbachan_t t = u.chan.r;
+ u.chan.r = u.chan.a;
+ u.chan.a = t;
+ t = u.chan.g;
+ u.chan.g = u.chan.b;
+ u.chan.b = t;
+ t = u.chan.r;
+ u.chan.r = u.chan.g;
+ u.chan.g = u.chan.b;
+ u.chan.b = u.chan.a;
+ u.chan.a = t;
+ p->pixels[i] = color_from_rgba(u.rgba);
+ }
+
+ fclose(file);
+ return p;
+
+fail:
+ if (p) {
+ raster_destroy(p);
+ }
+ fclose(file);
+ fprintf(stderr, "Unexpected file format in %s: %s\n", filename, strerror(errno));
+ return NULL;
+}
+
+#undef _DO_OR_DIE
+
+
+void raster_draw_model(raster_t* p, const model_t* model)
+{
+#if VERBOSITY >= 4
+#define PROGRESS_FMT "\033[80D\033[2K %s\t %9d / %d"
+ int tri;
+#endif
+
+ model_transformation(model, &p->model);
+ p->dirty = true;
+ raster_material(p, model_specular(model), model_shininess(model));
+ p->current = model;
+ IF_RENDER_PROGRESS(tri = 0);
+ array_it_t it = array_begin(model_geometry(model));
+ for (tri_t* t; t = array_it_tri_next(&it);) {
+#if VERBOSITY >= 4
+ if (++tri % 100 == 0) {
+ printf(PROGRESS_FMT, model_name(model), tri, model_size(model));
+ fflush(stdout);
+ }
+#endif
+ raster_draw_tri(p, t);
+ }
+#if VERBOSITY >= 4
+ printf(PROGRESS_FMT"\n", model_name(model), tri, model_size(model));
+#endif
+}
/*
* Determine what color is associated with the given vertex.
*/
INLINE_MAYBE
-color_t _get_vertex_color(raster_t* p, vert_t vert)
+color_t _do_phong_lighting(raster_t* p, vert_t vert)
{
+#if TEXTURING
+ vert.c = color_mult(vert.c, model_tcolor(p->current, vert.t));
+#endif
#if LIGHTING
color_t color = COLOR_BLACK;
+ color.a = vert.c.a;
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));
- vec_t r = vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l);
+ vec_t r = vec_normalize(vec_sub(vec_scale(n, S(2.0) * vec_dot(n, l)), l));
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;
p->modelviewprojection = mat_mult(p->projection, p->modelviewprojection);
p->dirty = false;
}
+
t = tri_transform(t, p->modelviewprojection);
+
+ // save w-values for texture mapping perspective correction
+ scal_t w1 = t.a.v.w;
+ scal_t w2 = t.b.v.w;
+ scal_t w3 = t.c.v.w;
+
t = tri_homodiv(t);
if (!_try_cull_backface(t)) {
return;
}
- tri_t temp = tri_transform(*triangle, p->model);
-#if LIGHTING && (!PRE_NORMALS || (!SMOOTH_COLOR && PRE_NORMALS >= 2))
- temp.a.n = temp.b.n = temp.c.n = vec_normalize(tri_normal(temp));
-#endif
-#if !SMOOTH_COLOR
- temp.a.c = tri_color(temp);
+#if LIGHTING >= 1
+ tri_t tl = tri_transform(*triangle, p->model);
+ tl.a.t.w = w1;
+ tl.b.t.w = w2;
+ tl.c.t.w = w3;
#endif
- color_t color1 = _get_vertex_color(p, temp.a);
-#if SMOOTH_COLOR
- color_t color2 = _get_vertex_color(p, temp.b);
- color_t color3 = _get_vertex_color(p, temp.c);
+
+#if LIGHTING == 1
+ vert_t tv = vert_new(tri_midpoint(tl));
+ tv.n = vec_normalize(tri_normal(tl));
+ tv.c = tri_color(tl);
+ color_t color = _do_phong_lighting(p, tv);
+#elif LIGHTING == 2 && SMOOTH_COLOR
+ color_t color1 = _do_phong_lighting(p, tl.a);
+ color_t color2 = _do_phong_lighting(p, tl.b);
+ color_t color3 = _do_phong_lighting(p, tl.c);
+#elif LIGHTING == 2 && !SMOOTH_COLOR
+ color_t c = tri_color(t);
+ tl.a.c = tl.b.c = tl.c.c = c;
+ color_t color1 = _do_phong_lighting(p, tl.a);
+ color_t color2 = _do_phong_lighting(p, tl.b);
+ color_t color3 = _do_phong_lighting(p, tl.c);
+#elif !LIGHTING && SMOOTH_COLOR
+ color_t color1 = t.a.c;
+ color_t color2 = t.b.c;
+ color_t color3 = t.c.c;
+#else
+ color_t color = tri_color(t);
#endif
for (int y = bottom; y < top; ++y) {
if (S(-1.0) < v.z && v.z < *n) {
#endif
color_t* c = p->pixels + y * p->w + x;
-#if SMOOTH_COLOR
- *c = color_interp2(color1, color2, color3, b);
+ color_t newC;
+
+#if LIGHTING == 2 || (!LIGHTING && SMOOTH_COLOR)
+ newC = color_interp2(color1, color2, color3, b);
+#elif LIGHTING == 3 && SMOOTH_COLOR
+ newC = _do_phong_lighting(p, tri_interp(tl, b));
+#elif LIGHTING == 3 && !SMOOTH_COLOR
+ vert_t d = vert_new(tri_point(t, b));
+ d.c = tri_color(t);
+ d.n = tri_normal2(t, b);
+ newC = _do_phong_lighting(p, d);
+#else
+ newC = color;
+#endif
+
+#if BLENDING
+ *c = color_blend(*c, newC);
#else
- *c = color1;
+ *c = newC;
#endif
+
#if DEPTH_TEST
*n = v.z;
}