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
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;
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);
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);
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 }
173 //finishing
174 slot.set(_output, out);
175 delete in;
176 return 0;
177 }
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 :