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