update Collada Importer: reworked export and import of Materials
[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   ntree = prepare_material_nodetree();
23   setShaderType();
24   shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
25   output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
26   add_link(shader_node, 0, output_node, 0);
27 }
28
29 MaterialNode::MaterialNode(bContext *C,
30                            COLLADAFW::EffectCommon *ef,
31                            Material *ma,
32                            UidImageMap &uid_image_map)
33     : mContext(C), material(ma), effect(ef), uid_image_map(&uid_image_map)
34 {
35   ntree = prepare_material_nodetree();
36   setShaderType();
37
38   std::map<std::string, bNode *> nmap;
39 #if 0
40   nmap["main"] = add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300);
41   nmap["emission"] = add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission");
42   nmap["add"] = add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400);
43   nmap["transparent"] = add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200);
44   nmap["mix"] = add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency");
45   nmap["out"] = add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300);
46   nmap["out"]->flag &= ~NODE_SELECT;
47
48   add_link(ntree, nmap["emission"], 0, nmap["add"], 0);
49   add_link(ntree, nmap["main"], 0, nmap["add"], 1);
50   add_link(ntree, nmap["add"], 0, nmap["mix"], 1);
51   add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2);
52
53   add_link(ntree, nmap["mix"], 0, nmap["out"], 0);
54   // experimental, probably not used.
55   make_group(C, ntree, nmap);
56 #else
57   shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
58   output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
59   add_link(shader_node, 0, output_node, 0);
60 #endif
61 }
62
63 void MaterialNode::setShaderType()
64 {
65 #if 0
66   COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType();
67   // Currently we only support PBR based shaders
68   // TODO: simulate the effects with PBR
69
70   // blinn
71   if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) {
72     ma->spec_shader = MA_SPEC_BLINN;
73     ma->spec = ef->getShininess().getFloatValue();
74   }
75   // phong
76   else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) {
77     ma->spec_shader = MA_SPEC_PHONG;
78     ma->har = ef->getShininess().getFloatValue();
79   }
80   // lambert
81   else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) {
82     ma->diff_shader = MA_DIFF_LAMBERT;
83   }
84   // default - lambert
85   else {
86     ma->diff_shader = MA_DIFF_LAMBERT;
87     fprintf(stderr, "Current shader type is not supported, default to lambert.\n");
88   }
89 #endif
90 }
91
92 bNodeTree *MaterialNode::prepare_material_nodetree()
93 {
94   if (material->nodetree == NULL) {
95     material->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree");
96     material->use_nodes = true;
97   }
98   return material->nodetree;
99 }
100
101 bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label)
102 {
103   bNode *node = nodeAddStaticNode(mContext, ntree, node_type);
104   if (node) {
105     if (label.length() > 0) {
106       strcpy(node->label, label.c_str());
107     }
108     node->locx = locx;
109     node->locy = locy;
110     node->flag |= NODE_SELECT;
111   }
112   node_map[label] = node;
113   return node;
114 }
115
116 void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, int to_index)
117 {
118   bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
119   bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
120
121   nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
122 }
123
124 void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
125 {
126   float reflectivity = val.getFloatValue();
127   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Metallic");
128   ((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity;
129
130   material->metallic = reflectivity;
131 }
132
133 void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val)
134 {
135   float roughness = val.getFloatValue();
136   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Roughness");
137   ((bNodeSocketValueFloat *)socket->default_value)->value = roughness;
138 }
139
140 void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val)
141 {
142   float ior = val.getFloatValue();
143   if (ior < 0) {
144     fprintf(stderr,
145             "IOR of negative value is not allowed for materials (using Blender default value "
146             "instead)");
147     return;
148   }
149
150   bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "IOR");
151   ((bNodeSocketValueFloat *)socket->default_value)->value = ior;
152 }
153
154 void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
155                              COLLADAFW::ColorOrTexture &cot,
156                              COLLADAFW::FloatOrParam &val)
157 {
158   if (effect == nullptr) {
159     return;
160   }
161
162   if (cot.isColor() || !cot.isValid()) {
163     COLLADAFW::Color col = (cot.isValid()) ? cot.getColor() : COLLADAFW::Color(1, 1, 1, 1);
164     float alpha = val.getFloatValue() * col.getAlpha();  // Assuming A_ONE opaque mode
165
166     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
167     ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
168   }
169   else if (cot.isTexture()) {
170     int locy = -300 * (node_map.size() - 2);
171     add_texture_node(cot, -300, locy, "Alpha");
172     // TODO: Connect node
173   }
174 }
175
176 void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
177 {
178   int locy = -300 * (node_map.size() - 2);
179   if (cot.isColor()) {
180     COLLADAFW::Color col = cot.getColor();
181     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
182     float *fcol = (float *)socket->default_value;
183
184     fcol[0] = material->r = col.getRed();
185     fcol[1] = material->g = col.getGreen();
186     fcol[2] = material->b = col.getBlue();
187     fcol[3] = material->a = col.getAlpha();
188   }
189   else if (cot.isTexture()) {
190     bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
191     if (texture_node != NULL) {
192       add_link(texture_node, 0, shader_node, 0);
193     }
194   }
195 }
196
197 Image *MaterialNode::get_diffuse_image()
198 {
199   bNode *shader = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED);
200   if (shader == nullptr) {
201     return nullptr;
202   }
203
204   bNodeSocket *in_socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
205   if (in_socket == nullptr) {
206     return nullptr;
207   }
208
209   bNodeLink *link = in_socket->link;
210   if (link == nullptr) {
211     return nullptr;
212   }
213
214   bNode *texture = link->fromnode;
215   if (texture == nullptr) {
216     return nullptr;
217   }
218
219   if (texture->type != SH_NODE_TEX_IMAGE) {
220     return nullptr;
221   }
222
223   Image *image = (Image *)texture->id;
224   return image;
225 }
226
227 static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col)
228 {
229   bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0);
230   float *fcol = (float *)socket->default_value;
231   fcol[0] = col.getRed();
232   fcol[1] = col.getGreen();
233   fcol[2] = col.getBlue();
234
235   return socket;
236 }
237
238 void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot)
239 {
240   int locy = -300 * (node_map.size() - 2);
241   if (cot.isColor()) {
242     COLLADAFW::Color col = cot.getColor();
243     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient");
244     set_color(node, col);
245     // TODO: Connect node
246   }
247   // texture
248   else if (cot.isTexture()) {
249     add_texture_node(cot, -300, locy, "Ambient");
250     // TODO: Connect node
251   }
252 }
253
254 void MaterialNode::set_reflective(COLLADAFW::ColorOrTexture &cot)
255 {
256   int locy = -300 * (node_map.size() - 2);
257   if (cot.isColor()) {
258     COLLADAFW::Color col = cot.getColor();
259     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Reflective");
260     set_color(node, col);
261     // TODO: Connect node
262   }
263   // texture
264   else if (cot.isTexture()) {
265     add_texture_node(cot, -300, locy, "Reflective");
266     // TODO: Connect node
267   }
268 }
269
270 void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
271 {
272   int locy = -300 * (node_map.size() - 2);
273   if (cot.isColor()) {
274     COLLADAFW::Color col = cot.getColor();
275     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission");
276     float *fcol = (float *)socket->default_value;
277
278     fcol[0] = col.getRed();
279     fcol[1] = col.getGreen();
280     fcol[2] = col.getBlue();
281     fcol[3] = col.getAlpha();
282   }
283   else if (cot.isTexture()) {
284     bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
285     if (texture_node != NULL) {
286       add_link(texture_node, 0, shader_node, 0);
287     }
288   }
289 }
290
291 void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
292 {
293   if (effect == nullptr) {
294     return;
295   }
296
297   int locy = -300 * (node_map.size() - 2);
298   if (cot.isColor()) {
299     COLLADAFW::Color col = effect->getTransparent().getColor();
300     float alpha = effect->getTransparency().getFloatValue();
301
302     if (col.isValid()) {
303       alpha *= col.getAlpha();  // Assuming A_ONE opaque mode
304     }
305
306     bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
307     ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
308   }
309   // texture
310   else if (cot.isTexture()) {
311     add_texture_node(cot, -300, locy, "Alpha");
312     // TODO: Connect node
313   }
314 }
315
316 void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
317 {
318   int locy = -300 * (node_map.size() - 2);
319   if (cot.isColor()) {
320     COLLADAFW::Color col = cot.getColor();
321     bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
322     set_color(node, col);
323     // TODO: Connect node
324   }
325   // texture
326   else if (cot.isTexture()) {
327     add_texture_node(cot, -300, locy, "Specular");
328     // TODO: Connect node
329   }
330 }
331
332 bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
333                                       int locx,
334                                       int locy,
335                                       std::string label)
336 {
337   if (effect == nullptr) {
338     return nullptr;
339   }
340
341   UidImageMap &image_map = *uid_image_map;
342
343   COLLADAFW::Texture ctex = cot.getTexture();
344
345   COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray();
346   COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
347
348   const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage();
349
350   if (image_map.find(ima_uid) == image_map.end()) {
351     fprintf(stderr, "Couldn't find an image by UID.\n");
352     return NULL;
353   }
354
355   Image *ima = image_map[ima_uid];
356   bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label);
357   texture_node->id = &ima->id;
358   return texture_node;
359 }