Blender V4.5
Materials.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2018-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "Materials.h"
6
7#include "BLI_listbase.h"
8#include "BLI_string.h"
9
10#include "BKE_node.hh"
12#include "BKE_node_runtime.hh"
14
16 : mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map)
17{
18 bNodeTree *new_ntree = prepare_material_nodetree();
19 setShaderType();
20 if (new_ntree) {
21 shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
22 output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
23 add_link(shader_node, 0, output_node, 0);
24 }
25}
26
28 COLLADAFW::EffectCommon *ef,
29 Material *ma,
30 UidImageMap &uid_image_map)
31 : mContext(C), material(ma), effect(ef), uid_image_map(&uid_image_map)
32{
33 prepare_material_nodetree();
34 setShaderType();
35
36 std::map<std::string, bNode *> nmap;
37#if 0
38 nmap["main"] = add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300);
39 nmap["emission"] = add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission");
40 nmap["add"] = add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400);
41 nmap["transparent"] = add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200);
42 nmap["mix"] = add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency");
43 nmap["out"] = add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300);
44 nmap["out"]->flag &= ~NODE_SELECT;
45
46 add_link(ntree, nmap["emission"], 0, nmap["add"], 0);
47 add_link(ntree, nmap["main"], 0, nmap["add"], 1);
48 add_link(ntree, nmap["add"], 0, nmap["mix"], 1);
49 add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2);
50
51 add_link(ntree, nmap["mix"], 0, nmap["out"], 0);
52 /* experimental, probably not used. */
53 make_group(C, ntree, nmap);
54#else
55 shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
56 output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
57 add_link(shader_node, 0, output_node, 0);
58#endif
59}
60
61void MaterialNode::setShaderType()
62{
63#if 0
64 COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType();
65 /* Currently we only support PBR based shaders */
66 /* TODO: simulate the effects with PBR */
67
68 /* blinn */
69 if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) {
70 ma->spec_shader = MA_SPEC_BLINN;
71 ma->spec = ef->getShininess().getFloatValue();
72 }
73 /* phong */
74 else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) {
75 ma->spec_shader = MA_SPEC_PHONG;
76 ma->har = ef->getShininess().getFloatValue();
77 }
78 /* lambert */
79 else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) {
80 ma->diff_shader = MA_DIFF_LAMBERT;
81 }
82 /* default - lambert */
83 else {
84 ma->diff_shader = MA_DIFF_LAMBERT;
85 fprintf(stderr, "Current shader type is not supported, default to lambert.\n");
86 }
87#endif
88}
89
90bNodeTree *MaterialNode::prepare_material_nodetree()
91{
92 if (material->nodetree) {
93 ntree = material->nodetree;
94 return nullptr;
95 }
96
98 nullptr, &material->id, "Shader Nodetree", "ShaderNodeTree");
99 material->use_nodes = true;
100 ntree = material->nodetree;
101 return ntree;
102}
103
108
109bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label)
110{
111 bNode *node = blender::bke::node_add_static_node(mContext, *ntree, node_type);
112 if (node) {
113 if (label.length() > 0) {
114 STRNCPY(node->label, label.c_str());
115 }
116 node->location[0] = locx;
117 node->location[1] = locy;
118 node->flag |= NODE_SELECT;
119 }
120 node_map[label] = node;
121 return node;
122}
123
124void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, int to_index)
125{
126 bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
127 bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
128
129 blender::bke::node_add_link(*ntree, *from_node, *from_socket, *to_node, *to_socket);
130}
131
132void MaterialNode::add_link(bNode *from_node,
133 const char *from_label,
134 bNode *to_node,
135 const char *to_label)
136{
137 bNodeSocket *from_socket = blender::bke::node_find_socket(*from_node, SOCK_OUT, from_label);
138 bNodeSocket *to_socket = blender::bke::node_find_socket(*to_node, SOCK_IN, to_label);
139
140 if (from_socket && to_socket) {
141 blender::bke::node_add_link(*ntree, *from_node, *from_socket, *to_node, *to_socket);
142 }
143}
144
145void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
146{
147 float reflectivity = val.getFloatValue();
148 if (reflectivity >= 0) {
149 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Metallic");
150 ((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity;
151 material->metallic = reflectivity;
152 }
153}
154
155#if 0
156/* needs rework to be done for 2.81 */
157void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val)
158{
159 float roughness = val.getFloatValue();
160 if (roughness >= 0) {
161 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Roughness");
162 ((bNodeSocketValueFloat *)socket->default_value)->value = roughness;
163 }
164}
165#endif
166
167void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val)
168{
169 float ior = val.getFloatValue();
170 if (ior < 0) {
171 fprintf(stderr,
172 "IOR of negative value is not allowed for materials (using Blender default value "
173 "instead)\n");
174 return;
175 }
176
177 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "IOR");
178 ((bNodeSocketValueFloat *)socket->default_value)->value = ior;
179}
180
181void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
182 COLLADAFW::ColorOrTexture &cot,
183 COLLADAFW::FloatOrParam &val)
184{
185 /* Handling the alpha value according to the Collada 1.4 reference guide
186 * see page 7-5 Determining Transparency (Opacity). */
187
188 if (effect == nullptr) {
189 return;
190 }
191
192 if (cot.isColor() || !cot.isValid()) {
193 /* transparent_cot is either a color or not defined */
194
195 float transparent_alpha;
196 if (cot.isValid()) {
197 COLLADAFW::Color col = cot.getColor();
198 transparent_alpha = col.getAlpha();
199 }
200 else {
201 /* no transparent color defined */
202 transparent_alpha = 1;
203 }
204
205 float transparency_alpha = val.getFloatValue();
206 if (transparency_alpha < 0) {
207 /* transparency is not defined */
208 transparency_alpha = 1; /* set to opaque */
209 }
210
211 float alpha = transparent_alpha * transparency_alpha;
212 if (mode == COLLADAFW::EffectCommon::RGB_ZERO) {
213 alpha = 1 - alpha;
214 }
215
216 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Alpha");
217 ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
218 material->a = alpha;
219 }
220 else if (cot.isTexture()) {
221 int locy = -300 * (node_map.size() - 2);
222 add_texture_node(cot, -300, locy, "Alpha");
223 }
224}
225
226void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
227{
228 int locy = -300 * (node_map.size() - 2);
229
230 if (cot.isTexture()) {
231 bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
232 if (texture_node != nullptr) {
233 add_link(texture_node, 0, shader_node, 0);
234 }
235 }
236 else {
237 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Base Color");
238 float *fcol = (float *)socket->default_value;
239
240 if (cot.isColor()) {
241 COLLADAFW::Color col = cot.getColor();
242 fcol[0] = material->r = col.getRed();
243 fcol[1] = material->g = col.getGreen();
244 fcol[2] = material->b = col.getBlue();
245 fcol[3] = material->a = col.getAlpha();
246 }
247 else {
248 /* no diffuse term = same as black */
249 fcol[0] = material->r = 0.0f;
250 fcol[1] = material->g = 0.0f;
251 fcol[2] = material->b = 0.0f;
252 fcol[3] = material->a = 1.0f;
253 }
254 }
255}
256
258{
259 ntree->ensure_topology_cache();
260 const blender::Span<const bNode *> nodes = ntree->nodes_by_type("ShaderNodeBsdfPrincipled");
261 if (nodes.is_empty()) {
262 return nullptr;
263 }
264 const bNode *shader = nodes.first();
265
266 const bNodeSocket *in_socket = blender::bke::node_find_socket(*shader, SOCK_IN, "Base Color");
267 if (in_socket == nullptr) {
268 return nullptr;
269 }
270
271 const bNodeLink *link = in_socket->link;
272 if (link == nullptr) {
273 return nullptr;
274 }
275
276 const bNode *texture = link->fromnode;
277 if (texture == nullptr) {
278 return nullptr;
279 }
280
281 if (texture->type_legacy != SH_NODE_TEX_IMAGE) {
282 return nullptr;
283 }
284
285 Image *image = (Image *)texture->id;
286 return image;
287}
288
289static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col)
290{
291 bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0);
292 float *fcol = (float *)socket->default_value;
293 fcol[0] = col.getRed();
294 fcol[1] = col.getGreen();
295 fcol[2] = col.getBlue();
296
297 return socket;
298}
299
300void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot)
301{
302 int locy = -300 * (node_map.size() - 2);
303 if (cot.isColor()) {
304 COLLADAFW::Color col = cot.getColor();
305 bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient");
306 set_color(node, col);
307 /* TODO: Connect node */
308 }
309 /* texture */
310 else if (cot.isTexture()) {
311 add_texture_node(cot, -300, locy, "Ambient");
312 /* TODO: Connect node */
313 }
314}
315
316void MaterialNode::set_reflective(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, "Reflective");
322 set_color(node, col);
323 /* TODO: Connect node */
324 }
325 /* texture */
326 else if (cot.isTexture()) {
327 add_texture_node(cot, -300, locy, "Reflective");
328 /* TODO: Connect node */
329 }
330}
331
332void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
333{
334 int locy = -300 * (node_map.size() - 2);
335 if (cot.isColor()) {
336 COLLADAFW::Color col = cot.getColor();
337 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Emission Color");
338 float *fcol = (float *)socket->default_value;
339
340 fcol[0] = col.getRed();
341 fcol[1] = col.getGreen();
342 fcol[2] = col.getBlue();
343 fcol[3] = col.getAlpha();
344 }
345 // texture
346 else if (cot.isTexture()) {
347 bNode *texture_node = add_texture_node(cot, -300, locy, "Emission Color");
348 if (texture_node != nullptr) {
349 add_link(texture_node, "Color", shader_node, "Emission Color");
350 }
351 }
352
353 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Emission Strength");
354 if (socket) {
355 *(float *)socket->default_value = 1.0f;
356 }
357}
358
359void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
360{
361 if (effect == nullptr) {
362 return;
363 }
364
365 int locy = -300 * (node_map.size() - 2);
366 if (cot.isColor()) {
367 COLLADAFW::Color col = effect->getTransparent().getColor();
368 float alpha = effect->getTransparency().getFloatValue();
369
370 if (col.isValid()) {
371 alpha *= col.getAlpha(); /* Assuming A_ONE opaque mode */
372 }
373
374 bNodeSocket *socket = blender::bke::node_find_socket(*shader_node, SOCK_IN, "Alpha");
375 ((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
376 }
377 /* texture */
378 else if (cot.isTexture()) {
379 add_texture_node(cot, -300, locy, "Alpha");
380 /* TODO: Connect node */
381 }
382}
383
384void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
385{
386 bool has_specularity = true;
387 int locy = -300 * (node_map.size() - 2);
388 if (cot.isColor()) {
389 COLLADAFW::Color col = cot.getColor();
390
391 if (col.getRed() == 0 && col.getGreen() == 0 && col.getBlue() == 0) {
392 has_specularity = false;
393 }
394 else {
395 bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular IOR Level");
396 set_color(node, col);
397 /* TODO: Connect node */
398 }
399 }
400 else if (cot.isTexture()) {
401 add_texture_node(cot, -300, locy, "Specular IOR Level");
402 /* TODO: Connect node */
403 }
404 else {
405 /* no specular term) */
406 has_specularity = false;
407 }
408
409 if (!has_specularity) {
410 /* If specularity is black or not defined reset the Specular value to 0
411 * TODO: This is a solution only for a corner case. We must find a better
412 * way to handle specularity in general. Also note that currently we
413 * do not export specularity values, see EffectExporter::operator() */
415 *shader_node, SOCK_IN, "Specular IOR Level");
416 ((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f;
417 }
418}
419
420bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
421 int locx,
422 int locy,
423 std::string label)
424{
425 if (effect == nullptr) {
426 return nullptr;
427 }
428
429 UidImageMap &image_map = *uid_image_map;
430
431 COLLADAFW::Texture ctex = cot.getTexture();
432
433 COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray();
434 COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
435
436 const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage();
437
438 if (image_map.find(ima_uid) == image_map.end()) {
439 fprintf(stderr, "Couldn't find an image by UID.\n");
440 return nullptr;
441 }
442
443 Image *ima = image_map[ima_uid];
444 bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label);
445 texture_node->id = &ima->id;
446 return texture_node;
447}
Main * CTX_data_main(const bContext *C)
#define SH_NODE_MIX_SHADER
#define SH_NODE_TEX_IMAGE
#define SH_NODE_BSDF_PRINCIPLED
#define SH_NODE_EMISSION
#define SH_NODE_ADD_SHADER
#define SH_NODE_BSDF_TRANSPARENT
#define SH_NODE_OUTPUT_MATERIAL
#define SH_NODE_RGB
void BKE_ntree_update_after_single_tree_change(Main &bmain, bNodeTree &modified_tree, const NodeTreeUpdateExtraParams &params={})
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
struct Image Image
@ NODE_SELECT
@ SOCK_OUT
@ SOCK_IN
struct bNode bNode
struct bNodeSocket bNodeSocket
static bNodeSocket * set_color(bNode *node, COLLADAFW::Color col)
#define C
Definition RandGen.cpp:29
void set_reflectivity(COLLADAFW::FloatOrParam &val)
void set_shininess(COLLADAFW::FloatOrParam &val)
void set_specular(COLLADAFW::ColorOrTexture &cot)
void update_material_nodetree()
void set_ior(COLLADAFW::FloatOrParam &val)
void set_reflective(COLLADAFW::ColorOrTexture &cot)
void set_emission(COLLADAFW::ColorOrTexture &cot)
Image * get_diffuse_image()
MaterialNode(bContext *C, COLLADAFW::EffectCommon *ef, Material *ma, UidImageMap &uid_image_map)
Definition Materials.cpp:27
void set_opacity(COLLADAFW::ColorOrTexture &cot)
void set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, COLLADAFW::ColorOrTexture &cot, COLLADAFW::FloatOrParam &val)
void set_diffuse(COLLADAFW::ColorOrTexture &cot)
void set_ambient(COLLADAFW::ColorOrTexture &cot)
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr bool is_empty() const
Definition BLI_span.hh:260
std::map< std::string, Image * > KeyImageMap
std::map< COLLADAFW::UniqueId, Image * > UidImageMap
uint col
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
bNodeTree * node_tree_add_tree_embedded(Main *bmain, ID *owner_id, StringRefNull name, StringRefNull idname)
Definition node.cc:4375
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:2864
bNode * node_add_static_node(const bContext *C, bNodeTree &ntree, int type)
Definition node.cc:3804
bNodeLink & node_add_link(bNodeTree &ntree, bNode &fromnode, bNodeSocket &fromsock, bNode &tonode, bNodeSocket &tosock)
Definition node.cc:4087
struct bNodeLink * link
void * default_value
float location[2]
ListBase inputs
struct ID * id
char label[64]
ListBase outputs