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