diff options
author | Tavian Barnes <tavianator@gmail.com> | 2011-12-14 19:27:22 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@gmail.com> | 2011-12-14 19:52:36 -0500 |
commit | bfbe9e43e108f6816c17b9b7764b75284ac78313 (patch) | |
tree | 189f85eeec18a76ccb626e45455fa7e45406db7c | |
parent | 7db5342a36341b061a8785a3b349cf0fcad69ebf (diff) | |
download | dimension-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.
28 files changed, 716 insertions, 887 deletions
diff --git a/dimension/tests/demo.dmnsn b/dimension/tests/demo.dmnsn index 75a97a7..2d51400 100644 --- a/dimension/tests/demo.dmnsn +++ b/dimension/tests/demo.dmnsn @@ -31,9 +31,9 @@ except OSError: background = PigmentMap( pattern = Gradient(Y), - map = { + map = { 0: image_map, - 0.35: sRGB(0, 0.1, 0.2, trans = 0.1, filter = 0.0), + 0.35: TColor(sRGB(0, 0.1, 0.2), trans = 0.1) }, ) @@ -50,7 +50,7 @@ hollow_cube = Difference( (-1, -1, -1), (1, 1, 1), texture = Texture( - pigment = sRGB(0, 0, 1, trans = 0.75, filter = 1/3), + pigment = TColor(Blue, trans = 0.75, filter = 1/3), finish = Reflection(min = 0, max = sRGB(0.5)), ), interior = Interior( @@ -63,7 +63,7 @@ hollow_cube = Difference( center = 0, radius = 1.25, texture = Texture( pigment = Green, - finish = Phong(strength = 0.2, size = 40), + finish = Phong(strength = sRGB(0.2), size = 40), ), ) ], diff --git a/libdimension-python/dimension.pxd b/libdimension-python/dimension.pxd index 94966f3..0d0fcc4 100644 --- a/libdimension-python/dimension.pxd +++ b/libdimension-python/dimension.pxd @@ -43,8 +43,7 @@ cdef extern from "../libdimension/dimension.h": # Arrays # ########## - ctypedef struct dmnsn_array: - pass + ctypedef struct dmnsn_array dmnsn_array *dmnsn_new_array(size_t objsize) void dmnsn_delete_array(dmnsn_array *array) @@ -148,12 +147,8 @@ cdef extern from "../libdimension/dimension.h": double R double G double B - double trans - double filter dmnsn_color dmnsn_new_color(double R, double G, double B) - dmnsn_color dmnsn_new_color5(double R, double G, double B, - double trans, double filter) double dmnsn_sRGB_gamma(double Clinear) dmnsn_color dmnsn_color_to_sRGB(dmnsn_color color) @@ -162,11 +157,11 @@ cdef extern from "../libdimension/dimension.h": double dmnsn_color_intensity(dmnsn_color color) dmnsn_color dmnsn_color_add(dmnsn_color color1, dmnsn_color color2) + dmnsn_color dmnsn_color_sub(dmnsn_color color1, dmnsn_color color2) dmnsn_color dmnsn_color_mul(double n, dmnsn_color color) dmnsn_color dmnsn_black dmnsn_color dmnsn_white - dmnsn_color dmnsn_clear dmnsn_color dmnsn_red dmnsn_color dmnsn_green dmnsn_color dmnsn_blue @@ -175,6 +170,18 @@ cdef extern from "../libdimension/dimension.h": dmnsn_color dmnsn_yellow dmnsn_color dmnsn_cyan + ctypedef struct dmnsn_tcolor: + dmnsn_color c + double T + double F + + dmnsn_tcolor dmnsn_new_tcolor(dmnsn_color c, double T, double F) + dmnsn_tcolor DMNSN_TCOLOR(dmnsn_color c) + dmnsn_tcolor dmnsn_new_tcolor5(double R, double G, double B, + double T, double F) + + dmnsn_tcolor dmnsn_clear + ############ # Canvases # ############ @@ -186,11 +193,11 @@ cdef extern from "../libdimension/dimension.h": dmnsn_canvas *dmnsn_new_canvas(size_t width, size_t height) void dmnsn_delete_canvas(dmnsn_canvas *canvas) - dmnsn_color dmnsn_canvas_get_pixel(dmnsn_canvas *canvas, size_t x, size_t y) + dmnsn_tcolor dmnsn_canvas_get_pixel(dmnsn_canvas *canvas, size_t x, size_t y) void dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, - dmnsn_color color) + dmnsn_tcolor tcolor) - void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_color color) + void dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor) int dmnsn_png_optimize_canvas(dmnsn_canvas *canvas) int dmnsn_png_write_canvas(dmnsn_canvas *canvas, FILE *file) @@ -234,7 +241,7 @@ cdef extern from "../libdimension/dimension.h": ctypedef struct dmnsn_pigment: dmnsn_matrix trans - dmnsn_color quick_color + dmnsn_tcolor quick_color ctypedef enum dmnsn_pigment_map_flags: DMNSN_PIGMENT_MAP_REGULAR @@ -242,7 +249,7 @@ cdef extern from "../libdimension/dimension.h": void dmnsn_delete_pigment(dmnsn_pigment *pigment) - dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_color color) + dmnsn_pigment *dmnsn_new_solid_pigment(dmnsn_tcolor tcolor) dmnsn_pigment *dmnsn_new_canvas_pigment(dmnsn_canvas *canvas) dmnsn_pigment *dmnsn_new_pigment_map_pigment(dmnsn_pattern *pattern, dmnsn_map *map, @@ -270,7 +277,7 @@ cdef extern from "../libdimension/dimension.h": void dmnsn_finish_cascade(dmnsn_finish *default_finish, dmnsn_finish *finish) - dmnsn_ambient *dmnsn_new_basic_ambient(dmnsn_color ambient) + dmnsn_ambient *dmnsn_new_ambient(dmnsn_color ambient) dmnsn_diffuse *dmnsn_new_lambertian(double diffuse) dmnsn_specular *dmnsn_new_phong(double specular, double exp) dmnsn_reflection *dmnsn_new_basic_reflection(dmnsn_color min, dmnsn_color max, @@ -332,7 +339,8 @@ cdef extern from "../libdimension/dimension.h": # Lights # ########## - ctypedef struct dmnsn_light + ctypedef struct dmnsn_light: + dmnsn_vector x0 dmnsn_light *dmnsn_new_light() void dmnsn_delete_light(dmnsn_light *light) diff --git a/libdimension-python/dimension.pyx b/libdimension-python/dimension.pyx index 8e48a5c..1e79620 100644 --- a/libdimension-python/dimension.pyx +++ b/libdimension-python/dimension.pyx @@ -408,13 +408,11 @@ cdef class _BaseColor: red -- The red component green -- The green component blue -- The blue component - trans -- The transparency of the color, 0.0 meaning opaque (default 0.0) - filter -- How filtered the transparency is (default 0.0) Alternatively, you can pass another Color, a gray intensity like 0.5, or a - tuple or other sequence (red, green, blue[, trans[, filter]]). + tuple or other sequence (red, green, blue). """ - if len(args) == 1: + if len(args) == 1 and len(kwargs) == 0: if isinstance(args[0], _BaseColor): self._clin = (<_BaseColor>args[0])._clin self._unlinearize() @@ -428,9 +426,8 @@ cdef class _BaseColor: self._linearize() - def _real_init(self, double red, double green, double blue, - double trans = 0.0, double filter = 0.0): - self._c = dmnsn_new_color5(red, green, blue, trans, filter) + def _real_init(self, double red, double green, double blue): + self._c = dmnsn_new_color(red, green, blue) property red: """The red component.""" @@ -444,14 +441,6 @@ cdef class _BaseColor: """The blue component.""" def __get__(self): return self._c.B - property trans: - """The transparency of the color.""" - def __get__(self): - return self._c.trans - property filter: - """How filtered the transparency is.""" - def __get__(self): - return self._c.filter def intensity(self): return dmnsn_color_intensity(self._c) @@ -489,10 +478,7 @@ cdef class _BaseColor: cdef double rdiff = clhs.red - crhs.red cdef double gdiff = clhs.green - crhs.green cdef double bdiff = clhs.blue - crhs.blue - cdef double tdiff = clhs.trans - crhs.trans - cdef double fdiff = clhs.filter - crhs.filter - cdef double sum = rdiff*rdiff + gdiff*gdiff + bdiff*bdiff \ - + tdiff*tdiff + fdiff*fdiff + cdef double sum = rdiff*rdiff + gdiff*gdiff + bdiff*bdiff equal = sqrt(sum) < dmnsn_epsilon if op == 2: # == return equal @@ -502,17 +488,11 @@ cdef class _BaseColor: return NotImplemented def __repr__(self): - return "dimension.%s(%r, %r, %r, %r, %r)" % \ - (type(self).__name__, self.red, self.green, self.blue, self.trans, - self.filter) + return "dimension.%s(%r, %r, %r)" % \ + (type(self).__name__, self.red, self.green, self.blue) def __str__(self): - if self.trans >= dmnsn_epsilon: - return "%s<%s, %s, %s, trans = %s, filter = %s>" % \ - (type(self).__name__, - self.red, self.green, self.blue, self.trans, self.filter) - else: - return "%s<%s, %s, %s>" % \ - (type(self).__name__, self.red, self.green, self.blue) + return "%s<%s, %s, %s>" % \ + (type(self).__name__, self.red, self.green, self.blue) cdef class Color(_BaseColor): """ @@ -551,7 +531,6 @@ cdef _BaseColor _Color(dmnsn_color c, type t): Black = _Color(dmnsn_black, Color) White = _Color(dmnsn_white, Color) -Clear = _Color(dmnsn_clear, Color) Red = _Color(dmnsn_red, Color) Green = _Color(dmnsn_green, Color) Blue = _Color(dmnsn_blue, Color) @@ -560,6 +539,65 @@ Orange = _Color(dmnsn_orange, Color) Yellow = _Color(dmnsn_yellow, Color) Cyan = _Color(dmnsn_cyan, Color) +cdef class TColor: + """ + A transparent color. + + This type is used for representing pigments and pixels, as it carries color as + well as transparency information. + """ + cdef dmnsn_tcolor _tc + + def __init__(self, *args, **kwargs): + """ + Create a transparent color. + + Keyword arguments: + color -- The Color() (or sRGB()) component + trans -- The transparency component + filter -- The proportion of the transparency that is filtered + + Alternatively, you can pass another TColor. + """ + if len(args) == 1 and len(kwargs) == 0: + if isinstance(args[0], TColor): + self._tc = (<TColor>args[0])._tc + else: + self._real_init(*args, **kwargs) + else: + self._real_init(*args, **kwargs) + + def _real_init(self, color, double trans = 0, double filter = 0): + self._tc = dmnsn_new_tcolor(Color(color)._clin, trans, filter) + + property color: + """The color component.""" + def __get__(self): + return _Color(self._tc.c, Color) + property trans: + """The transparency component.""" + def __get__(self): + return self._tc.T + property filter: + """The filter proportion.""" + def __get__(self): + return self._tc.F + + def __repr__(self): + return "dimension.TColor(%r, %r, %r)" % \ + (self.color, self.trans, self.filter) + def __str__(self): + return "TColor<%s, %s, %s>" % \ + (self.color, self.trans, self.filter) + +cdef TColor _TColor(dmnsn_tcolor tc): + """Wrap a TColor around a dmnsn_tcolor.""" + cdef TColor self = TColor.__new__(TColor) + self._tc = tc + return self + +Clear = _TColor(dmnsn_clear) + ############ # Canvases # ############ @@ -612,7 +650,7 @@ cdef class Canvas: def clear(self, c): """Clear a canvas with a solid color.""" - dmnsn_canvas_clear(self._canvas, Color(c)._c) + dmnsn_canvas_clear(self._canvas, TColor(c)._tc) def write_PNG(self, path): """Export the canvas as a PNG file.""" @@ -660,10 +698,10 @@ cdef class _CanvasProxy: return self._canvas.height def __getitem__(self, int y): self._bounds_check(y) - return _Color(dmnsn_canvas_get_pixel(self._canvas, self._x, y), Color) + return _TColor(dmnsn_canvas_get_pixel(self._canvas, self._x, y)) def __setitem__(self, int y, color): self._bounds_check(y) - dmnsn_canvas_set_pixel(self._canvas, self._x, y, Color(color)._c) + dmnsn_canvas_set_pixel(self._canvas, self._x, y, TColor(color)._tc) def _bounds_check(self, int y): if y < 0 or y >= self._canvas.height: @@ -734,9 +772,9 @@ cdef class Pigment(_Transformable): self._pigment = (<Pigment>quick_color)._pigment DMNSN_INCREF(self._pigment) else: - self._pigment = dmnsn_new_solid_pigment(Color(quick_color)._c) + self._pigment = dmnsn_new_solid_pigment(TColor(quick_color)._tc) else: - self._pigment.quick_color = Color(quick_color)._c + self._pigment.quick_color = TColor(quick_color)._tc def __dealloc__(self): dmnsn_delete_pigment(self._pigment) @@ -858,7 +896,7 @@ cdef class Ambient(Finish): Keyword arguments: color -- the color and intensity of the ambient light """ - self._finish.ambient = dmnsn_new_basic_ambient(Color(color)._c) + self._finish.ambient = dmnsn_new_ambient(Color(color)._c) cdef class Diffuse(Finish): """Lambertian diffuse reflection.""" diff --git a/libdimension-python/tests/color.py b/libdimension-python/tests/color.py index 9482545..8bf1836 100755 --- a/libdimension-python/tests/color.py +++ b/libdimension-python/tests/color.py @@ -24,21 +24,15 @@ from dimension import * # Treat warnings as errors for tests die_on_warnings(True) -c = Color(0, 0.5, 1, trans = 0.5, filter = 0.35) -assert repr(c) == "dimension.Color(0.0, 0.5, 1.0, 0.5, 0.35)", repr(c) -assert str(c) == "Color<0.0, 0.5, 1.0, trans = 0.5, filter = 0.35>", str(c) +c = Color(0, 0.5, 1) +assert repr(c) == "dimension.Color(0.0, 0.5, 1.0)", repr(c) +assert str(c) == "Color<0.0, 0.5, 1.0>", str(c) assert c.red == 0, c.red assert c.green == 0.5, c.green assert c.blue == 1, c.blue -assert c.trans == 0.5, c.filter -assert c.filter == 0.35, c.trans - -c = Color(1, 0.5, 0) -assert str(c) == "Color<1.0, 0.5, 0.0>", str(c) assert Black == Color(0, 0, 0), Black assert White == Color(1, 1, 1), White -assert Clear == Color(0, 0, 0, trans = 1), Clear assert Red == Color(1, 0, 0), Red assert Green == Color(0, 1, 0), Green assert Blue == Color(0, 0, 1), Blue 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)); } } } |