fix T65576 collada exporter duplicates node tree when exported material already uses...
[blender.git] / source / blender / collada / Materials.cpp
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
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
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
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
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
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
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
131 void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
132 {
133   float reflectivity = val.getFloatValue();
134   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Metallic");
135   ((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity;
136
137   material->metallic = reflectivity;
138 }
139
140 void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val)
141 {
142   float roughness = val.getFloatValue();
143   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Roughness");
144   ((bNodeSocketValueFloat *)socket->default_value)->value = roughness;
145 }
146
147 void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val)
148 {
149   float ior = val.getFloatValue();
150   if (ior < 0) {
151     fprintf(stderr,
152             "IOR of negative value is not allowed for materials (using Blender default value "
153             "instead)");
154     return;
155   }
156
157   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "IOR");
158   ((bNodeSocketValueFloat *)socket->default_value)->value = ior;
159 }
160
161 void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
162                              COLLADAFW::ColorOrTexture &cot,
163                              COLLADAFW::FloatOrParam &val)
164 {
165   if (effect == nullptr) {
166     return;
167   }
168
169   if (cot.isColor() || !cot.isValid()) {
170     COLLADAFW::Color col = (cot.isValid()) ? cot.getColor() : COLLADAFW::Color(1, 1, 1, 1);
171     float alpha = val.getFloatValue() * col.getAlpha();  // Assuming A_ONE opaque mode
172
173     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
174     ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
175   }
176   else if (cot.isTexture()) {
177     int locy = -300 * (node_map.size() - 2);
178     add_texture_node(cot, -300, locy, "Alpha");
179     // TODO: Connect node
180   }
181 }
182
183 void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
184 {
185   int locy = -300 * (node_map.size() - 2);
186   if (cot.isColor()) {
187     COLLADAFW::Color col = cot.getColor();
188     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
189     float *fcol = (float *)socket->default_value;
190
191     fcol[0] = material->r = col.getRed();
192     fcol[1] = material->g = col.getGreen();
193     fcol[2] = material->b = col.getBlue();
194     fcol[3] = material->a = col.getAlpha();
195   }
196   else if (cot.isTexture()) {
197     bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
198     if (texture_node != NULL) {
199       add_link(texture_node, 0, shader_node, 0);
200     }
201   }
202 }
203
204 Image *MaterialNode::get_diffuse_image()
205 {
206   bNode *shader = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED);
207   if (shader == nullptr) {
208     return nullptr;
209   }
210
211   bNodeSocket *in_socket = nodeFindSocket(shader, SOCK_IN, "Base Color");
212   if (in_socket == nullptr) {
213     return nullptr;
214   }
215
216   bNodeLink *link = in_socket->link;
217   if (link == nullptr) {
218     return nullptr;
219   }
220
221   bNode *texture = link->fromnode;
222   if (texture == nullptr) {
223     return nullptr;
224   }
225
226   if (texture->type != SH_NODE_TEX_IMAGE) {
227     return nullptr;
228   }
229
230   Image *image = (Image *)texture->id;
231   return image;
232 }
233
234 static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col)
235 {
236   bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0);
237   float *fcol = (float *)socket->default_value;
238   fcol[0] = col.getRed();
239   fcol[1] = col.getGreen();
240   fcol[2] = col.getBlue();
241
242   return socket;
243 }
244
245 void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot)
246 {
247   int locy = -300 * (node_map.size() - 2);
248   if (cot.isColor()) {
249     COLLADAFW::Color col = cot.getColor();
250     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient");
251     set_color(node, col);
252     // TODO: Connect node
253   }
254   // texture
255   else if (cot.isTexture()) {
256     add_texture_node(cot, -300, locy, "Ambient");
257     // TODO: Connect node
258   }
259 }
260
261 void MaterialNode::set_reflective(COLLADAFW::ColorOrTexture &cot)
262 {
263   int locy = -300 * (node_map.size() - 2);
264   if (cot.isColor()) {
265     COLLADAFW::Color col = cot.getColor();
266     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Reflective");
267     set_color(node, col);
268     // TODO: Connect node
269   }
270   // texture
271   else if (cot.isTexture()) {
272     add_texture_node(cot, -300, locy, "Reflective");
273     // TODO: Connect node
274   }
275 }
276
277 void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
278 {
279   int locy = -300 * (node_map.size() - 2);
280   if (cot.isColor()) {
281     COLLADAFW::Color col = cot.getColor();
282     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission");
283     float *fcol = (float *)socket->default_value;
284
285     fcol[0] = col.getRed();
286     fcol[1] = col.getGreen();
287     fcol[2] = col.getBlue();
288     fcol[3] = col.getAlpha();
289   }
290   else if (cot.isTexture()) {
291     bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
292     if (texture_node != NULL) {
293       add_link(texture_node, 0, shader_node, 0);
294     }
295   }
296 }
297
298 void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
299 {
300   if (effect == nullptr) {
301     return;
302   }
303
304   int locy = -300 * (node_map.size() - 2);
305   if (cot.isColor()) {
306     COLLADAFW::Color col = effect->getTransparent().getColor();
307     float alpha = effect->getTransparency().getFloatValue();
308
309     if (col.isValid()) {
310       alpha *= col.getAlpha();  // Assuming A_ONE opaque mode
311     }
312
313     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
314     ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
315   }
316   // texture
317   else if (cot.isTexture()) {
318     add_texture_node(cot, -300, locy, "Alpha");
319     // TODO: Connect node
320   }
321 }
322
323 void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
324 {
325   int locy = -300 * (node_map.size() - 2);
326   if (cot.isColor()) {
327     COLLADAFW::Color col = cot.getColor();
328     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
329     set_color(node, col);
330     // TODO: Connect node
331   }
332   // texture
333   else if (cot.isTexture()) {
334     add_texture_node(cot, -300, locy, "Specular");
335     // TODO: Connect node
336   }
337 }
338
339 bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
340                                       int locx,
341                                       int locy,
342                                       std::string label)
343 {
344   if (effect == nullptr) {
345     return nullptr;
346   }
347
348   UidImageMap &image_map = *uid_image_map;
349
350   COLLADAFW::Texture ctex = cot.getTexture();
351
352   COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray();
353   COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
354
355   const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage();
356
357   if (image_map.find(ima_uid) == image_map.end()) {
358     fprintf(stderr, "Couldn't find an image by UID.\n");
359     return NULL;
360   }
361
362   Image *ima = image_map[ima_uid];
363   bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label);
364   texture_node->id = &ima->id;
365   return texture_node;
366 }