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