Code

Patch from Jean-René Reinhard: support for feDiffuseLighting and
[inkscape.git] / src / display / nr-filter-specularlighting.cpp
1 /*
2  * feSpecularLighting renderer
3  *
4  * Authors:
5  *   Niko Kiirala <niko@kiirala.com>
6  *   Jean-Rene Reinhard <jr@komite.net>
7  *
8  * Copyright (C) 2007 authors
9  *
10  * Released under GNU GPL, read the file 'COPYING' for more information
11  */
13 #include <glib/gmessages.h>
14 #include <cmath>
16 #include "display/nr-3dutils.h"
17 #include "display/nr-arena-item.h"
18 #include "display/nr-filter-specularlighting.h"
19 #include "display/nr-filter-getalpha.h"
20 #include "display/nr-filter-slot.h"
21 #include "display/nr-light.h"
22 #include "libnr/nr-blit.h"
23 #include "libnr/nr-pixblock.h"
24 #include "libnr/nr-matrix.h"
25 #include "libnr/nr-rect-l.h"
26 #include "color.h"
27 #include "round.h"
29 namespace NR {
31 FilterSpecularLighting::FilterSpecularLighting() 
32 {
33     light_type = NO_LIGHT;
34     specularConstant = 1;
35     specularExponent = 1;
36     surfaceScale = 1;
37     lighting_color = 0xffffffff;
38 }
40 FilterPrimitive * FilterSpecularLighting::create() {
41     return new FilterSpecularLighting();
42 }
44 FilterSpecularLighting::~FilterSpecularLighting()
45 {}
47 //Investigating Phong Lighting model we should not take N.H but
48 //R.E which equals to 2*N.H^2 - 1
49 #define COMPUTE_INTER(inter, H, N, ks, speculaExponent) \
50 do {\
51     gdouble scal = scalar_product((N), (H));\
52     scal = 2 * scal * scal - 1;\
53     if (scal <= 0)\
54         (inter) = 0;\
55     else\
56         (inter) = (ks) * std::pow(scal, (specularExponent));\
57 }while(0)
59 int FilterSpecularLighting::render(FilterSlot &slot, Matrix const &trans) {
60     NRPixBlock *in = filter_get_alpha(slot.get(_input));
61     NRPixBlock *out = new NRPixBlock;
63     //Fvector *L = NULL; //vector to the light
64     
65     int w = in->area.x1 - in->area.x0;
66     int h = in->area.y1 - in->area.y0;
67     int x0 = in->area.x0;
68     int y0 = in->area.y0;
69     int i, j;
70     //As long as FilterRes and kernel unit is not supported we hardcode the
71     //default value
72     int dx = 1; //TODO setup
73     int dy = 1; //TODO setup
74     //surface scale
75     //TODO for the time being, assumes userSpaceOnUse
76     gdouble ss = surfaceScale * trans[0];
77     gdouble ks = specularConstant; //diffuse lighting constant
78     Fvector L, N, LC, H;
79     gdouble inter;
80     
81     nr_pixblock_setup_fast(out, in->mode,
82             in->area.x0, in->area.y0, in->area.x1, in->area.y1,
83             true);
84     unsigned char *data_i = NR_PIXBLOCK_PX (in);
85     unsigned char *data_o = NR_PIXBLOCK_PX (out);
86     //No light, nothing to do
87     switch (light_type) {
88         case DISTANT_LIGHT:  
89             //the light vector is constant
90             {
91             DistantLight *dl = new DistantLight(light.distant, lighting_color);
92             dl->light_vector(L);
93             dl->light_components(LC);
94             normalized_sum(H, L, EYE_VECTOR);
95             //finish the work
96             for (i = 0, j = 0; i < w*h; i++) {
97                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
98                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
100                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_RED]);
101                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_GREEN]);
102                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_BLUE]);
103                 data_o[j++] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
104             }
105             out->empty = FALSE;
106             delete dl;
107             }
108             break;
109         case POINT_LIGHT:
110             {
111             PointLight *pl = new PointLight(light.point, lighting_color, trans);
112             pl->light_components(LC);
113         //TODO we need a reference to the filter to determine primitiveUnits
114         //slot._arena_item->filter seems to be ok on simple examples
115         //for now assume userSpaceOnUse
116         //if objectBoundingBox is used, use a different matrix for light_vector
117             //finish the work
118             for (i = 0, j = 0; i < w*h; i++) {
119                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
120                 pl->light_vector(L,
121                         i % w + x0,
122                         i / w + y0,
123                         ss * (double) data_i[4*i+3]/ 255);
124                 normalized_sum(H, L, EYE_VECTOR);
125                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
126                 
127                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_RED]);
128                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_GREEN]);
129                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_BLUE]);
130                 data_o[j++] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
131             }
132             out->empty = FALSE;
133             delete pl;
134             }
135             break;
136         case SPOT_LIGHT:
137             {
138             SpotLight *sl = new SpotLight(light.spot, lighting_color, trans);
139         //TODO we need a reference to the filter to determine primitiveUnits
140         //slot._arena_item->filter seems to be ok on simple examples
141         //for now assume userSpaceOnUse
142         //if objectBoundingBox is used, use a different matrix for light_vector
143             //finish the work
144             for (i = 0, j = 0; i < w*h; i++) {
145                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
146                 sl->light_vector(L,
147                     i % w + x0,
148                     i / w + y0,
149                     ss * (double) data_i[4*i+3]/ 255);
150                 sl->light_components(LC, L);
151                 normalized_sum(H, L, EYE_VECTOR);
152                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
153                 
154                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_RED]);
155                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_GREEN]);
156                 data_o[j++] = (unsigned char) round(inter * LC[LIGHT_BLUE]);
157                 data_o[j++] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
158             }
159             out->empty = FALSE;
160             delete sl;
161             }
162             break;
163         //else unknown light source, doing nothing
164         case NO_LIGHT:
165         default:
166             {
167             if (light_type != NO_LIGHT)
168                 g_warning("unknown light source %d", light_type);
169             out->empty = false;
170             }
171     }
172         
173     //finishing
174     slot.set(_output, out);
175     delete in;
176     return 0;
179 } /* namespace NR */
181 /*
182   Local Variables:
183   mode:c++
184   c-file-style:"stroustrup"
185   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
186   indent-tabs-mode:nil
187   fill-column:99
188   End:
189 */
190 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :