1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
17 #include "Materials.h"
18 
MaterialNode(bContext * C,Material * ma,KeyImageMap & key_image_map)19 MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map)
20     : mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map)
21 {
22   bNodeTree *new_ntree = prepare_material_nodetree();
23   setShaderType();
24   if (new_ntree) {
25     shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
26     output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
27     add_link(shader_node, 0, output_node, 0);
28   }
29 }
30 
MaterialNode(bContext * C,COLLADAFW::EffectCommon * ef,Material * ma,UidImageMap & uid_image_map)31 MaterialNode::MaterialNode(bContext *C,
32                            COLLADAFW::EffectCommon *ef,
33                            Material *ma,
34                            UidImageMap &uid_image_map)
35     : mContext(C), material(ma), effect(ef), uid_image_map(&uid_image_map)
36 {
37   prepare_material_nodetree();
38   setShaderType();
39 
40   std::map<std::string, bNode *> nmap;
41 #if 0
42   nmap["main"] = add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300);
43   nmap["emission"] = add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission");
44   nmap["add"] = add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400);
45   nmap["transparent"] = add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200);
46   nmap["mix"] = add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency");
47   nmap["out"] = add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300);
48   nmap["out"]->flag &= ~NODE_SELECT;
49 
50   add_link(ntree, nmap["emission"], 0, nmap["add"], 0);
51   add_link(ntree, nmap["main"], 0, nmap["add"], 1);
52   add_link(ntree, nmap["add"], 0, nmap["mix"], 1);
53   add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2);
54 
55   add_link(ntree, nmap["mix"], 0, nmap["out"], 0);
56   /* experimental, probably not used. */
57   make_group(C, ntree, nmap);
58 #else
59   shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
60   output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
61   add_link(shader_node, 0, output_node, 0);
62 #endif
63 }
64 
setShaderType()65 void MaterialNode::setShaderType()
66 {
67 #if 0
68   COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType();
69   /* Currently we only support PBR based shaders */
70   /* TODO: simulate the effects with PBR */
71 
72   /* blinn */
73   if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) {
74     ma->spec_shader = MA_SPEC_BLINN;
75     ma->spec = ef->getShininess().getFloatValue();
76   }
77   /* phong */
78   else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) {
79     ma->spec_shader = MA_SPEC_PHONG;
80     ma->har = ef->getShininess().getFloatValue();
81   }
82   /* lambert */
83   else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) {
84     ma->diff_shader = MA_DIFF_LAMBERT;
85   }
86   /* default - lambert */
87   else {
88     ma->diff_shader = MA_DIFF_LAMBERT;
89     fprintf(stderr, "Current shader type is not supported, default to lambert.\n");
90   }
91 #endif
92 }
93 
94 /* returns null if material already has a node tree */
prepare_material_nodetree()95 bNodeTree *MaterialNode::prepare_material_nodetree()
96 {
97   if (material->nodetree) {
98     ntree = material->nodetree;
99     return NULL;
100   }
101 
102   material->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree");
103   material->use_nodes = true;
104   ntree = material->nodetree;
105   return ntree;
106 }
107 
add_node(int node_type,int locx,int locy,std::string label)108 bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label)
109 {
110   bNode *node = nodeAddStaticNode(mContext, ntree, node_type);
111   if (node) {
112     if (label.length() > 0) {
113       strcpy(node->label, label.c_str());
114     }
115     node->locx = locx;
116     node->locy = locy;
117     node->flag |= NODE_SELECT;
118   }
119   node_map[label] = node;
120   return node;
121 }
122 
add_link(bNode * from_node,int from_index,bNode * to_node,int to_index)123 void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, int to_index)
124 {
125   bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
126   bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
127 
128   nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
129 }
130 
set_reflectivity(COLLADAFW::FloatOrParam & val)131 void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
132 {
133   float reflectivity = val.getFloatValue();
134   if (reflectivity >= 0) {
135     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Metallic");
136     ((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity;
137     material->metallic = reflectivity;
138   }
139 }
140 
141 #if 0
142 /* needs rework to be done for 2.81 */
143 void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val)
144 {
145   float roughness = val.getFloatValue();
146   if (roughness >= 0) {
147     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Roughness");
148     ((bNodeSocketValueFloat *)socket->default_value)->value = roughness;
149   }
150 }
151 #endif
152 
set_ior(COLLADAFW::FloatOrParam & val)153 void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val)
154 {
155   float ior = val.getFloatValue();
156   if (ior < 0) {
157     fprintf(stderr,
158             "IOR of negative value is not allowed for materials (using Blender default value "
159             "instead)");
160     return;
161   }
162 
163   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "IOR");
164   ((bNodeSocketValueFloat *)socket->default_value)->value = ior;
165 }
166 
set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,COLLADAFW::ColorOrTexture & cot,COLLADAFW::FloatOrParam & val)167 void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
168                              COLLADAFW::ColorOrTexture &cot,
169                              COLLADAFW::FloatOrParam &val)
170 {
171   /*  Handling the alpha value according to the Collada 1.4 reference guide
172    *  see page 7-5 Determining Transparency (Opacity)
173    */
174 
175   if (effect == nullptr) {
176     return;
177   }
178 
179   if (cot.isColor() || !cot.isValid()) {
180     /* transparent_cot is either a color or not defined */
181 
182     float transparent_alpha;
183     if (cot.isValid()) {
184       COLLADAFW::Color col = cot.getColor();
185       transparent_alpha = col.getAlpha();
186     }
187     else {
188       /* no transparent color defined */
189       transparent_alpha = 1;
190     }
191 
192     float transparency_alpha = val.getFloatValue();
193     if (transparency_alpha < 0) {
194       /* transparency is not defined */
195       transparency_alpha = 1; /* set to opaque */
196     }
197 
198     float alpha = transparent_alpha * transparency_alpha;
199     if (mode == COLLADAFW::EffectCommon::RGB_ZERO) {
200       alpha = 1 - alpha;
201     }
202 
203     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
204     ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
205     material->a = alpha;
206   }
207   else if (cot.isTexture()) {
208     int locy = -300 * (node_map.size() - 2);
209     add_texture_node(cot, -300, locy, "Alpha");
210   }
211 }
212 
set_diffuse(COLLADAFW::ColorOrTexture & cot)213 void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
214 {
215   int locy = -300 * (node_map.size() - 2);
216   if (cot.isColor()) {
217     COLLADAFW::Color col = cot.getColor();
218     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
219     float *fcol = (float *)socket->default_value;
220 
221     fcol[0] = material->r = col.getRed();
222     fcol[1] = material->g = col.getGreen();
223     fcol[2] = material->b = col.getBlue();
224     fcol[3] = material->a = col.getAlpha();
225   }
226   else if (cot.isTexture()) {
227     bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
228     if (texture_node != NULL) {
229       add_link(texture_node, 0, shader_node, 0);
230     }
231   }
232 }
233 
get_diffuse_image()234 Image *MaterialNode::get_diffuse_image()
235 {
236   bNode *shader = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED);
237   if (shader == nullptr) {
238     return nullptr;
239   }
240 
241   bNodeSocket *in_socket = nodeFindSocket(shader, SOCK_IN, "Base Color");
242   if (in_socket == nullptr) {
243     return nullptr;
244   }
245 
246   bNodeLink *link = in_socket->link;
247   if (link == nullptr) {
248     return nullptr;
249   }
250 
251   bNode *texture = link->fromnode;
252   if (texture == nullptr) {
253     return nullptr;
254   }
255 
256   if (texture->type != SH_NODE_TEX_IMAGE) {
257     return nullptr;
258   }
259 
260   Image *image = (Image *)texture->id;
261   return image;
262 }
263 
set_color(bNode * node,COLLADAFW::Color col)264 static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col)
265 {
266   bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0);
267   float *fcol = (float *)socket->default_value;
268   fcol[0] = col.getRed();
269   fcol[1] = col.getGreen();
270   fcol[2] = col.getBlue();
271 
272   return socket;
273 }
274 
set_ambient(COLLADAFW::ColorOrTexture & cot)275 void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot)
276 {
277   int locy = -300 * (node_map.size() - 2);
278   if (cot.isColor()) {
279     COLLADAFW::Color col = cot.getColor();
280     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient");
281     set_color(node, col);
282     /* TODO: Connect node */
283   }
284   /* texture */
285   else if (cot.isTexture()) {
286     add_texture_node(cot, -300, locy, "Ambient");
287     /* TODO: Connect node */
288   }
289 }
290 
set_reflective(COLLADAFW::ColorOrTexture & cot)291 void MaterialNode::set_reflective(COLLADAFW::ColorOrTexture &cot)
292 {
293   int locy = -300 * (node_map.size() - 2);
294   if (cot.isColor()) {
295     COLLADAFW::Color col = cot.getColor();
296     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Reflective");
297     set_color(node, col);
298     /* TODO: Connect node */
299   }
300   /* texture */
301   else if (cot.isTexture()) {
302     add_texture_node(cot, -300, locy, "Reflective");
303     /* TODO: Connect node */
304   }
305 }
306 
set_emission(COLLADAFW::ColorOrTexture & cot)307 void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
308 {
309   int locy = -300 * (node_map.size() - 2);
310   if (cot.isColor()) {
311     COLLADAFW::Color col = cot.getColor();
312     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission");
313     float *fcol = (float *)socket->default_value;
314 
315     fcol[0] = col.getRed();
316     fcol[1] = col.getGreen();
317     fcol[2] = col.getBlue();
318     fcol[3] = col.getAlpha();
319   }
320   // texture
321   else if (cot.isTexture()) {
322     bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
323     if (texture_node != NULL) {
324       add_link(texture_node, 0, shader_node, 0);
325     }
326   }
327 
328   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission Strength");
329   if (socket) {
330     *(float *)socket->default_value = 1.0f;
331   }
332 }
333 
set_opacity(COLLADAFW::ColorOrTexture & cot)334 void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
335 {
336   if (effect == nullptr) {
337     return;
338   }
339 
340   int locy = -300 * (node_map.size() - 2);
341   if (cot.isColor()) {
342     COLLADAFW::Color col = effect->getTransparent().getColor();
343     float alpha = effect->getTransparency().getFloatValue();
344 
345     if (col.isValid()) {
346       alpha *= col.getAlpha(); /* Assuming A_ONE opaque mode */
347     }
348 
349     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
350     ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
351   }
352   /* texture */
353   else if (cot.isTexture()) {
354     add_texture_node(cot, -300, locy, "Alpha");
355     /* TODO: Connect node */
356   }
357 }
358 
set_specular(COLLADAFW::ColorOrTexture & cot)359 void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
360 {
361   int locy = -300 * (node_map.size() - 2);
362   if (cot.isColor()) {
363     COLLADAFW::Color col = cot.getColor();
364     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
365     set_color(node, col);
366     /* TODO: Connect node */
367   }
368   /* texture */
369   else if (cot.isTexture()) {
370     add_texture_node(cot, -300, locy, "Specular");
371     /* TODO: Connect node */
372   }
373 }
374 
add_texture_node(COLLADAFW::ColorOrTexture & cot,int locx,int locy,std::string label)375 bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
376                                       int locx,
377                                       int locy,
378                                       std::string label)
379 {
380   if (effect == nullptr) {
381     return nullptr;
382   }
383 
384   UidImageMap &image_map = *uid_image_map;
385 
386   COLLADAFW::Texture ctex = cot.getTexture();
387 
388   COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray();
389   COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
390 
391   const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage();
392 
393   if (image_map.find(ima_uid) == image_map.end()) {
394     fprintf(stderr, "Couldn't find an image by UID.\n");
395     return NULL;
396   }
397 
398   Image *ima = image_map[ima_uid];
399   bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label);
400   texture_node->id = &ima->id;
401   return texture_node;
402 }
403