2902bf143c82adc7a94513294e4f3468fce1e084
[blender.git] / source / blender / nodes / shader / nodes / node_shader_material.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2005 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/nodes/shader/nodes/node_shader_material.c
29  *  \ingroup shdnodes
30  */
31
32
33 #include "node_shader_util.h"
34
35 /* **************** MATERIAL ******************** */
36
37 static bNodeSocketTemplate sh_node_material_in[] = {
38         {       SOCK_RGBA, 1, N_("Color"),              0.0f, 0.0f, 0.0f, 1.0f},
39         {       SOCK_RGBA, 1, N_("Spec"),               0.0f, 0.0f, 0.0f, 1.0f},
40         {       SOCK_FLOAT, 1, N_("Refl"),              0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
41         {       SOCK_VECTOR, 1, N_("Normal"),   0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION},
42         {       -1, 0, ""       }
43 };
44
45 static bNodeSocketTemplate sh_node_material_out[] = {
46         {       SOCK_RGBA, 0, N_("Color")},
47         {       SOCK_FLOAT, 0, N_("Alpha")},
48         {       SOCK_VECTOR, 0, N_("Normal")},
49         {       -1, 0, ""       }
50 };
51
52 /* **************** EXTENDED MATERIAL ******************** */
53
54 static bNodeSocketTemplate sh_node_material_ext_in[] = {
55         {       SOCK_RGBA, 1, N_("Color"),              0.0f, 0.0f, 0.0f, 1.0f},
56         {       SOCK_RGBA, 1, N_("Spec"),               0.0f, 0.0f, 0.0f, 1.0f},
57         {       SOCK_FLOAT, 1, N_("Refl"),              0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
58         {       SOCK_VECTOR, 1, N_("Normal"),   0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_DIRECTION},
59         {       SOCK_RGBA, 1, N_("Mirror"),             0.0f, 0.0f, 0.0f, 1.0f},
60         {       SOCK_FLOAT, 1, N_("Ambient"),   0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
61         {       SOCK_FLOAT, 1, N_("Emit"),              0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED},
62         {       SOCK_FLOAT, 1, N_("SpecTra"),   0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
63         {       SOCK_FLOAT, 1, N_("Ray Mirror"),        0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
64         {       SOCK_FLOAT, 1, N_("Alpha"),             0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED},
65         {       SOCK_FLOAT, 1, N_("Translucency"),      0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR},
66         {       -1, 0, ""       }
67 };
68
69 static bNodeSocketTemplate sh_node_material_ext_out[] = {
70         {       SOCK_RGBA, 0, N_("Color")},
71         {       SOCK_FLOAT, 0, N_("Alpha")},
72         {       SOCK_VECTOR, 0, N_("Normal")},
73         {       SOCK_RGBA, 0, N_("Diffuse")},
74         {       SOCK_RGBA, 0, N_("Spec")},
75         {       SOCK_RGBA, 0, N_("AO")},
76         {       -1, 0, ""       }
77 };
78
79 static void node_shader_exec_material(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
80 {
81         if (data && node->id) {
82                 ShadeResult shrnode;
83                 ShadeInput *shi;
84                 ShaderCallData *shcd= data;
85                 float col[4];
86                 bNodeSocket *sock;
87                 char hasinput[NUM_MAT_IN] = {'\0'};
88                 int i, mode;
89                 
90                 /* note: cannot use the in[]->hasinput flags directly, as these are not necessarily
91                  * the constant input stack values (e.g. in case material node is inside a group).
92                  * we just want to know if a node input uses external data or the material setting.
93                  * this is an ugly hack, but so is this node as a whole.
94                  */
95                 for (sock=node->inputs.first, i=0; sock; sock=sock->next, ++i)
96                         hasinput[i] = (sock->link != NULL);
97                 
98                 shi= shcd->shi;
99                 shi->mat= (Material *)node->id;
100                 
101                 /* copy all relevant material vars, note, keep this synced with render_types.h */
102                 memcpy(&shi->r, &shi->mat->r, 23*sizeof(float));
103                 shi->har= shi->mat->har;
104                 
105                 /* write values */
106                 if (hasinput[MAT_IN_COLOR])
107                         nodestack_get_vec(&shi->r, SOCK_VECTOR, in[MAT_IN_COLOR]);
108                 
109                 if (hasinput[MAT_IN_SPEC])
110                         nodestack_get_vec(&shi->specr, SOCK_VECTOR, in[MAT_IN_SPEC]);
111                 
112                 if (hasinput[MAT_IN_REFL])
113                         nodestack_get_vec(&shi->refl, SOCK_FLOAT, in[MAT_IN_REFL]);
114                 
115                 /* retrieve normal */
116                 if (hasinput[MAT_IN_NORMAL]) {
117                         nodestack_get_vec(shi->vn, SOCK_VECTOR, in[MAT_IN_NORMAL]);
118                         normalize_v3(shi->vn);
119                 }
120                 else
121                         copy_v3_v3(shi->vn, shi->vno);
122                 
123                 /* custom option to flip normal */
124                 if (node->custom1 & SH_NODE_MAT_NEG) {
125                         negate_v3(shi->vn);
126                 }
127                 
128                 if (node->type == SH_NODE_MATERIAL_EXT) {
129                         if (hasinput[MAT_IN_MIR])
130                                 nodestack_get_vec(&shi->mirr, SOCK_VECTOR, in[MAT_IN_MIR]);
131                         if (hasinput[MAT_IN_AMB])
132                                 nodestack_get_vec(&shi->amb, SOCK_FLOAT, in[MAT_IN_AMB]);
133                         if (hasinput[MAT_IN_EMIT])
134                                 nodestack_get_vec(&shi->emit, SOCK_FLOAT, in[MAT_IN_EMIT]);
135                         if (hasinput[MAT_IN_SPECTRA])
136                                 nodestack_get_vec(&shi->spectra, SOCK_FLOAT, in[MAT_IN_SPECTRA]);
137                         if (hasinput[MAT_IN_RAY_MIRROR])
138                                 nodestack_get_vec(&shi->ray_mirror, SOCK_FLOAT, in[MAT_IN_RAY_MIRROR]);
139                         if (hasinput[MAT_IN_ALPHA])
140                                 nodestack_get_vec(&shi->alpha, SOCK_FLOAT, in[MAT_IN_ALPHA]);
141                         if (hasinput[MAT_IN_TRANSLUCENCY])
142                                 nodestack_get_vec(&shi->translucency, SOCK_FLOAT, in[MAT_IN_TRANSLUCENCY]);
143                 }
144                 
145                 /* make alpha output give results even if transparency is only enabled on
146                  * the material linked in this not and not on the parent material */
147                 mode = shi->mode;
148                 if(shi->mat->mode & MA_TRANSP)
149                         shi->mode |= MA_TRANSP;
150
151                 shi->nodes= 1; /* temp hack to prevent trashadow recursion */
152                 node_shader_lamp_loop(shi, &shrnode);   /* clears shrnode */
153                 shi->nodes= 0;
154                 
155                 shi->mode = mode;
156
157                 /* write to outputs */
158                 if (node->custom1 & SH_NODE_MAT_DIFF) {
159                         copy_v3_v3(col, shrnode.combined);
160                         if (!(node->custom1 & SH_NODE_MAT_SPEC)) {
161                                 sub_v3_v3(col, shrnode.spec);
162                         }
163                 }
164                 else if (node->custom1 & SH_NODE_MAT_SPEC) {
165                         copy_v3_v3(col, shrnode.spec);
166                 }
167                 else
168                         col[0] = col[1] = col[2] = 0.0f;
169                 
170                 col[3] = shrnode.alpha;
171                 
172                 if (shi->do_preview)
173                         nodeAddToPreview(node, col, shi->xs, shi->ys, shi->do_manage);
174                 
175                 copy_v3_v3(out[MAT_OUT_COLOR]->vec, col);
176                 out[MAT_OUT_ALPHA]->vec[0] = shrnode.alpha;
177                 
178                 if (node->custom1 & SH_NODE_MAT_NEG) {
179                         shi->vn[0] = -shi->vn[0];
180                         shi->vn[1] = -shi->vn[1];
181                         shi->vn[2] = -shi->vn[2];
182                 }
183                 
184                 copy_v3_v3(out[MAT_OUT_NORMAL]->vec, shi->vn);
185                 
186                 /* Extended material options */
187                 if (node->type == SH_NODE_MATERIAL_EXT) {
188                         /* Shadow, Reflect, Refract, Radiosity, Speed seem to cause problems inside
189                          * a node tree :( */
190                         copy_v3_v3(out[MAT_OUT_DIFFUSE]->vec, shrnode.diff);
191                         copy_v3_v3(out[MAT_OUT_SPEC]->vec, shrnode.spec);
192                         copy_v3_v3(out[MAT_OUT_AO]->vec, shrnode.ao);
193                 }
194                 
195                 /* copy passes, now just active node */
196                 if (node->flag & NODE_ACTIVE_ID) {
197                         float combined[4], alpha;
198
199                         copy_v4_v4(combined, shcd->shr->combined);
200                         alpha= shcd->shr->alpha;
201
202                         *(shcd->shr)= shrnode;
203
204                         copy_v4_v4(shcd->shr->combined, combined);
205                         shcd->shr->alpha= alpha;
206                 }
207         }
208 }
209
210
211 static void node_shader_init_material(bNodeTree *UNUSED(ntree), bNode *node, bNodeTemplate *UNUSED(ntemp))
212 {
213         node->custom1= SH_NODE_MAT_DIFF|SH_NODE_MAT_SPEC;
214 }
215
216 /* XXX this is also done as a local static function in gpu_codegen.c,
217  * but we need this to hack around the crappy material node.
218  */
219 static GPUNodeLink *gpu_get_input_link(GPUNodeStack *in)
220 {
221         if (in->link)
222                 return in->link;
223         else
224                 return GPU_uniform(in->vec);
225 }
226
227 static int gpu_shader_material(GPUMaterial *mat, bNode *node, GPUNodeStack *in, GPUNodeStack *out)
228 {
229         if (node->id) {
230                 GPUShadeInput shi;
231                 GPUShadeResult shr;
232                 bNodeSocket *sock;
233                 char hasinput[NUM_MAT_IN] = {'\0'};
234                 int i;
235                 
236                 /* note: cannot use the in[]->hasinput flags directly, as these are not necessarily
237                  * the constant input stack values (e.g. in case material node is inside a group).
238                  * we just want to know if a node input uses external data or the material setting.
239                  */
240                 for (sock=node->inputs.first, i=0; sock; sock=sock->next, ++i)
241                         hasinput[i] = (sock->link != NULL);
242
243                 GPU_shadeinput_set(mat, (Material*)node->id, &shi);
244
245                 /* write values */
246                 if (hasinput[MAT_IN_COLOR])
247                         shi.rgb = gpu_get_input_link(&in[MAT_IN_COLOR]);
248                 
249                 if (hasinput[MAT_IN_SPEC])
250                         shi.specrgb = gpu_get_input_link(&in[MAT_IN_SPEC]);
251                 
252                 if (hasinput[MAT_IN_REFL])
253                         shi.refl = gpu_get_input_link(&in[MAT_IN_REFL]);
254                 
255                 /* retrieve normal */
256                 if (hasinput[MAT_IN_NORMAL]) {
257                         GPUNodeLink *tmp;
258                         shi.vn = gpu_get_input_link(&in[MAT_IN_NORMAL]);
259                         GPU_link(mat, "vec_math_normalize", shi.vn, &shi.vn, &tmp);
260                 }
261                 
262                 /* custom option to flip normal */
263                 if (node->custom1 & SH_NODE_MAT_NEG)
264                         GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
265
266                 if (node->type == SH_NODE_MATERIAL_EXT) {
267                         if (hasinput[MAT_IN_AMB])
268                                 shi.amb= gpu_get_input_link(&in[MAT_IN_AMB]);
269                         if (hasinput[MAT_IN_EMIT])
270                                 shi.emit= gpu_get_input_link(&in[MAT_IN_EMIT]);
271                         if (hasinput[MAT_IN_ALPHA])
272                                 shi.alpha= gpu_get_input_link(&in[MAT_IN_ALPHA]);
273                 }
274
275                 GPU_shaderesult_set(&shi, &shr); /* clears shr */
276                 
277                 /* write to outputs */
278                 if (node->custom1 & SH_NODE_MAT_DIFF) {
279                         out[MAT_OUT_COLOR].link= shr.combined;
280
281                         if (!(node->custom1 & SH_NODE_MAT_SPEC)) {
282                                 GPUNodeLink *link;
283                                 GPU_link(mat, "vec_math_sub", shr.combined, shr.spec, &out[MAT_OUT_COLOR].link, &link);
284                         }
285                 }
286                 else if (node->custom1 & SH_NODE_MAT_SPEC) {
287                         out[MAT_OUT_COLOR].link= shr.spec;
288                 }
289                 else
290                         GPU_link(mat, "set_rgb_zero", &out[MAT_OUT_COLOR].link);
291
292                 GPU_link(mat, "mtex_alpha_to_col", out[MAT_OUT_COLOR].link, shr.alpha, &out[MAT_OUT_COLOR].link);
293
294                 out[MAT_OUT_ALPHA].link = shr.alpha; //
295                 
296                 if (node->custom1 & SH_NODE_MAT_NEG)
297                         GPU_link(mat, "vec_math_negate", shi.vn, &shi.vn);
298                 out[MAT_OUT_NORMAL].link = shi.vn;
299
300                 if (node->type == SH_NODE_MATERIAL_EXT) {
301                         out[MAT_OUT_DIFFUSE].link = shr.diff;
302                         out[MAT_OUT_SPEC].link = shr.spec;
303                 }
304
305                 return 1;
306         }
307
308         return 0;
309 }
310
311 void register_node_type_sh_material(bNodeTreeType *ttype)
312 {
313         static bNodeType ntype;
314
315         node_type_base(ttype, &ntype, SH_NODE_MATERIAL, "Material", NODE_CLASS_INPUT, NODE_OPTIONS|NODE_PREVIEW);
316         node_type_compatibility(&ntype, NODE_OLD_SHADING);
317         node_type_socket_templates(&ntype, sh_node_material_in, sh_node_material_out);
318         node_type_size(&ntype, 120, 80, 240);
319         node_type_init(&ntype, node_shader_init_material);
320         node_type_exec(&ntype, node_shader_exec_material);
321         node_type_gpu(&ntype, gpu_shader_material);
322
323         nodeRegisterType(ttype, &ntype);
324 }
325
326
327 void register_node_type_sh_material_ext(bNodeTreeType *ttype)
328 {
329         static bNodeType ntype;
330
331         node_type_base(ttype, &ntype, SH_NODE_MATERIAL_EXT, "Extended Material", NODE_CLASS_INPUT, NODE_OPTIONS|NODE_PREVIEW);
332         node_type_compatibility(&ntype, NODE_OLD_SHADING);
333         node_type_socket_templates(&ntype, sh_node_material_ext_in, sh_node_material_ext_out);
334         node_type_size(&ntype, 120, 80, 240);
335         node_type_init(&ntype, node_shader_init_material);
336         node_type_exec(&ntype, node_shader_exec_material);
337         node_type_gpu(&ntype, gpu_shader_material);
338
339         nodeRegisterType(ttype, &ntype);
340 }