summaryrefslogtreecommitdiffstats
path: root/libdimension
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@gmail.com>2011-12-14 19:27:22 -0500
committerTavian Barnes <tavianator@gmail.com>2011-12-14 19:52:36 -0500
commitbfbe9e43e108f6816c17b9b7764b75284ac78313 (patch)
tree189f85eeec18a76ccb626e45455fa7e45406db7c /libdimension
parent7db5342a36341b061a8785a3b349cf0fcad69ebf (diff)
downloaddimension-bfbe9e43e108f6816c17b9b7764b75284ac78313.tar.xz
Re-think colors.
Color is a property of light, and thus doesn't include information about transparency. But canvas pixels and object pigments represent a color and a degree of transparency. The new type dmnsn_tcolor/ TColor encapsulates that information. Also, fix the transparent shadow implementation.
Diffstat (limited to 'libdimension')
-rw-r--r--libdimension/Makefile.am2
-rw-r--r--libdimension/ambient.c53
-rw-r--r--libdimension/canvas.c12
-rw-r--r--libdimension/canvas_pigment.c6
-rw-r--r--libdimension/color.c344
-rw-r--r--libdimension/dimension.h1
-rw-r--r--libdimension/dimension/canvas.h18
-rw-r--r--libdimension/dimension/color.h192
-rw-r--r--libdimension/dimension/finish.h8
-rw-r--r--libdimension/dimension/finishes.h7
-rw-r--r--libdimension/dimension/pigment.h10
-rw-r--r--libdimension/dimension/pigments.h11
-rw-r--r--libdimension/dimension/tcolor.h100
-rw-r--r--libdimension/finish.c8
-rw-r--r--libdimension/gl.c40
-rw-r--r--libdimension/lambertian.c6
-rw-r--r--libdimension/pigment.c4
-rw-r--r--libdimension/pigment_map.c21
-rw-r--r--libdimension/png.c58
-rw-r--r--libdimension/ray_trace.c458
-rw-r--r--libdimension/rgba16.c17
-rw-r--r--libdimension/solid_pigment.c2
-rw-r--r--libdimension/tests/render.c57
-rw-r--r--libdimension/tests/test_canvas.c2
24 files changed, 613 insertions, 824 deletions
diff --git a/libdimension/Makefile.am b/libdimension/Makefile.am
index e99fe3c..aa5921e 100644
--- a/libdimension/Makefile.am
+++ b/libdimension/Makefile.am
@@ -63,12 +63,10 @@ nobase_include_HEADERS = dimension.h \
lib_LTLIBRARIES = libdimension.la
libdimension_la_SOURCES = $(nobase_include_HEADERS) \
- ambient.c \
camera.c \
canvas.c \
canvas_pigment.c \
checker.c \
- color.c \
compiler-internal.h \
cone.c \
cube.c \
diff --git a/libdimension/ambient.c b/libdimension/ambient.c
deleted file mode 100644
index 2f991f6..0000000
--- a/libdimension/ambient.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*************************************************************************
- * Copyright (C) 2010-2011 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * This file is part of The Dimension Library. *
- * *
- * The Dimension Library is free software; you can redistribute it and/ *
- * or modify it under the terms of the GNU Lesser General Public License *
- * as published by the Free Software Foundation; either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * The Dimension Library is distributed in the hope that it will be *
- * useful, but WITHOUT ANY WARRANTY; without even the implied warranty *
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
- * Lesser General Public License for more details. *
- * *
- * You should have received a copy of the GNU Lesser General Public *
- * License along with this program. If not, see *
- * <http://www.gnu.org/licenses/>. *
- *************************************************************************/
-
-/**
- * @file
- * Ambient finish.
- */
-
-#include "dimension.h"
-#include <math.h>
-#include <stdlib.h>
-
-/** Ambient finish callback. */
-static dmnsn_color
-dmnsn_basic_ambient_fn(const dmnsn_ambient *ambient, dmnsn_color pigment)
-{
- dmnsn_color *light = ambient->ptr;
- dmnsn_color ret = dmnsn_color_illuminate(*light, pigment);
- ret.trans = 0.0;
- ret.filter = 0.0;
- return ret;
-}
-
-dmnsn_ambient *
-dmnsn_new_basic_ambient(dmnsn_color ambient)
-{
- dmnsn_ambient *basic = dmnsn_new_ambient();
-
- dmnsn_color *param = dmnsn_malloc(sizeof(dmnsn_color));
- *param = ambient;
-
- basic->ambient_fn = dmnsn_basic_ambient_fn;
- basic->free_fn = dmnsn_free;
- basic->ptr = param;
- return basic;
-}
diff --git a/libdimension/canvas.c b/libdimension/canvas.c
index c0e8b2b..eeeb66b 100644
--- a/libdimension/canvas.c
+++ b/libdimension/canvas.c
@@ -35,7 +35,7 @@ dmnsn_new_canvas(size_t width, size_t height)
canvas->width = width;
canvas->height = height;
canvas->optimizers = dmnsn_new_array(sizeof(dmnsn_canvas_optimizer));
- canvas->pixels = dmnsn_malloc(sizeof(dmnsn_color)*width*height);
+ canvas->pixels = dmnsn_malloc(sizeof(dmnsn_tcolor)*width*height);
canvas->refcount = 1;
return canvas;
@@ -67,16 +67,16 @@ dmnsn_canvas_optimize(dmnsn_canvas *canvas, dmnsn_canvas_optimizer optimizer)
dmnsn_array_push(canvas->optimizers, &optimizer);
}
-/* Set the color of a pixel */
+/* Set the value of a pixel */
void
dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y,
- dmnsn_color color)
+ dmnsn_tcolor tcolor)
{
dmnsn_assert(x < canvas->width && y < canvas->height,
"Canvas access out of bounds.");
/* Set the pixel */
- canvas->pixels[y*canvas->width + x] = color;
+ canvas->pixels[y*canvas->width + x] = tcolor;
/* Call the optimizers */
DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer *, i, canvas->optimizers) {
@@ -86,11 +86,11 @@ dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y,
/* Fill a canvas with a solid color */
void
-dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_color color)
+dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor)
{
for (size_t x = 0; x < canvas->width; ++x) {
for (size_t y = 0; y < canvas->height; ++y) {
- dmnsn_canvas_set_pixel(canvas, x, y, color);
+ dmnsn_canvas_set_pixel(canvas, x, y, tcolor);
}
}
}
diff --git a/libdimension/canvas_pigment.c b/libdimension/canvas_pigment.c
index 564ce00..6847b6c 100644
--- a/libdimension/canvas_pigment.c
+++ b/libdimension/canvas_pigment.c
@@ -26,16 +26,14 @@
#include "dimension.h"
/** Canvas pigment color callback. */
-static dmnsn_color
+static dmnsn_tcolor
dmnsn_canvas_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v)
{
dmnsn_canvas *canvas = pigment->ptr;
size_t x = llround((fmod(v.x, 1.0) + 1.0)*(canvas->width - 1));
size_t y = llround((fmod(v.y, 1.0) + 1.0)*(canvas->height - 1));
- dmnsn_color c = dmnsn_canvas_get_pixel(canvas,
- x%canvas->width, y%canvas->height);
- return c;
+ return dmnsn_canvas_get_pixel(canvas, x%canvas->width, y%canvas->height);
}
/** Canvas pigment destructor. */
diff --git a/libdimension/color.c b/libdimension/color.c
deleted file mode 100644
index 862c8ea..0000000
--- a/libdimension/color.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*************************************************************************
- * Copyright (C) 2009-2011 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * This file is part of The Dimension Library. *
- * *
- * The Dimension Library is free software; you can redistribute it and/ *
- * or modify it under the terms of the GNU Lesser General Public License *
- * as published by the Free Software Foundation; either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * The Dimension Library is distributed in the hope that it will be *
- * useful, but WITHOUT ANY WARRANTY; without even the implied warranty *
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
- * Lesser General Public License for more details. *
- * *
- * You should have received a copy of the GNU Lesser General Public *
- * License along with this program. If not, see *
- * <http://www.gnu.org/licenses/>. *
- *************************************************************************/
-
-/**
- * @file
- * Color handling.
- */
-
-#include "dimension.h"
-
-/* Standard colors */
-const dmnsn_color dmnsn_black = {
- .R = 0.0,
- .G = 0.0,
- .B = 0.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_white = {
- .R = 1.0,
- .G = 1.0,
- .B = 1.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_clear = {
- .R = 0.0,
- .G = 0.0,
- .B = 0.0,
- .trans = 1.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_red = {
- .R = 1.0,
- .G = 0.0,
- .B = 0.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_green = {
- .R = 0.0,
- .G = 1.0,
- .B = 0.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_blue = {
- .R = 0.0,
- .G = 0.0,
- .B = 1.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_magenta = {
- .R = 1.0,
- .G = 0.0,
- .B = 1.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_orange = {
- .R = 1.0,
- .G = 0.21404114048223255,
- .B = 0.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_yellow = {
- .R = 1.0,
- .G = 1.0,
- .B = 0.0,
- .trans = 0.0,
- .filter = 0.0,
-};
-const dmnsn_color dmnsn_cyan = {
- .R = 0.0,
- .G = 1.0,
- .B = 1.0,
- .filter = 0.0,
- .trans = 0.0,
-};
-
-/* sRGB's `C' function. */
-static inline double
-dmnsn_sRGB_C(double Clinear)
-{
- /*
- * If C represents R, G, and B, then the sRGB values are now found as follows:
- *
- * { 12.92*Clinear, Clinear <= 0.0031308
- * Csrgb = { 1/2.4
- * { (1.055)*Clinear - 0.055, Clinear > 0.0031308
- */
- if (Clinear == 1.0) {
- return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */
- } else if (Clinear > 0.0031308) {
- return 1.055*pow(Clinear, 1.0/2.4) - 0.055;
- } else {
- return 12.92*Clinear;
- }
-}
-
-/* Export dmnsn_sRGB_C */
-double
-dmnsn_sRGB_gamma(double Clinear)
-{
- return dmnsn_sRGB_C(Clinear);
-}
-
-/* Convert to sRGB space */
-dmnsn_color
-dmnsn_color_to_sRGB(dmnsn_color color)
-{
- dmnsn_color ret = {
- .R = dmnsn_sRGB_C(color.R),
- .G = dmnsn_sRGB_C(color.G),
- .B = dmnsn_sRGB_C(color.B),
- .trans = color.trans,
- .filter = color.filter,
- };
- return ret;
-}
-
-/* Inverse function of sRGB's `C' function, for the reverse conversion. */
-double
-dmnsn_sRGB_C_inv(double CsRGB)
-{
- /*
- * If C represents R, G, and B, then the Clinear values are now found as
- * follows:
- *
- * { Csrgb/12.92, Csrgb <= 0.04045
- * Clinear = { 1/2.4
- * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045
- */
- if (CsRGB == 1.0) {
- return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */
- } else if (CsRGB <= 0.040449936) {
- return CsRGB/12.92;
- } else {
- return pow((CsRGB + 0.055)/1.055, 2.4);
- }
-}
-
-/* Export dmnsn_sRGB_C_inv */
-double
-dmnsn_sRGB_inverse_gamma(double CsRGB)
-{
- return dmnsn_sRGB_C_inv(CsRGB);
-}
-
-/* Convert from sRGB space */
-dmnsn_color
-dmnsn_color_from_sRGB(dmnsn_color color)
-{
- dmnsn_color ret = {
- .R = dmnsn_sRGB_C_inv(color.R),
- .G = dmnsn_sRGB_C_inv(color.G),
- .B = dmnsn_sRGB_C_inv(color.B),
- .trans = color.trans,
- .filter = color.filter,
- };
- return ret;
-}
-
-/* Greyscale color intensity */
-double
-dmnsn_color_intensity(dmnsn_color color)
-{
- return 0.2126*color.R + 0.7152*color.G + 0.0722*color.B;
-}
-
-/* Add two colors */
-dmnsn_color
-dmnsn_color_add(dmnsn_color c1, dmnsn_color c2)
-{
- dmnsn_color ret = dmnsn_new_color(c1.R + c2.R, c1.G + c2.G, c1.B + c2.B);
-
- /* Switch into absolute filter and transmittance space */
- double n1 = dmnsn_color_intensity(c1), n2 = dmnsn_color_intensity(c2);
- double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans;
- double t1 = c1.trans - f1, t2 = c2.trans - f2;
- double f = 0.0;
- if (n1 + n2 >= dmnsn_epsilon)
- f = (n1*f1 + n2*f2)/(n1 + n2);
- double t = t1 + t2;
-
- /* Switch back */
- ret.trans = f + t;
- if (ret.trans >= dmnsn_epsilon)
- ret.filter = f/ret.trans;
-
- return ret;
-}
-
-/* Subtract two colors */
-dmnsn_color
-dmnsn_color_sub(dmnsn_color c1, dmnsn_color c2)
-{
- dmnsn_color ret = dmnsn_new_color(c1.R - c2.R, c1.G - c2.G, c1.B - c2.B);
-
- /* Switch into absolute filter and transmittance space */
- double n1 = dmnsn_color_intensity(c1), n2 = dmnsn_color_intensity(c2);
- double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans;
- double t1 = c1.trans - f1, t2 = c2.trans - f2;
- double f = 0.0;
- if (n1 - n2 >= dmnsn_epsilon)
- f = (n1*f1 - n2*f2)/(n1 - n2);
- double t = t1 - t2;
-
- /* Switch back */
- ret.trans = f + t;
- if (ret.trans >= dmnsn_epsilon)
- ret.filter = f/ret.trans;
-
- return ret;
-}
-
-/* Multiply a color by a scalar */
-dmnsn_color
-dmnsn_color_mul(double n, dmnsn_color color)
-{
- color.R *= n;
- color.G *= n;
- color.B *= n;
- color.trans *= n;
- return color;
-}
-
-/* For n in [0, 1] get the color in a gradient between c1 and c2 */
-dmnsn_color
-dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n)
-{
- dmnsn_color ret = dmnsn_new_color(
- n*(c2.R - c1.R) + c1.R,
- n*(c2.G - c1.G) + c1.G,
- n*(c2.B - c1.B) + c1.B
- );
-
- /* Switch into absolute filter and transmittance space */
- double f1 = c1.filter*c1.trans, f2 = c2.filter*c2.trans;
- double t1 = c1.trans - f1, t2 = c2.trans - f2;
- double f = n*(f2 - f1) + f1;
- double t = n*(t2 - t1) + t1;
-
- /* Switch back */
- ret.trans = f + t;
- ret.filter = 0.0;
- if (ret.trans >= dmnsn_epsilon)
- ret.filter = f/ret.trans;
-
- return ret;
-}
-
-/* Filters `light' through `filter' */
-dmnsn_color
-dmnsn_filter_light(dmnsn_color light, dmnsn_color filter)
-{
- dmnsn_color transmitted = dmnsn_color_mul(
- (1.0 - filter.filter)*filter.trans,
- light
- );
- dmnsn_color filtered = dmnsn_color_mul(
- filter.filter*filter.trans,
- dmnsn_color_illuminate(filter, light)
- );
-
- dmnsn_color ret = dmnsn_new_color(
- transmitted.R + filtered.R,
- transmitted.G + filtered.G,
- transmitted.B + filtered.B
- );
-
- /* Switch into absolute filter and transmittance space */
- double lf = light.filter*light.trans, ff = filter.filter*filter.trans;
- double lt = light.trans - lf, ft = filter.trans - ff;
- double f = lf*(dmnsn_color_intensity(filtered) + ft) + lt*ff;
- double t = ft*lt;
-
- /* Switch back */
- ret.trans = f + t;
- if (ret.trans >= dmnsn_epsilon)
- ret.filter = f/ret.trans;
-
- return ret;
-}
-
-/* Adds the background contribution, `filtered', to `filter' */
-dmnsn_color
-dmnsn_apply_transparency(dmnsn_color filtered, dmnsn_color filter)
-{
- dmnsn_color ret = dmnsn_color_add(
- dmnsn_color_mul(1.0 - filter.trans, filter),
- filtered
- );
- ret.trans = filtered.trans;
- ret.filter = filtered.filter;
- return ret;
-}
-
-/* Adds the background contribution of `color' to `filter' */
-dmnsn_color
-dmnsn_apply_filter(dmnsn_color color, dmnsn_color filter)
-{
- return dmnsn_apply_transparency(dmnsn_filter_light(color, filter), filter);
-}
-
-/* Remove the filter channel */
-dmnsn_color
-dmnsn_remove_filter(dmnsn_color color)
-{
- double intensity = dmnsn_color_intensity(color);
- double newtrans = (1.0 - (1.0 - intensity)*color.filter)*color.trans;
- if (1.0 - newtrans >= dmnsn_epsilon)
- color = dmnsn_color_mul((1.0 - color.trans)/(1.0 - newtrans), color);
- color.trans = newtrans;
- color.filter = 0.0;
- return color;
-}
-
-/* Illuminates `color' with `light' */
-dmnsn_color
-dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color)
-{
- return dmnsn_new_color5(light.R*color.R, light.G*color.G, light.B*color.B,
- color.filter, color.trans);
-}
diff --git a/libdimension/dimension.h b/libdimension/dimension.h
index e427b17..708cd8a 100644
--- a/libdimension/dimension.h
+++ b/libdimension/dimension.h
@@ -85,6 +85,7 @@ typedef void dmnsn_free_fn(void *ptr);
#include <dimension/geometry.h>
#include <dimension/polynomial.h>
#include <dimension/color.h>
+#include <dimension/tcolor.h>
#include <dimension/canvas.h>
#include <dimension/gl.h>
#include <dimension/png.h>
diff --git a/libdimension/dimension/canvas.h b/libdimension/dimension/canvas.h
index 48a64c1..ba06b8c 100644
--- a/libdimension/dimension/canvas.h
+++ b/libdimension/dimension/canvas.h
@@ -26,7 +26,7 @@
#include <stddef.h>
/** A canvas, or image. */
-typedef struct {
+typedef struct dmnsn_canvas {
size_t width; /**< Canvas width. */
size_t height; /**< Canvas height. */
@@ -38,7 +38,7 @@ typedef struct {
* Stored in first-quadrant representation (origin is bottom-left). The pixel
* at (a,b) is accessible as pixels[b*width + a].
*/
- dmnsn_color *pixels;
+ dmnsn_tcolor *pixels;
dmnsn_refcount refcount; /**< @internal Reference count. */
} dmnsn_canvas;
@@ -94,9 +94,9 @@ void dmnsn_canvas_optimize(dmnsn_canvas *canvas,
* @param[in] canvas The canvas to access.
* @param[in] x The x coordinate.
* @param[in] y The y coordinate.
- * @return The color of the canvas at (\p x, \p y).
+ * @return The color of the pixel at (\p x, \p y).
*/
-DMNSN_INLINE dmnsn_color
+DMNSN_INLINE dmnsn_tcolor
dmnsn_canvas_get_pixel(const dmnsn_canvas *canvas, size_t x, size_t y)
{
dmnsn_assert(x < canvas->width && y < canvas->height,
@@ -105,18 +105,18 @@ dmnsn_canvas_get_pixel(const dmnsn_canvas *canvas, size_t x, size_t y)
}
/**
- * Set the color of a pixel.
+ * Set the value of a pixel.
* @param[in,out] canvas The canvas to modify.
* @param[in] x The x coordinate of the pixel.
* @param[in] y The y coordinate of the pixel.
- * @param[in] color The color to set the pixel at (\p x, \p y) to.
+ * @param[in] tcolor The value to set the pixel at (\p x, \p y) to.
*/
void dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y,
- dmnsn_color color);
+ dmnsn_tcolor tcolor);
/**
* Clear a canvas uniformly with a given color.
* @param[in,out] canvas The canvas to erase.
- * @param[in] color The color to paint it with.
+ * @param[in] tcolor The color to paint it with.
*/
-void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_color color);
+void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor);
diff --git a/libdimension/dimension/color.h b/libdimension/dimension/color.h
index 295400b..34f3884 100644
--- a/libdimension/dimension/color.h
+++ b/libdimension/dimension/color.h
@@ -20,97 +20,167 @@
/**
* @file
- * Color-related types and operations.
+ * Colors.
*/
#include <stdbool.h>
/** A color value. */
typedef struct {
- double R; /**< Red. */
- double G; /**< Green. */
- double B; /**< Blue. */
-
- double trans; /**< Transparency. */
- double filter; /**< Degree of filtering. */
+ double R; /**< Red component. */
+ double G; /**< Green component. */
+ double B; /**< Blue component. */
} dmnsn_color;
/** A standard format string for colors. */
-#define DMNSN_COLOR_FORMAT \
- "<red = %g, green = %g, blue = %g, trans = %g, filter = %g>"
+#define DMNSN_COLOR_FORMAT "Color<%g, %g, %g>"
/** The appropriate arguements to printf() a color. */
-#define DMNSN_COLOR_PRINTF(c) (c).R, (c).G, (c).B, (c).trans, (c).filter
-
-/* Standard colors */
-extern const dmnsn_color dmnsn_black; /**< Black. */
-extern const dmnsn_color dmnsn_white; /**< White. */
-extern const dmnsn_color dmnsn_clear; /**< Clear. */
-extern const dmnsn_color dmnsn_red; /**< Red. */
-extern const dmnsn_color dmnsn_green; /**< Green. */
-extern const dmnsn_color dmnsn_blue; /**< Blue. */
-extern const dmnsn_color dmnsn_magenta; /**< Magenta. */
-extern const dmnsn_color dmnsn_orange; /**< Orange. */
-extern const dmnsn_color dmnsn_yellow; /**< Yellow. */
-extern const dmnsn_color dmnsn_cyan; /**< Cyan. */
-
-/* Color construction */
+#define DMNSN_COLOR_PRINTF(c) (c).R, (c).G, (c).B
/** Construct a new color. */
DMNSN_INLINE dmnsn_color
dmnsn_new_color(double R, double G, double B)
{
- dmnsn_color ret = { R, G, B, 0.0, 0.0 };
+ dmnsn_color ret = { R, G, B };
return ret;
}
-/** Construct a new color with transparent components. */
-DMNSN_INLINE dmnsn_color
-dmnsn_new_color5(double R, double G, double B, double trans, double filter)
+/** Apply sRGB gamma */
+DMNSN_INLINE double
+dmnsn_sRGB_gamma(double Clinear)
{
- dmnsn_color ret = { R, G, B, trans, filter };
- return ret;
+ /*
+ * If C represents R, G, and B, then the sRGB values are now found as follows:
+ *
+ * { 12.92*Clinear, Clinear <= 0.0031308
+ * Csrgb = { 1/2.4
+ * { (1.055)*Clinear - 0.055, Clinear > 0.0031308
+ */
+ if (Clinear == 1.0) {
+ return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */
+ } else if (Clinear > 0.0031308) {
+ return 1.055*pow(Clinear, 1.0/2.4) - 0.055;
+ } else {
+ return 12.92*Clinear;
+ }
}
-/** Saturate the color components to [0.0, 1.0]. */
+/** Convert to sRGB space. */
DMNSN_INLINE dmnsn_color
-dmnsn_color_saturate(dmnsn_color color)
+dmnsn_color_to_sRGB(dmnsn_color color)
{
- color.R = dmnsn_min(dmnsn_max(color.R, 0.0), 1.0);
- color.G = dmnsn_min(dmnsn_max(color.G, 0.0), 1.0);
- color.B = dmnsn_min(dmnsn_max(color.B, 0.0), 1.0);
- color.trans = dmnsn_min(dmnsn_max(color.trans, 0.0), 1.0);
- color.filter = dmnsn_min(dmnsn_max(color.filter, 0.0), 1.0);
- return color;
+ return dmnsn_new_color(
+ dmnsn_sRGB_gamma(color.R),
+ dmnsn_sRGB_gamma(color.G),
+ dmnsn_sRGB_gamma(color.B)
+ );
}
-/* Perceptual color manipulation */
-
-/** Apply sRGB gamma */
-double dmnsn_sRGB_gamma(double Clinear);
-/** Convert to sRGB space. */
-dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color);
/** Remove sRGB gamma */
-double dmnsn_sRGB_inverse_gamma(double CsRGB);
+DMNSN_INLINE double
+dmnsn_sRGB_inverse_gamma(double CsRGB)
+{
+ /*
+ * If C represents R, G, and B, then the Clinear values are now found as
+ * follows:
+ *
+ * { Csrgb/12.92, Csrgb <= 0.04045
+ * Clinear = { 1/2.4
+ * { ((Csrgb + 0.055)/1.055) , Csrgb > 0.04045
+ */
+ if (CsRGB == 1.0) {
+ return 1.0; /* Map 1.0 to 1.0 instead of 0.9999999999999999 */
+ } else if (CsRGB <= 0.040449936) {
+ return CsRGB/12.92;
+ } else {
+ return pow((CsRGB + 0.055)/1.055, 2.4);
+ }
+}
+
/** Convert from sRGB space. */
-dmnsn_color dmnsn_color_from_sRGB(dmnsn_color color);
+DMNSN_INLINE dmnsn_color
+dmnsn_color_from_sRGB(dmnsn_color color)
+{
+ return dmnsn_new_color(
+ dmnsn_sRGB_inverse_gamma(color.R),
+ dmnsn_sRGB_inverse_gamma(color.G),
+ dmnsn_sRGB_inverse_gamma(color.B)
+ );
+}
/** Greyscale color intensity. */
-double dmnsn_color_intensity(dmnsn_color color);
+DMNSN_INLINE double
+dmnsn_color_intensity(dmnsn_color color)
+{
+ return 0.2126*color.R + 0.7152*color.G + 0.0722*color.B;
+}
+
/** Add two colors together. */
-dmnsn_color dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs);
+DMNSN_INLINE dmnsn_color
+dmnsn_color_add(dmnsn_color lhs, dmnsn_color rhs)
+{
+ return dmnsn_new_color(lhs.R + rhs.R, lhs.G + rhs.G, lhs.B + rhs.B);
+}
+
/** Subtract two colors. */
-dmnsn_color dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs);
-/** Multiply a color's intensity by \p n. */
-dmnsn_color dmnsn_color_mul(double n, dmnsn_color color);
+DMNSN_INLINE dmnsn_color
+dmnsn_color_sub(dmnsn_color lhs, dmnsn_color rhs)
+{
+ return dmnsn_new_color(lhs.R - rhs.R, lhs.G - rhs.G, lhs.B - rhs.B);
+}
+
+/** Scale a color's intensity. */
+DMNSN_INLINE dmnsn_color
+dmnsn_color_mul(double n, dmnsn_color color)
+{
+ return dmnsn_new_color(n*color.R, n*color.G, n*color.B);
+}
+
/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */
-dmnsn_color dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n);
-/** Filter \p light through \p filter. */
-dmnsn_color dmnsn_filter_light(dmnsn_color light, dmnsn_color filter);
-/** Add the background contribution \p filtered to \p filter. */
-dmnsn_color dmnsn_apply_transparency(dmnsn_color filtered, dmnsn_color filter);
-/** Add the background contribution of \p color to \p filter. */
-dmnsn_color dmnsn_apply_filter(dmnsn_color color, dmnsn_color filter);
-/** Convert the color into a close equivalent with only transmittance. */
-dmnsn_color dmnsn_remove_filter(dmnsn_color color);
+DMNSN_INLINE dmnsn_color
+dmnsn_color_gradient(dmnsn_color c1, dmnsn_color c2, double n)
+{
+ return dmnsn_new_color(
+ n*(c2.R - c1.R) + c1.R,
+ n*(c2.G - c1.G) + c1.G,
+ n*(c2.B - c1.B) + c1.B
+ );
+}
+
/** Illuminate \p color with \p light. */
-dmnsn_color dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color);
+DMNSN_INLINE dmnsn_color
+dmnsn_color_illuminate(dmnsn_color light, dmnsn_color color)
+{
+ return dmnsn_new_color(light.R*color.R, light.G*color.G, light.B*color.B);
+}
+
+/** Saturate the color components to [0.0, 1.0]. */
+DMNSN_INLINE dmnsn_color
+dmnsn_color_saturate(dmnsn_color color)
+{
+ color.R = dmnsn_min(dmnsn_max(color.R, 0.0), 1.0);
+ color.G = dmnsn_min(dmnsn_max(color.G, 0.0), 1.0);
+ color.B = dmnsn_min(dmnsn_max(color.B, 0.0), 1.0);
+ return color;
+}
+
+/* Standard colors */
+
+/** Black. */
+#define dmnsn_black dmnsn_new_color(0.0, 0.0, 0.0)
+/** White. */
+#define dmnsn_white dmnsn_new_color(1.0, 1.0, 1.0)
+/** Red. */
+#define dmnsn_red dmnsn_new_color(1.0, 0.0, 0.0)
+/** Green. */
+#define dmnsn_green dmnsn_new_color(0.0, 1.0, 0.0)
+/** Blue. */
+#define dmnsn_blue dmnsn_new_color(0.0, 0.0, 1.0)
+/** Magenta. */
+#define dmnsn_magenta dmnsn_new_color(1.0, 0.0, 1.0)
+/** Orange. */
+#define dmnsn_orange dmnsn_new_color(1.0, 0.21404114048223255, 0.0)
+/** Yellow. */
+#define dmnsn_yellow dmnsn_new_color(1.0, 1.0, 0.0)
+/** Cyan. */
+#define dmnsn_cyan dmnsn_new_color(0.0, 1.0, 1.0)
diff --git a/libdimension/dimension/finish.h b/libdimension/dimension/finish.h
index 2e3b48c..4ac9239 100644
--- a/libdimension/dimension/finish.h
+++ b/libdimension/dimension/finish.h
@@ -38,14 +38,12 @@ typedef dmnsn_color dmnsn_ambient_fn(const dmnsn_ambient *ambient,
/** Ambient finish component. */
struct dmnsn_ambient {
- dmnsn_ambient_fn *ambient_fn; /**< Ambient callback. */
- dmnsn_free_fn *free_fn; /**< Destructor callback. */
- void *ptr; /**< Generic data pointer. */
- dmnsn_refcount refcount; /**< @internal Reference count. */
+ dmnsn_color ambient; /**< Ambient light. */
+ dmnsn_refcount refcount; /**< @internal Reference count. */
};
/** Allocate a dummy ambient component. */
-dmnsn_ambient *dmnsn_new_ambient(void);
+dmnsn_ambient *dmnsn_new_ambient(dmnsn_color ambient);
/** Delete an ambient component. */
void dmnsn_delete_ambient(dmnsn_ambient *ambient);
diff --git a/libdimension/dimension/finishes.h b/libdimension/dimension/finishes.h
index d9cd14c..7ed66d3 100644
--- a/libdimension/dimension/finishes.h
+++ b/libdimension/dimension/finishes.h
@@ -24,13 +24,6 @@
*/
/**
- * Ambient finish.
- * @param[in] ambient The color of the ambient light.
- * @return An ambient finish component.
- */
-dmnsn_ambient *dmnsn_new_basic_ambient(dmnsn_color ambient);
-
-/**
* Regular diffuse finish.
* @param[in] diffuse The diffuse reflection coefficient.
* @return A diffuse finish component.
diff --git a/libdimension/dimension/pigment.h b/libdimension/dimension/pigment.h
index 716c28b..176ff28 100644
--- a/libdimension/dimension/pigment.h
+++ b/libdimension/dimension/pigment.h
@@ -32,8 +32,8 @@ typedef struct dmnsn_pigment dmnsn_pigment;
* @param[in] v The point to color.
* @return The color of the pigment at \p v.
*/
-typedef dmnsn_color dmnsn_pigment_fn(const dmnsn_pigment *pigment,
- dmnsn_vector v);
+typedef dmnsn_tcolor dmnsn_pigment_fn(const dmnsn_pigment *pigment,
+ dmnsn_vector v);
/**
* Pigment initializer callback.
@@ -51,7 +51,7 @@ struct dmnsn_pigment {
dmnsn_matrix trans_inv; /**< The inverse of the transformation matrix. */
/** Quick color -- used for low-quality renders. */
- dmnsn_color quick_color;
+ dmnsn_tcolor quick_color;
/** Generic pointer. */
void *ptr;
@@ -86,5 +86,5 @@ void dmnsn_pigment_initialize(dmnsn_pigment *pigment);
* @param[in] v The point to color.
* @return The color at \p v.
*/
-dmnsn_color dmnsn_pigment_evaluate(const dmnsn_pigment *pigment,
- dmnsn_vector v);
+dmnsn_tcolor dmnsn_pigment_evaluate(const dmnsn_pigment *pigment,
+ dmnsn_vector v);
diff --git a/libdimension/dimension/pigments.h b/libdimension/dimension/pigments.h
index 3584167..383e38b 100644
--- a/libdimension/dimension/pigments.h
+++ b/libdimension/dimension/pigments.h
@@ -28,7 +28,7 @@
* @param[in] color The color of the pigment.
* @return A pigment with the color \p color everywhere.
*/
-dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_color color);
+dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_tcolor color);
/**
* An image map. The image (regardless of its real dimensions) is projected
@@ -53,15 +53,6 @@ typedef enum dmnsn_pigment_map_flags {
dmnsn_map *dmnsn_new_pigment_map(void);
/**
- * Add a raw color to a pigment map.
- * Shorthand for creating a solid pigment and adding it manually.
- * @param[in,out] map The pigment map to add to.
- * @param[in] n The index of the entry.
- * @param[in] color The value of the entry.
- */
-void dmnsn_pigment_map_add_color(dmnsn_map *map, double n, dmnsn_color color);
-
-/**
* A pigment-mapped pigment.
* @param[in,out] pattern The pattern of the pigment.
* @param[in,out] map The pigment map to apply to the pattern.
diff --git a/libdimension/dimension/tcolor.h b/libdimension/dimension/tcolor.h
new file mode 100644
index 0000000..d1b7338
--- /dev/null
+++ b/libdimension/dimension/tcolor.h
@@ -0,0 +1,100 @@
+/*************************************************************************
+ * Copyright (C) 2009-2011 Tavian Barnes <tavianator@tavianator.com> *
+ * *
+ * This file is part of The Dimension Library. *
+ * *
+ * The Dimension Library is free software; you can redistribute it and/ *
+ * or modify it under the terms of the GNU Lesser General Public License *
+ * as published by the Free Software Foundation; either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * The Dimension Library is distributed in the hope that it will be *
+ * useful, but WITHOUT ANY WARRANTY; without even the implied warranty *
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+/**
+ * @file
+ * Colors with transparency information.
+ */
+
+/** A transparent color. */
+typedef struct dmnsn_tcolor {
+ dmnsn_color c; /**< Color. */
+ double T; /**< Transparency. */
+ double F; /**< Proportion of filtered transparency. */
+} dmnsn_tcolor;
+
+/** Create a tcolor. */
+DMNSN_INLINE dmnsn_tcolor
+dmnsn_new_tcolor(dmnsn_color c, double T, double F)
+{
+ dmnsn_tcolor ret = { c, T, F };
+ return ret;
+}
+
+/** Convert a dmnsn_color into a dmnsn_tcolor. */
+#define DMNSN_TCOLOR(c) dmnsn_new_tcolor(c, 0.0, 0.0)
+
+/** Create a tcolor with individually-specified components. */
+DMNSN_INLINE dmnsn_tcolor
+dmnsn_new_tcolor5(double R, double G, double B, double T, double F)
+{
+ dmnsn_tcolor ret = { dmnsn_new_color(R, G, B), T, F };
+ return ret;
+}
+
+/** Return the color at \p n on a gradient from \p c1 at 0 to \p c2 at 1. */
+DMNSN_INLINE dmnsn_tcolor
+dmnsn_tcolor_gradient(dmnsn_tcolor c1, dmnsn_tcolor c2, double n)
+{
+ return dmnsn_new_tcolor(
+ dmnsn_color_gradient(c1.c, c2.c, n),
+ n*(c2.T - c1.T) + c1.T,
+ n*(c2.F - c1.F) + c1.F
+ );
+}
+
+/** Filter \p light through \p filter. */
+DMNSN_INLINE dmnsn_color
+dmnsn_tcolor_filter(dmnsn_color light, dmnsn_tcolor filter)
+{
+ dmnsn_color filtered =
+ dmnsn_color_mul(filter.T*filter.F, dmnsn_color_illuminate(light, filter.c));
+ dmnsn_color transmitted = dmnsn_color_mul(filter.T*(1.0 - filter.F), light);
+ return dmnsn_color_add(filtered, transmitted);
+}
+
+/** Remove the filtered component of a tcolor. */
+DMNSN_INLINE dmnsn_tcolor
+dmnsn_tcolor_remove_filter(dmnsn_tcolor tcolor)
+{
+ double intensity = dmnsn_color_intensity(tcolor.c);
+ double newtrans = (1.0 - (1.0 - intensity)*tcolor.F)*tcolor.T;
+ if (1.0 - newtrans >= dmnsn_epsilon) {
+ tcolor.c = dmnsn_color_mul((1.0 - tcolor.T)/(1.0 - newtrans), tcolor.c);
+ }
+ tcolor.T = newtrans;
+ tcolor.F = 0.0;
+ return tcolor;
+}
+
+/** Saturate the tcolor components to [0.0, 1.0]. */
+DMNSN_INLINE dmnsn_tcolor
+dmnsn_tcolor_saturate(dmnsn_tcolor tcolor)
+{
+ tcolor.c = dmnsn_color_saturate(tcolor.c);
+ tcolor.T = dmnsn_min(dmnsn_max(tcolor.T, 0.0), 1.0);
+ tcolor.F = dmnsn_min(dmnsn_max(tcolor.F, 0.0), 1.0);
+ return tcolor;
+}
+
+/* Standard tcolors */
+
+/** Clear. */
+#define dmnsn_clear dmnsn_new_tcolor5(0.0, 0.0, 0.0, 0.0, 0.0)
diff --git a/libdimension/finish.c b/libdimension/finish.c
index 90f41ae..cd922ff 100644
--- a/libdimension/finish.c
+++ b/libdimension/finish.c
@@ -26,11 +26,10 @@
#include "dimension-internal.h"
dmnsn_ambient *
-dmnsn_new_ambient(void)
+dmnsn_new_ambient(dmnsn_color ambient_light)
{
dmnsn_ambient *ambient = dmnsn_malloc(sizeof(dmnsn_ambient));
- ambient->free_fn = NULL;
- ambient->ptr = NULL;
+ ambient->ambient = ambient_light;
ambient->refcount = 1;
return ambient;
}
@@ -39,9 +38,6 @@ void
dmnsn_delete_ambient(dmnsn_ambient *ambient)
{
if (DMNSN_DECREF(ambient)) {
- if (ambient->free_fn) {
- ambient->free_fn(ambient->ptr);
- }
dmnsn_free(ambient);
}
}
diff --git a/libdimension/gl.c b/libdimension/gl.c
index 1250307..e2d222c 100644
--- a/libdimension/gl.c
+++ b/libdimension/gl.c
@@ -40,10 +40,6 @@ dmnsn_gl_optimize_canvas(dmnsn_canvas *canvas)
int
dmnsn_gl_write_canvas(const dmnsn_canvas *canvas)
{
- GLushort *pixels; /* Array of 16-bit ints in RGBA order */
- GLushort *pixel;
- dmnsn_color color;
-
size_t width = canvas->width;
size_t height = canvas->height;
@@ -56,21 +52,21 @@ dmnsn_gl_write_canvas(const dmnsn_canvas *canvas)
}
/* We couldn't, so transform the canvas to RGB now */
- pixels = dmnsn_malloc(4*width*height*sizeof(GLushort));
+ GLushort *pixels = dmnsn_malloc(4*width*height*sizeof(GLushort));
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
- pixel = pixels + 4*(y*width + x);
+ GLushort *pixel = pixels + 4*(y*width + x);
- color = dmnsn_canvas_get_pixel(canvas, x, y);
- color = dmnsn_remove_filter(color);
- color = dmnsn_color_to_sRGB(color);
- color = dmnsn_color_saturate(color);
+ dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y);
+ tcolor = dmnsn_tcolor_remove_filter(tcolor);
+ tcolor.c = dmnsn_color_to_sRGB(tcolor.c);
+ tcolor = dmnsn_tcolor_saturate(tcolor);
- pixel[0] = lround(color.R*UINT16_MAX);
- pixel[1] = lround(color.G*UINT16_MAX);
- pixel[2] = lround(color.B*UINT16_MAX);
- pixel[3] = lround(color.trans*UINT16_MAX);
+ pixel[0] = lround(tcolor.c.R*UINT16_MAX);
+ pixel[1] = lround(tcolor.c.G*UINT16_MAX);
+ pixel[2] = lround(tcolor.c.B*UINT16_MAX);
+ pixel[3] = lround(tcolor.T*UINT16_MAX);
}
}
@@ -102,13 +98,15 @@ dmnsn_gl_read_canvas(size_t x0, size_t y0,
for (size_t x = 0; x < width; ++x) {
GLushort *pixel = pixels + 4*(y*width + x);
- dmnsn_color color = dmnsn_new_color5((double)pixel[0]/UINT16_MAX,
- (double)pixel[1]/UINT16_MAX,
- (double)pixel[2]/UINT16_MAX,
- (double)pixel[3]/UINT16_MAX,
- 0.0);
- color = dmnsn_color_from_sRGB(color);
- dmnsn_canvas_set_pixel(canvas, x, y, color);
+ dmnsn_tcolor tcolor = dmnsn_new_tcolor5(
+ (double)pixel[0]/UINT16_MAX,
+ (double)pixel[1]/UINT16_MAX,
+ (double)pixel[2]/UINT16_MAX,
+ (double)pixel[3]/UINT16_MAX,
+ 0.0
+ );
+ tcolor.c = dmnsn_color_from_sRGB(tcolor.c);
+ dmnsn_canvas_set_pixel(canvas, x, y, tcolor);
}
}
diff --git a/libdimension/lambertian.c b/libdimension/lambertian.c
index 8227cc5..16dc910 100644
--- a/libdimension/lambertian.c
+++ b/libdimension/lambertian.c
@@ -35,11 +35,7 @@ dmnsn_lambertian_diffuse_fn(const dmnsn_diffuse *diffuse,
{
double *coeff = diffuse->ptr;
double diffuse_factor = fabs((*coeff)*dmnsn_vector_dot(ray, normal));
- dmnsn_color ret
- = dmnsn_color_mul(diffuse_factor, dmnsn_color_illuminate(light, color));
- ret.filter = 0.0;
- ret.trans = 0.0;
- return ret;
+ return dmnsn_color_mul(diffuse_factor, dmnsn_color_illuminate(light, color));
}
dmnsn_diffuse *
diff --git a/libdimension/pigment.c b/libdimension/pigment.c
index 74159ee..c518005 100644
--- a/libdimension/pigment.c
+++ b/libdimension/pigment.c
@@ -34,7 +34,7 @@ dmnsn_new_pigment(void)
pigment->initialize_fn = NULL;
pigment->free_fn = NULL;
pigment->trans = dmnsn_identity_matrix();
- pigment->quick_color = dmnsn_black;
+ pigment->quick_color = DMNSN_TCOLOR(dmnsn_black);
pigment->refcount = 1;
pigment->initialized = false;
return pigment;
@@ -67,7 +67,7 @@ dmnsn_pigment_initialize(dmnsn_pigment *pigment)
}
/* Evaluate a pigment */
-dmnsn_color
+dmnsn_tcolor
dmnsn_pigment_evaluate(const dmnsn_pigment *pigment, dmnsn_vector v)
{
if (pigment->pigment_fn) {
diff --git a/libdimension/pigment_map.c b/libdimension/pigment_map.c
index 09665c0..f34be5a 100644
--- a/libdimension/pigment_map.c
+++ b/libdimension/pigment_map.c
@@ -56,13 +56,6 @@ typedef struct dmnsn_pigment_map_payload {
dmnsn_pigment_map_flags flags;
} dmnsn_pigment_map_payload;
-void
-dmnsn_pigment_map_add_color(dmnsn_map *map, double n, dmnsn_color color)
-{
- dmnsn_pigment *pigment = dmnsn_new_solid_pigment(color);
- dmnsn_map_add_entry(map, n, &pigment);
-}
-
/** Free a pigment_map payload. */
static void
dmnsn_delete_pigment_map_payload(void *ptr)
@@ -74,7 +67,7 @@ dmnsn_delete_pigment_map_payload(void *ptr)
}
/** pigment_map pigment callback. */
-static dmnsn_color
+static dmnsn_tcolor
dmnsn_pigment_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v)
{
const dmnsn_pigment_map_payload *payload = pigment->ptr;
@@ -82,16 +75,16 @@ dmnsn_pigment_map_pigment_fn(const dmnsn_pigment *pigment, dmnsn_vector v)
dmnsn_pigment *pigment1, *pigment2;
dmnsn_map_evaluate(payload->map, dmnsn_pattern_value(payload->pattern, v),
&n, &pigment1, &pigment2);
- dmnsn_color color1 = dmnsn_pigment_evaluate(pigment1, v);
- dmnsn_color color2 = dmnsn_pigment_evaluate(pigment2, v);
+ dmnsn_tcolor color1 = dmnsn_pigment_evaluate(pigment1, v);
+ dmnsn_tcolor color2 = dmnsn_pigment_evaluate(pigment2, v);
if (payload->flags == DMNSN_PIGMENT_MAP_SRGB) {
- color1 = dmnsn_color_to_sRGB(color1);
- color2 = dmnsn_color_to_sRGB(color2);
+ color1.c = dmnsn_color_to_sRGB(color1.c);
+ color2.c = dmnsn_color_to_sRGB(color2.c);
}
- dmnsn_color ret = dmnsn_color_gradient(color1, color2, n);
+ dmnsn_tcolor ret = dmnsn_tcolor_gradient(color1, color2, n);
if (payload->flags == DMNSN_PIGMENT_MAP_SRGB) {
- ret = dmnsn_color_from_sRGB(ret);
+ ret.c = dmnsn_color_from_sRGB(ret.c);
}
return ret;
diff --git a/libdimension/png.c b/libdimension/png.c
index 7631804..62f0fcd 100644
--- a/libdimension/png.c
+++ b/libdimension/png.c
@@ -201,16 +201,16 @@ dmnsn_png_write_canvas_thread(void *ptr)
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
/* Invert the rows. PNG coordinates are fourth quadrant. */
- dmnsn_color color = dmnsn_canvas_get_pixel(payload->canvas,
- x, height - y - 1);
- color = dmnsn_remove_filter(color);
- color = dmnsn_color_to_sRGB(color);
- color = dmnsn_color_saturate(color);
-
- row[4*x] = lround(color.R*UINT16_MAX);
- row[4*x + 1] = lround(color.G*UINT16_MAX);
- row[4*x + 2] = lround(color.B*UINT16_MAX);
- row[4*x + 3] = lround(color.trans*UINT16_MAX);
+ dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(payload->canvas,
+ x, height - y - 1);
+ tcolor = dmnsn_tcolor_remove_filter(tcolor);
+ tcolor.c = dmnsn_color_to_sRGB(tcolor.c);
+ tcolor = dmnsn_tcolor_saturate(tcolor);
+
+ row[4*x] = lround(tcolor.c.R*UINT16_MAX);
+ row[4*x + 1] = lround(tcolor.c.G*UINT16_MAX);
+ row[4*x + 2] = lround(tcolor.c.B*UINT16_MAX);
+ row[4*x + 3] = lround(tcolor.T*UINT16_MAX);
}
/* Write the row */
@@ -350,41 +350,41 @@ dmnsn_png_read_canvas_thread(void *ptr)
of an alpha channel. */
for (size_t y = 0; y < height; ++y) {
for (size_t x = 0; x < width; ++x) {
- dmnsn_color color;
- color.filter = 0.0;
+ dmnsn_tcolor tcolor;
+ tcolor.F = 0.0;
if (color_type & PNG_COLOR_MASK_ALPHA) {
if (bit_depth == 16) {
png_bytep png_pixel = image + 8*(y*width + x);
- color.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX;
- color.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX;
- color.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX;
- color.trans = (double)((png_pixel[6] << 8) + png_pixel[7])/UINT16_MAX;
+ tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX;
+ tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX;
+ tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX;
+ tcolor.T = (double)((png_pixel[6] << 8) + png_pixel[7])/UINT16_MAX;
} else {
png_bytep png_pixel = image + 4*(y*width + x);
- color.R = (double)png_pixel[0]/UINT8_MAX;
- color.G = (double)png_pixel[1]/UINT8_MAX;
- color.B = (double)png_pixel[2]/UINT8_MAX;
- color.trans = (double)png_pixel[3]/UINT8_MAX;
+ tcolor.c.R = (double)png_pixel[0]/UINT8_MAX;
+ tcolor.c.G = (double)png_pixel[1]/UINT8_MAX;
+ tcolor.c.B = (double)png_pixel[2]/UINT8_MAX;
+ tcolor.T = (double)png_pixel[3]/UINT8_MAX;
}
} else {
- color.trans = 0.0;
+ tcolor.T = 0.0;
if (bit_depth == 16) {
png_bytep png_pixel = image + 6*(y*width + x);
- color.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX;
- color.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX;
- color.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX;
+ tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX;
+ tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX;
+ tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX;
} else {
png_bytep png_pixel = image + 3*(y*width + x);
- color.R = (double)png_pixel[0]/UINT8_MAX;
- color.G = (double)png_pixel[1]/UINT8_MAX;
- color.B = (double)png_pixel[2]/UINT8_MAX;
+ tcolor.c.R = (double)png_pixel[0]/UINT8_MAX;
+ tcolor.c.G = (double)png_pixel[1]/UINT8_MAX;
+ tcolor.c.B = (double)png_pixel[2]/UINT8_MAX;
}
}
- color = dmnsn_color_from_sRGB(color);
- dmnsn_canvas_set_pixel(*payload->canvas, x, height - y - 1, color);
+ tcolor.c = dmnsn_color_from_sRGB(tcolor.c);
+ dmnsn_canvas_set_pixel(*payload->canvas, x, height - y - 1, tcolor);
}
dmnsn_future_increment(payload->future);
diff --git a/libdimension/ray_trace.c b/libdimension/ray_trace.c
index dd84a26..127eb1e 100644
--- a/libdimension/ray_trace.c
+++ b/libdimension/ray_trace.c
@@ -118,11 +118,13 @@ typedef struct dmnsn_rtstate {
dmnsn_vector pigment_r;
dmnsn_vector viewer;
dmnsn_vector reflected;
- dmnsn_vector light;
- dmnsn_color pigment;
- dmnsn_color diffuse;
- dmnsn_color additional;
+ bool is_shadow_ray;
+ dmnsn_vector light_ray;
+ dmnsn_color light_color;
+
+ dmnsn_tcolor pigment;
+ dmnsn_tcolor color;
double ior;
@@ -132,34 +134,9 @@ typedef struct dmnsn_rtstate {
/** Compute a ray-tracing state from an intersection. */
static inline void
dmnsn_rtstate_initialize(dmnsn_rtstate *state,
- const dmnsn_intersection *intersection)
-{
- state->intersection = intersection;
- state->texture = intersection->object->texture;
- state->interior = intersection->object->interior;
-
- state->r = dmnsn_line_point(intersection->ray, intersection->t);
- state->pigment_r = dmnsn_transform_point(
- intersection->object->pigment_trans,
- state->r
- );
- state->viewer = dmnsn_vector_normalized(
- dmnsn_vector_negate(intersection->ray.n)
- );
- state->reflected = dmnsn_vector_sub(
- dmnsn_vector_mul(
- 2*dmnsn_vector_dot(state->viewer, intersection->normal),
- intersection->normal),
- state->viewer
- );
-
- state->pigment = dmnsn_black;
- state->diffuse = dmnsn_black;
- state->additional = dmnsn_black;
-}
-
-/** Main helper for dmnsn_ray_trace_scene_impl - shoot a ray. */
-static dmnsn_color dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray);
+ const dmnsn_intersection *intersection);
+/** Main helper for dmnsn_ray_trace_scene_concurrent - shoot a ray. */
+static dmnsn_tcolor dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray);
/* Actually ray-trace a scene */
static int
@@ -191,9 +168,8 @@ dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread,
state.reclevel = scene->reclimit;
state.ior = 1.0;
state.adc_value = dmnsn_white;
- dmnsn_color color = dmnsn_ray_shoot(&state, ray);
-
- dmnsn_canvas_set_pixel(scene->canvas, x, y, color);
+ dmnsn_tcolor tcolor = dmnsn_ray_shoot(&state, ray);
+ dmnsn_canvas_set_pixel(scene->canvas, x, y, tcolor);
}
dmnsn_future_increment(future);
@@ -202,19 +178,93 @@ dmnsn_ray_trace_scene_concurrent(void *ptr, unsigned int thread,
return 0;
}
+/* Compute rtstate fields */
+static inline void
+dmnsn_rtstate_initialize(dmnsn_rtstate *state,
+ const dmnsn_intersection *intersection)
+{
+ state->intersection = intersection;
+ state->texture = intersection->object->texture;
+ state->interior = intersection->object->interior;
+
+ state->r = dmnsn_line_point(intersection->ray, intersection->t);
+ state->pigment_r = dmnsn_transform_point(
+ intersection->object->pigment_trans,
+ state->r
+ );
+ state->viewer = dmnsn_vector_normalized(
+ dmnsn_vector_negate(intersection->ray.n)
+ );
+ state->reflected = dmnsn_vector_sub(
+ dmnsn_vector_mul(
+ 2*dmnsn_vector_dot(state->viewer, intersection->normal),
+ intersection->normal),
+ state->viewer
+ );
+
+ state->is_shadow_ray = false;
+}
+
/** Calculate the background color. */
-static dmnsn_color
-dmnsn_trace_background(const dmnsn_rtstate *state, dmnsn_line ray)
+static void dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_line ray);
+/** Calculate the base pigment at the intersection. */
+static void dmnsn_trace_pigment(dmnsn_rtstate *state);
+/** Handle light, shadow, and shading. */
+static void dmnsn_trace_lighting(dmnsn_rtstate *state);
+/** Trace a reflected ray. */
+static void dmnsn_trace_reflection(dmnsn_rtstate *state);
+/** Trace a transmitted ray. */
+static void dmnsn_trace_transparency(dmnsn_rtstate *state);
+
+/* Shoot a ray, and calculate the color */
+static dmnsn_tcolor
+dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray)
+{
+ if (state->reclevel == 0
+ || dmnsn_color_intensity(state->adc_value) < state->scene->adc_bailout)
+ {
+ return DMNSN_TCOLOR(dmnsn_black);
+ }
+
+ --state->reclevel;
+
+ dmnsn_intersection intersection;
+ bool reset = state->reclevel == state->scene->reclimit - 1;
+ dmnsn_prtree_intersection(state->prtree, ray, &intersection, reset);
+ if (dmnsn_prtree_intersection(state->prtree, ray, &intersection, reset)) {
+ /* Found an intersection */
+ dmnsn_rtstate_initialize(state, &intersection);
+
+ dmnsn_trace_pigment(state);
+ if (state->scene->quality & DMNSN_RENDER_LIGHTS) {
+ dmnsn_trace_lighting(state);
+ }
+ if (state->scene->quality & DMNSN_RENDER_REFLECTION) {
+ dmnsn_trace_reflection(state);
+ }
+ if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) {
+ dmnsn_trace_transparency(state);
+ }
+ } else {
+ /* No intersection, return the background color */
+ dmnsn_trace_background(state, ray);
+ }
+
+ return state->color;
+}
+
+static void
+dmnsn_trace_background(dmnsn_rtstate *state, dmnsn_line ray)
{
dmnsn_pigment *background = state->scene->background;
if (state->scene->quality & DMNSN_RENDER_PIGMENT) {
- return dmnsn_pigment_evaluate(background, dmnsn_vector_normalized(ray.n));
+ dmnsn_vector r = dmnsn_vector_normalized(ray.n);
+ state->color = dmnsn_pigment_evaluate(background, r);
} else {
- return background->quick_color;
+ state->color = background->quick_color;
}
}
-/** Calculate the base pigment at the intersection. */
static void
dmnsn_trace_pigment(dmnsn_rtstate *state)
{
@@ -224,143 +274,185 @@ dmnsn_trace_pigment(dmnsn_rtstate *state)
} else {
state->pigment = pigment->quick_color;
}
+ state->color = state->pigment;
+}
+
+/** Determine the amount of specular highlight. */
+static inline dmnsn_color
+dmnsn_evaluate_specular(const dmnsn_rtstate *state)
+{
+ const dmnsn_finish *finish = &state->texture->finish;
+ if (finish->specular) {
+ return finish->specular->specular_fn(
+ finish->specular, state->light_color, state->pigment.c,
+ state->light_ray, state->intersection->normal, state->viewer
+ );
+ } else {
+ return dmnsn_black;
+ }
+}
- state->diffuse = state->pigment;
+/** Determine the amount of reflected light. */
+static inline dmnsn_color
+dmnsn_evaluate_reflection(const dmnsn_rtstate *state,
+ dmnsn_color light, dmnsn_vector direction)
+{
+ const dmnsn_reflection *reflection = state->texture->finish.reflection;
+ if (reflection && (state->scene->quality & DMNSN_RENDER_REFLECTION)) {
+ return reflection->reflection_fn(
+ reflection, light, state->pigment.c, direction,
+ state->intersection->normal
+ );
+ } else {
+ return dmnsn_black;
+ }
+}
+
+/** Determine the amount of transmitted light. */
+static inline dmnsn_color
+dmnsn_evaluate_transparency(const dmnsn_rtstate *state, dmnsn_color light)
+{
+ if (state->pigment.T >= dmnsn_epsilon
+ && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY))
+ {
+ return dmnsn_tcolor_filter(light, state->pigment);
+ } else {
+ return dmnsn_black;
+ }
+}
+
+/** Get a light's diffuse contribution to the object */
+static inline dmnsn_color
+dmnsn_evaluate_diffuse(const dmnsn_rtstate *state)
+{
+ const dmnsn_finish *finish = &state->texture->finish;
+ if (finish->diffuse) {
+ return finish->diffuse->diffuse_fn(
+ finish->diffuse, state->light_color, state->pigment.c,
+ state->light_ray, state->intersection->normal
+ );
+ } else {
+ return dmnsn_black;
+ }
}
/** Get the color of a light ray at an intersection point. */
static bool
-dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light,
- dmnsn_color *color)
+dmnsn_trace_light_ray(dmnsn_rtstate *state, const dmnsn_light *light)
{
- /** @todo: Start at the light source */
dmnsn_line shadow_ray = dmnsn_new_line(
state->r,
light->direction_fn(light, state->r)
);
- state->light = dmnsn_vector_normalized(shadow_ray.n);
/* Add epsilon to avoid hitting ourselves with the shadow ray */
shadow_ray = dmnsn_line_add_epsilon(shadow_ray);
/* Check if we're casting a shadow on ourself */
- if (dmnsn_vector_dot(shadow_ray.n, state->intersection->normal)
- * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0)
+ if ((dmnsn_vector_dot(shadow_ray.n, state->intersection->normal)
+ * dmnsn_vector_dot(state->viewer, state->intersection->normal) < 0.0)
+ && (!state->is_shadow_ray || state->pigment.T < dmnsn_epsilon))
{
return false;
}
- *color = light->illumination_fn(light, state->r);
+ state->light_ray = dmnsn_vector_normalized(shadow_ray.n);
+ state->light_color = light->illumination_fn(light, state->r);
/* Test for shadow ray intersections */
- unsigned int reclevel = state->reclevel;
- while (reclevel-- > 0
- && dmnsn_color_intensity(*color) >= state->scene->adc_bailout)
- {
- dmnsn_intersection shadow_caster;
- bool shadow_was_cast = dmnsn_prtree_intersection(state->prtree, shadow_ray,
- &shadow_caster, false);
-
- if (!shadow_was_cast || !light->shadow_fn(light, shadow_caster.t)) {
- return true;
- }
+ dmnsn_intersection shadow_caster;
+ bool in_shadow = dmnsn_prtree_intersection(state->prtree, shadow_ray,
+ &shadow_caster, false);
+ if (!in_shadow || !light->shadow_fn(light, shadow_caster.t)) {
+ return true;
+ }
- /* Handle transparency */
- if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) {
- dmnsn_rtstate shadow_state = *state;
- dmnsn_rtstate_initialize(&shadow_state, &shadow_caster);
- dmnsn_trace_pigment(&shadow_state);
-
- if (shadow_state.pigment.trans >= dmnsn_epsilon) {
- /* Reflect the light */
- const dmnsn_reflection *reflection =
- shadow_state.texture->finish.reflection;
- if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) {
- dmnsn_color reflected = reflection->reflection_fn(
- reflection, *color, shadow_state.pigment, shadow_state.reflected,
- shadow_state.intersection->normal
- );
- *color = dmnsn_color_sub(*color, reflected);
- }
-
- /* Filter the light */
- *color = dmnsn_filter_light(*color, shadow_state.pigment);
- shadow_ray.x0 = dmnsn_line_point(shadow_ray, shadow_caster.t);
- shadow_ray.n = light->direction_fn(light, shadow_ray.x0);
- shadow_ray = dmnsn_line_add_epsilon(shadow_ray);
- continue;
+ if (state->reclevel > 0
+ && dmnsn_color_intensity(state->adc_value) >= state->scene->adc_bailout
+ && (state->scene->quality & DMNSN_RENDER_TRANSPARENCY)) {
+ dmnsn_rtstate shadow_state = *state;
+ dmnsn_rtstate_initialize(&shadow_state, &shadow_caster);
+ dmnsn_trace_pigment(&shadow_state);
+
+ if (shadow_state.pigment.T >= dmnsn_epsilon) {
+ --shadow_state.reclevel;
+ shadow_state.adc_value = dmnsn_evaluate_transparency(
+ &shadow_state, shadow_state.adc_value
+ );
+ shadow_state.is_shadow_ray = true;
+ if (dmnsn_trace_light_ray(&shadow_state, light)) {
+ state->light_color = shadow_state.light_color;
+
+ /* Handle reflection */
+ dmnsn_color reflected = dmnsn_evaluate_reflection(
+ &shadow_state, state->light_color, state->light_ray
+ );
+ state->light_color = dmnsn_color_sub(state->light_color, reflected);
+
+ /* Handle transparency */
+ state->light_color = dmnsn_evaluate_transparency(
+ &shadow_state, state->light_color
+ );
+
+ return true;
}
}
-
- break;
}
return false;
}
-/** Handle light, shadow, and shading. */
static void
dmnsn_trace_lighting(dmnsn_rtstate *state)
{
- /* The ambient color */
- state->diffuse = dmnsn_black;
-
+ /* Calculate the ambient color */
+ state->color = DMNSN_TCOLOR(dmnsn_black);
const dmnsn_finish *finish = &state->texture->finish;
if (finish->ambient) {
- state->diffuse =
- finish->ambient->ambient_fn(finish->ambient, state->pigment);
+ dmnsn_color ambient = finish->ambient->ambient;
+
+ /* Handle reflection and transmittance of the ambient light */
+ dmnsn_color reflected = dmnsn_evaluate_reflection(
+ state, ambient, state->intersection->normal
+ );
+ ambient = dmnsn_color_sub(ambient, reflected);
+ dmnsn_color transmitted = dmnsn_evaluate_transparency(state, ambient);
+ ambient = dmnsn_color_sub(ambient, transmitted);
+
+ state->color.c = dmnsn_color_illuminate(ambient, state->pigment.c);
}
/* Iterate over each light */
DMNSN_ARRAY_FOREACH (dmnsn_light **, light, state->scene->lights) {
- dmnsn_color light_color;
- if (dmnsn_trace_light_ray(state, *light, &light_color)) {
+ if (dmnsn_trace_light_ray(state, *light)) {
if (state->scene->quality & DMNSN_RENDER_FINISH) {
- /* Get this light's specular contribution to the object */
- dmnsn_color specular = dmnsn_black;
- if (finish->specular) {
- specular = finish->specular->specular_fn(
- finish->specular, light_color, state->pigment, state->light,
- state->intersection->normal, state->viewer
- );
- light_color = dmnsn_color_sub(light_color, specular);
- }
-
- /* Reflect the light */
- const dmnsn_reflection *reflection = state->texture->finish.reflection;
- if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) {
- dmnsn_color reflected = reflection->reflection_fn(
- reflection, light_color, state->pigment, state->reflected,
- state->intersection->normal
- );
- light_color = dmnsn_color_sub(light_color, reflected);
- }
-
- /* Get this light's diffuse contribution to the object */
- dmnsn_color diffuse = dmnsn_black;
- if (finish->diffuse) {
- diffuse = finish->diffuse->diffuse_fn(
- finish->diffuse, light_color, state->pigment, state->light,
- state->intersection->normal
- );
- }
-
- state->diffuse = dmnsn_color_add(diffuse, state->diffuse);
- state->additional = dmnsn_color_add(specular, state->additional);
+ dmnsn_color specular = dmnsn_evaluate_specular(state);
+ state->light_color = dmnsn_color_sub(state->light_color, specular);
+
+ dmnsn_color reflected = dmnsn_evaluate_reflection(
+ state, state->light_color, state->reflected
+ );
+ state->light_color = dmnsn_color_sub(state->light_color, reflected);
+
+ dmnsn_color transmitted = dmnsn_evaluate_transparency(
+ state, state->light_color
+ );
+ state->light_color = dmnsn_color_sub(state->light_color, transmitted);
+
+ dmnsn_color diffuse = dmnsn_evaluate_diffuse(state);
+
+ state->color.c = dmnsn_color_add(state->color.c, specular);
+ state->color.c = dmnsn_color_add(state->color.c, diffuse);
} else {
- state->diffuse = state->pigment;
- state->diffuse.trans = 0.0;
- state->diffuse.filter = 0.0;
+ state->color.c = state->pigment.c;
+ break;
}
}
}
}
-/** Trace a reflected ray. */
-static dmnsn_color
-dmnsn_trace_reflection(const dmnsn_rtstate *state)
+static void
+dmnsn_trace_reflection(dmnsn_rtstate *state)
{
- dmnsn_color reflected = dmnsn_black;
-
const dmnsn_reflection *reflection = state->texture->finish.reflection;
if (reflection) {
dmnsn_line refl_ray = dmnsn_new_line(state->r, state->reflected);
@@ -369,29 +461,26 @@ dmnsn_trace_reflection(const dmnsn_rtstate *state)
dmnsn_rtstate recursive_state = *state;
/* Calculate ADC value */
- recursive_state.adc_value = reflection->reflection_fn(
- reflection, state->adc_value, state->pigment, state->reflected,
- state->intersection->normal
+ recursive_state.adc_value = dmnsn_evaluate_reflection(
+ state, state->adc_value, state->reflected
);
/* Shoot the reflected ray */
- dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray);
- reflected = reflection->reflection_fn(
- reflection, rec, state->pigment, state->reflected,
- state->intersection->normal
+ dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, refl_ray).c;
+ dmnsn_color reflected = dmnsn_evaluate_reflection(
+ state, rec, state->reflected
);
- reflected.trans = 0.0;
- reflected.filter = 0.0;
- }
- return reflected;
+ state->color.c = dmnsn_color_add(state->color.c, reflected);
+ }
}
-/** Handle transparency - must be called last to work correctly. */
static void
dmnsn_trace_transparency(dmnsn_rtstate *state)
{
- if (state->pigment.trans >= dmnsn_epsilon) {
+ if (state->pigment.T >= dmnsn_epsilon) {
+ const dmnsn_interior *interior = state->interior;
+
dmnsn_line trans_ray = dmnsn_new_line(state->r, state->intersection->ray.n);
trans_ray = dmnsn_line_add_epsilon(trans_ray);
@@ -403,7 +492,7 @@ dmnsn_trace_transparency(dmnsn_rtstate *state)
/* Calculate new refractive index */
if (dmnsn_vector_dot(r, n) < 0.0) {
/* We are entering an object */
- recursive_state.ior = state->interior->ior;
+ recursive_state.ior = interior->ior;
recursive_state.parent = state;
} else {
/* We are leaving an object */
@@ -433,71 +522,26 @@ dmnsn_trace_transparency(dmnsn_rtstate *state)
}
/* Calculate ADC value */
- recursive_state.adc_value =
- dmnsn_filter_light(state->adc_value, state->pigment);
+ recursive_state.adc_value = dmnsn_evaluate_transparency(
+ state, state->adc_value
+ );
+ dmnsn_color adc_reflected = dmnsn_evaluate_reflection(
+ state, recursive_state.adc_value, state->reflected
+ );
+ recursive_state.adc_value = dmnsn_color_sub(
+ recursive_state.adc_value, adc_reflected
+ );
/* Shoot the transmitted ray */
- dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray);
- dmnsn_color filtered = dmnsn_filter_light(rec, state->pigment);
+ dmnsn_color rec = dmnsn_ray_shoot(&recursive_state, trans_ray).c;
+ dmnsn_color filtered = dmnsn_evaluate_transparency(state, rec);
/* Conserve energy */
- const dmnsn_reflection *reflection = state->texture->finish.reflection;
- if ((state->scene->quality & DMNSN_RENDER_REFLECTION) && reflection) {
- dmnsn_color reflected = reflection->reflection_fn(
- reflection, filtered, state->pigment, state->reflected,
- state->intersection->normal
- );
- filtered = dmnsn_color_sub(filtered, reflected);
- }
-
- state->diffuse.filter = state->pigment.filter;
- state->diffuse.trans = state->pigment.trans;
- state->diffuse = dmnsn_apply_transparency(filtered, state->diffuse);
- }
-}
-
-/* Shoot a ray, and calculate the color */
-static dmnsn_color
-dmnsn_ray_shoot(dmnsn_rtstate *state, dmnsn_line ray)
-{
- if (state->reclevel == 0
- || dmnsn_color_intensity(state->adc_value) < state->scene->adc_bailout)
- {
- return dmnsn_black;
- }
-
- --state->reclevel;
-
- dmnsn_intersection intersection;
- bool reset = state->reclevel == state->scene->reclimit - 1;
- if (dmnsn_prtree_intersection(state->prtree, ray, &intersection, reset)) {
- /* Found an intersection */
- dmnsn_rtstate_initialize(state, &intersection);
-
- /* Pigment */
- dmnsn_trace_pigment(state);
-
- /* Finishes and shadows */
- if (state->scene->quality & DMNSN_RENDER_LIGHTS) {
- dmnsn_trace_lighting(state);
- }
-
- /* Reflection */
- if (state->scene->quality & DMNSN_RENDER_REFLECTION) {
- state->additional = dmnsn_color_add(
- dmnsn_trace_reflection(state),
- state->additional
- );
- }
-
- /* Transparency */
- if (state->scene->quality & DMNSN_RENDER_TRANSPARENCY) {
- dmnsn_trace_transparency(state);
- }
+ dmnsn_color reflected = dmnsn_evaluate_reflection(
+ state, filtered, state->reflected
+ );
+ filtered = dmnsn_color_sub(filtered, reflected);
- return dmnsn_color_add(state->diffuse, state->additional);
- } else {
- /* No intersection, return the background color */
- return dmnsn_trace_background(state, ray);
+ state->color.c = dmnsn_color_add(state->color.c, filtered);
}
}
diff --git a/libdimension/rgba16.c b/libdimension/rgba16.c
index 980a2d3..2c7a8d5 100644
--- a/libdimension/rgba16.c
+++ b/libdimension/rgba16.c
@@ -50,14 +50,13 @@ dmnsn_rgba16_optimizer_fn(const dmnsn_canvas *canvas,
dmnsn_canvas_optimizer optimizer, size_t x, size_t y)
{
uint16_t *pixel = (uint16_t *)optimizer.ptr + 4*(y*canvas->width + x);
- dmnsn_color color;
- color = dmnsn_canvas_get_pixel(canvas, x, y);
- color = dmnsn_remove_filter(color);
- color = dmnsn_color_to_sRGB(color);
- color = dmnsn_color_saturate(color);
+ dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y);
+ tcolor = dmnsn_tcolor_remove_filter(tcolor);
+ tcolor.c = dmnsn_color_to_sRGB(tcolor.c);
+ tcolor = dmnsn_tcolor_saturate(tcolor);
- pixel[0] = lround(color.R*UINT16_MAX);
- pixel[1] = lround(color.G*UINT16_MAX);
- pixel[2] = lround(color.B*UINT16_MAX);
- pixel[3] = lround(color.trans*UINT16_MAX);
+ pixel[0] = lround(tcolor.c.R*UINT16_MAX);
+ pixel[1] = lround(tcolor.c.G*UINT16_MAX);
+ pixel[2] = lround(tcolor.c.B*UINT16_MAX);
+ pixel[3] = lround(tcolor.T*UINT16_MAX);
}
diff --git a/libdimension/solid_pigment.c b/libdimension/solid_pigment.c
index 1dbda2d..7f91819 100644
--- a/libdimension/solid_pigment.c
+++ b/libdimension/solid_pigment.c
@@ -28,7 +28,7 @@
/* Create a solid color */
dmnsn_pigment *
-dmnsn_new_solid_pigment(dmnsn_color color)
+dmnsn_new_solid_pigment(dmnsn_tcolor color)
{
dmnsn_pigment *pigment = dmnsn_new_pigment();
pigment->quick_color = color;
diff --git a/libdimension/tests/render.c b/libdimension/tests/render.c
index abd88d6..41b19b4 100644
--- a/libdimension/tests/render.c
+++ b/libdimension/tests/render.c
@@ -26,9 +26,10 @@ static void
dmnsn_test_scene_set_defaults(dmnsn_scene *scene)
{
/* Default texture */
- scene->default_texture->pigment = dmnsn_new_solid_pigment(dmnsn_black);
+ scene->default_texture->pigment =
+ dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_black));
dmnsn_finish *default_finish = &scene->default_texture->finish;
- default_finish->ambient = dmnsn_new_basic_ambient(
+ default_finish->ambient = dmnsn_new_ambient(
dmnsn_color_from_sRGB(dmnsn_color_mul(0.1, dmnsn_white))
);
default_finish->diffuse = dmnsn_new_lambertian(dmnsn_sRGB_inverse_gamma(0.7));
@@ -88,14 +89,16 @@ dmnsn_test_scene_add_background(dmnsn_scene *scene)
} else {
/* Loading png2.png failed, possibly compiled with --disable-png */
fprintf(stderr, "--- WARNING: Couldn't open or read png2.png! ---\n");
- png_pigment = dmnsn_new_solid_pigment(dmnsn_orange);
+ png_pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_orange));
}
dmnsn_map_add_entry(sky_gradient_pigment_map, 0.0, &png_pigment);
dmnsn_color background = dmnsn_color_from_sRGB(
- dmnsn_new_color5(0.0, 0.1, 0.2, 0.1, 0.0)
+ dmnsn_new_color(0.0, 0.1, 0.2)
);
- dmnsn_pigment_map_add_color(sky_gradient_pigment_map, 0.35, background);
+ dmnsn_tcolor tbackground = dmnsn_new_tcolor(background, 0.1, 0.0);
+ dmnsn_pigment *bkgpigment = dmnsn_new_solid_pigment(tbackground);
+ dmnsn_map_add_entry(sky_gradient_pigment_map, 0.35, &bkgpigment);
scene->background =
dmnsn_new_pigment_map_pigment(sky_gradient, sky_gradient_pigment_map,
@@ -120,10 +123,8 @@ dmnsn_test_scene_add_hollow_cube(dmnsn_scene *scene)
dmnsn_new_vector(dmnsn_radians(45.0), 0.0, 0.0)
);
- dmnsn_color cube_color = dmnsn_blue;
- cube_color.trans = 0.75;
- cube_color.filter = 1.0/3.0;
cube->texture = dmnsn_new_texture();
+ dmnsn_tcolor cube_color = dmnsn_new_tcolor(dmnsn_blue, 0.75, 1.0/3.0);
cube->texture->pigment = dmnsn_new_solid_pigment(cube_color);
dmnsn_color reflect =
@@ -136,7 +137,7 @@ dmnsn_test_scene_add_hollow_cube(dmnsn_scene *scene)
dmnsn_object *sphere = dmnsn_new_sphere();
sphere->texture = dmnsn_new_texture();
- sphere->texture->pigment = dmnsn_new_solid_pigment(dmnsn_green);
+ sphere->texture->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_green));
sphere->texture->finish.specular =
dmnsn_new_phong(dmnsn_sRGB_inverse_gamma(0.2), 40.0);
sphere->trans = dmnsn_scale_matrix(dmnsn_new_vector(1.25, 1.25, 1.25));
@@ -145,6 +146,13 @@ dmnsn_test_scene_add_hollow_cube(dmnsn_scene *scene)
dmnsn_array_push(scene->objects, &hollow_cube);
}
+#define dmnsn_pigment_map_add_color(map, n, color) \
+ do { \
+ dmnsn_tcolor tcolor = DMNSN_TCOLOR(color); \
+ dmnsn_pigment *pigment = dmnsn_new_solid_pigment(tcolor); \
+ dmnsn_map_add_entry(map, n, &pigment); \
+ } while (0)
+
static void
dmnsn_test_scene_add_spike(dmnsn_scene *scene)
{
@@ -155,11 +163,10 @@ dmnsn_test_scene_add_spike(dmnsn_scene *scene)
dmnsn_array_push(arrow_array, &cylinder);
dmnsn_object *cone = dmnsn_new_cone(0.1, 0.0, true);
- cone->trans =
- dmnsn_matrix_mul(
- dmnsn_translation_matrix(dmnsn_new_vector(0.0, 1.375, 0.0)),
- dmnsn_scale_matrix(dmnsn_new_vector(1.0, 0.125, 1.0))
- );
+ cone->trans = dmnsn_matrix_mul(
+ dmnsn_translation_matrix(dmnsn_new_vector(0.0, 1.375, 0.0)),
+ dmnsn_scale_matrix(dmnsn_new_vector(1.0, 0.125, 1.0))
+ );
dmnsn_array_push(arrow_array, &cone);
dmnsn_object *arrow = dmnsn_new_csg_union(arrow_array);
@@ -200,9 +207,8 @@ dmnsn_test_scene_add_spike(dmnsn_scene *scene)
dmnsn_object *torii = dmnsn_new_csg_union(torus_array);
dmnsn_delete_array(torus_array);
torii->texture = dmnsn_new_texture();
- torii->texture->pigment = dmnsn_new_solid_pigment(dmnsn_blue);
- torii->texture->finish.ambient
- = dmnsn_new_basic_ambient(dmnsn_white);
+ torii->texture->pigment = dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_blue));
+ torii->texture->finish.ambient = dmnsn_new_ambient(dmnsn_white);
dmnsn_array *spike_array = dmnsn_new_array(sizeof(dmnsn_object *));
dmnsn_array_push(spike_array, &arrow);
@@ -227,9 +233,12 @@ dmnsn_test_scene_add_triangle_strip(dmnsn_scene *scene)
dmnsn_new_texture(),
dmnsn_new_texture(),
};
- strip_textures[0]->pigment = dmnsn_new_solid_pigment(dmnsn_red);
- strip_textures[1]->pigment = dmnsn_new_solid_pigment(dmnsn_orange);
- strip_textures[2]->pigment = dmnsn_new_solid_pigment(dmnsn_yellow);
+ strip_textures[0]->pigment =
+ dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_red));
+ strip_textures[1]->pigment =
+ dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_orange));
+ strip_textures[2]->pigment =
+ dmnsn_new_solid_pigment(DMNSN_TCOLOR(dmnsn_yellow));
for (unsigned int i = 0; i < 128; ++i) {
dmnsn_object *triangle = dmnsn_new_triangle(a, b, c);
triangle->texture = strip_textures[i%3];
@@ -271,8 +280,10 @@ dmnsn_test_scene_add_ground(dmnsn_scene *scene)
plane->texture = dmnsn_new_texture();
plane->texture->pigment =
dmnsn_new_pigment_map_pigment(checker2, big_map, DMNSN_PIGMENT_MAP_REGULAR);
- plane->texture->pigment->quick_color = dmnsn_color_from_sRGB(
- dmnsn_new_color(1.0, 0.5, 0.75)
+ plane->texture->pigment->quick_color = DMNSN_TCOLOR(
+ dmnsn_color_from_sRGB(
+ dmnsn_new_color(1.0, 0.5, 0.75)
+ )
);
dmnsn_array_push(scene->objects, &plane);
}
@@ -337,7 +348,7 @@ main(void)
}
}
- dmnsn_canvas_clear(scene->canvas, dmnsn_black);
+ dmnsn_canvas_clear(scene->canvas, DMNSN_TCOLOR(dmnsn_black));
/* Create a new glX display */
dmnsn_display *display = NULL;
diff --git a/libdimension/tests/test_canvas.c b/libdimension/tests/test_canvas.c
index 783914d..73ca1d9 100644
--- a/libdimension/tests/test_canvas.c
+++ b/libdimension/tests/test_canvas.c
@@ -50,7 +50,7 @@ dmnsn_paint_test_canvas(dmnsn_canvas *canvas)
color = dmnsn_color_from_sRGB(color);
}
- dmnsn_canvas_set_pixel(canvas, x, y, color);
+ dmnsn_canvas_set_pixel(canvas, x, y, DMNSN_TCOLOR(color));
}
}
}