Code

patch by Dennis Lin: optionally for debugging paint the rect-to-redraw yellow
[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-filter-utils.h"
22 #include "display/nr-light.h"
23 #include "libnr/nr-blit.h"
24 #include "libnr/nr-pixblock.h"
25 #include "libnr/nr-matrix.h"
26 #include "libnr/nr-rect-l.h"
27 #include "color.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 //replace the second line by 
50 //gdouble scal = scalar_product((N), (H)); scal = 2 * scal * scal - 1;\
51 //to get the expected formula
52 #define COMPUTE_INTER(inter, H, N, ks, speculaExponent) \
53 do {\
54     gdouble scal = scalar_product((N), (H)); \
55     if (scal <= 0)\
56         (inter) = 0;\
57     else\
58         (inter) = (ks) * std::pow(scal, (specularExponent));\
59 }while(0)
61 int FilterSpecularLighting::render(FilterSlot &slot, Matrix const &trans) {
62     NRPixBlock *in = filter_get_alpha(slot.get(_input));
63     NRPixBlock *out = new NRPixBlock;
65     //Fvector *L = NULL; //vector to the light
66     
67     int w = in->area.x1 - in->area.x0;
68     int h = in->area.y1 - in->area.y0;
69     int x0 = in->area.x0;
70     int y0 = in->area.y0;
71     int i, j;
72     //As long as FilterRes and kernel unit is not supported we hardcode the
73     //default value
74     int dx = 1; //TODO setup
75     int dy = 1; //TODO setup
76     //surface scale
77     //TODO for the time being, assumes userSpaceOnUse
78     gdouble ss = surfaceScale * trans[0];
79     gdouble ks = specularConstant; //diffuse lighting constant
80     Fvector L, N, LC, H;
81     gdouble inter;
82     
83     nr_pixblock_setup_fast(out, in->mode,
84             in->area.x0, in->area.y0, in->area.x1, in->area.y1,
85             true);
86     unsigned char *data_i = NR_PIXBLOCK_PX (in);
87     unsigned char *data_o = NR_PIXBLOCK_PX (out);
88     //No light, nothing to do
89     switch (light_type) {
90         case DISTANT_LIGHT:  
91             //the light vector is constant
92             {
93             DistantLight *dl = new DistantLight(light.distant, lighting_color);
94             dl->light_vector(L);
95             dl->light_components(LC);
96             normalized_sum(H, L, EYE_VECTOR);
97             //finish the work
98             for (i = 0, j = 0; i < w*h; i++) {
99                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
100                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
102                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
103                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
104                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
105                 data_o[j++] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
106             }
107             out->empty = FALSE;
108             delete dl;
109             }
110             break;
111         case POINT_LIGHT:
112             {
113             PointLight *pl = new PointLight(light.point, lighting_color, trans);
114             pl->light_components(LC);
115         //TODO we need a reference to the filter to determine primitiveUnits
116         //slot._arena_item->filter seems to be ok on simple examples
117         //for now assume userSpaceOnUse
118         //if objectBoundingBox is used, use a different matrix for light_vector
119             //finish the work
120             for (i = 0, j = 0; i < w*h; i++) {
121                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
122                 pl->light_vector(L,
123                         i % w + x0,
124                         i / w + y0,
125                         ss * (double) data_i[4*i+3]/ 255);
126                 normalized_sum(H, L, EYE_VECTOR);
127                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
128                 
129                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
130                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
131                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
132                 data_o[j++] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
133             }
134             out->empty = FALSE;
135             delete pl;
136             }
137             break;
138         case SPOT_LIGHT:
139             {
140             SpotLight *sl = new SpotLight(light.spot, lighting_color, trans);
141         //TODO we need a reference to the filter to determine primitiveUnits
142         //slot._arena_item->filter seems to be ok on simple examples
143         //for now assume userSpaceOnUse
144         //if objectBoundingBox is used, use a different matrix for light_vector
145             //finish the work
146             for (i = 0, j = 0; i < w*h; i++) {
147                 compute_surface_normal(N, ss, in, i / w, i % w, dx, dy);
148                 sl->light_vector(L,
149                     i % w + x0,
150                     i / w + y0,
151                     ss * (double) data_i[4*i+3]/ 255);
152                 sl->light_components(LC, L);
153                 normalized_sum(H, L, EYE_VECTOR);
154                 COMPUTE_INTER(inter, N, H, ks, specularExponent);
155                 
156                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_RED]);
157                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_GREEN]);
158                 data_o[j++] = CLAMP_D_TO_U8(inter * LC[LIGHT_BLUE]);
159                 data_o[j++] = MAX(MAX(data_o[j-3], data_o[j-2]), data_o[j-1]);
160             }
161             out->empty = FALSE;
162             delete sl;
163             }
164             break;
165         //else unknown light source, doing nothing
166         case NO_LIGHT:
167         default:
168             {
169             if (light_type != NO_LIGHT)
170                 g_warning("unknown light source %d", light_type);
171             out->empty = false;
172             }
173     }
174         
175     //finishing
176     slot.set(_output, out);
177     nr_pixblock_release(in);
178     delete in;
179     return 0;
182 } /* namespace NR */
184 /*
185   Local Variables:
186   mode:c++
187   c-file-style:"stroustrup"
188   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
189   indent-tabs-mode:nil
190   fill-column:99
191   End:
192 */
193 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :