Add a dedicated `owner_id` pointer to ID types that can be embedded (Collections and NodeTrees), and modify slightly come code to make handling those more safe and consistent. This implements first part of T69169. Reviewed By: brecht Differential Revision: https://developer.blender.org/D15838
437 lines
13 KiB
C++
437 lines
13 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "Materials.h"
|
|
|
|
#include "BKE_node_tree_update.h"
|
|
|
|
MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map)
|
|
: mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map)
|
|
{
|
|
bNodeTree *new_ntree = prepare_material_nodetree();
|
|
setShaderType();
|
|
if (new_ntree) {
|
|
shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
|
|
output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
|
|
add_link(shader_node, 0, output_node, 0);
|
|
}
|
|
}
|
|
|
|
MaterialNode::MaterialNode(bContext *C,
|
|
COLLADAFW::EffectCommon *ef,
|
|
Material *ma,
|
|
UidImageMap &uid_image_map)
|
|
: mContext(C), material(ma), effect(ef), uid_image_map(&uid_image_map)
|
|
{
|
|
prepare_material_nodetree();
|
|
setShaderType();
|
|
|
|
std::map<std::string, bNode *> nmap;
|
|
#if 0
|
|
nmap["main"] = add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300);
|
|
nmap["emission"] = add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission");
|
|
nmap["add"] = add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400);
|
|
nmap["transparent"] = add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200);
|
|
nmap["mix"] = add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency");
|
|
nmap["out"] = add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300);
|
|
nmap["out"]->flag &= ~NODE_SELECT;
|
|
|
|
add_link(ntree, nmap["emission"], 0, nmap["add"], 0);
|
|
add_link(ntree, nmap["main"], 0, nmap["add"], 1);
|
|
add_link(ntree, nmap["add"], 0, nmap["mix"], 1);
|
|
add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2);
|
|
|
|
add_link(ntree, nmap["mix"], 0, nmap["out"], 0);
|
|
/* experimental, probably not used. */
|
|
make_group(C, ntree, nmap);
|
|
#else
|
|
shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, "");
|
|
output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, "");
|
|
add_link(shader_node, 0, output_node, 0);
|
|
#endif
|
|
}
|
|
|
|
void MaterialNode::setShaderType()
|
|
{
|
|
#if 0
|
|
COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType();
|
|
/* Currently we only support PBR based shaders */
|
|
/* TODO: simulate the effects with PBR */
|
|
|
|
/* blinn */
|
|
if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) {
|
|
ma->spec_shader = MA_SPEC_BLINN;
|
|
ma->spec = ef->getShininess().getFloatValue();
|
|
}
|
|
/* phong */
|
|
else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) {
|
|
ma->spec_shader = MA_SPEC_PHONG;
|
|
ma->har = ef->getShininess().getFloatValue();
|
|
}
|
|
/* lambert */
|
|
else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) {
|
|
ma->diff_shader = MA_DIFF_LAMBERT;
|
|
}
|
|
/* default - lambert */
|
|
else {
|
|
ma->diff_shader = MA_DIFF_LAMBERT;
|
|
fprintf(stderr, "Current shader type is not supported, default to lambert.\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bNodeTree *MaterialNode::prepare_material_nodetree()
|
|
{
|
|
if (material->nodetree) {
|
|
ntree = material->nodetree;
|
|
return nullptr;
|
|
}
|
|
|
|
ntreeAddTreeEmbedded(nullptr, &material->id, "Shader Nodetree", "ShaderNodeTree");
|
|
material->use_nodes = true;
|
|
ntree = material->nodetree;
|
|
return ntree;
|
|
}
|
|
|
|
void MaterialNode::update_material_nodetree()
|
|
{
|
|
BKE_ntree_update_main_tree(CTX_data_main(mContext), ntree, nullptr);
|
|
}
|
|
|
|
bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label)
|
|
{
|
|
bNode *node = nodeAddStaticNode(mContext, ntree, node_type);
|
|
if (node) {
|
|
if (label.length() > 0) {
|
|
strcpy(node->label, label.c_str());
|
|
}
|
|
node->locx = locx;
|
|
node->locy = locy;
|
|
node->flag |= NODE_SELECT;
|
|
}
|
|
node_map[label] = node;
|
|
return node;
|
|
}
|
|
|
|
void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, int to_index)
|
|
{
|
|
bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
|
|
bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
|
|
|
|
nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
|
|
}
|
|
|
|
void MaterialNode::add_link(bNode *from_node,
|
|
const char *from_label,
|
|
bNode *to_node,
|
|
const char *to_label)
|
|
{
|
|
bNodeSocket *from_socket = nodeFindSocket(from_node, SOCK_OUT, from_label);
|
|
bNodeSocket *to_socket = nodeFindSocket(to_node, SOCK_IN, to_label);
|
|
|
|
if (from_socket && to_socket) {
|
|
nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
|
|
}
|
|
}
|
|
|
|
void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val)
|
|
{
|
|
float reflectivity = val.getFloatValue();
|
|
if (reflectivity >= 0) {
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Metallic");
|
|
((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity;
|
|
material->metallic = reflectivity;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* needs rework to be done for 2.81 */
|
|
void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val)
|
|
{
|
|
float roughness = val.getFloatValue();
|
|
if (roughness >= 0) {
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Roughness");
|
|
((bNodeSocketValueFloat *)socket->default_value)->value = roughness;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val)
|
|
{
|
|
float ior = val.getFloatValue();
|
|
if (ior < 0) {
|
|
fprintf(stderr,
|
|
"IOR of negative value is not allowed for materials (using Blender default value "
|
|
"instead)");
|
|
return;
|
|
}
|
|
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "IOR");
|
|
((bNodeSocketValueFloat *)socket->default_value)->value = ior;
|
|
}
|
|
|
|
void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode,
|
|
COLLADAFW::ColorOrTexture &cot,
|
|
COLLADAFW::FloatOrParam &val)
|
|
{
|
|
/* Handling the alpha value according to the Collada 1.4 reference guide
|
|
* see page 7-5 Determining Transparency (Opacity)
|
|
*/
|
|
|
|
if (effect == nullptr) {
|
|
return;
|
|
}
|
|
|
|
if (cot.isColor() || !cot.isValid()) {
|
|
/* transparent_cot is either a color or not defined */
|
|
|
|
float transparent_alpha;
|
|
if (cot.isValid()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
transparent_alpha = col.getAlpha();
|
|
}
|
|
else {
|
|
/* no transparent color defined */
|
|
transparent_alpha = 1;
|
|
}
|
|
|
|
float transparency_alpha = val.getFloatValue();
|
|
if (transparency_alpha < 0) {
|
|
/* transparency is not defined */
|
|
transparency_alpha = 1; /* set to opaque */
|
|
}
|
|
|
|
float alpha = transparent_alpha * transparency_alpha;
|
|
if (mode == COLLADAFW::EffectCommon::RGB_ZERO) {
|
|
alpha = 1 - alpha;
|
|
}
|
|
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
|
|
((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
|
|
material->a = alpha;
|
|
}
|
|
else if (cot.isTexture()) {
|
|
int locy = -300 * (node_map.size() - 2);
|
|
add_texture_node(cot, -300, locy, "Alpha");
|
|
}
|
|
}
|
|
|
|
void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot)
|
|
{
|
|
int locy = -300 * (node_map.size() - 2);
|
|
|
|
if (cot.isTexture()) {
|
|
bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
|
|
if (texture_node != nullptr) {
|
|
add_link(texture_node, 0, shader_node, 0);
|
|
}
|
|
}
|
|
else {
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
|
|
float *fcol = (float *)socket->default_value;
|
|
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
fcol[0] = material->r = col.getRed();
|
|
fcol[1] = material->g = col.getGreen();
|
|
fcol[2] = material->b = col.getBlue();
|
|
fcol[3] = material->a = col.getAlpha();
|
|
}
|
|
else {
|
|
/* no diffuse term = same as black */
|
|
fcol[0] = material->r = 0.0f;
|
|
fcol[1] = material->g = 0.0f;
|
|
fcol[2] = material->b = 0.0f;
|
|
fcol[3] = material->a = 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
Image *MaterialNode::get_diffuse_image()
|
|
{
|
|
bNode *shader = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED);
|
|
if (shader == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
bNodeSocket *in_socket = nodeFindSocket(shader, SOCK_IN, "Base Color");
|
|
if (in_socket == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
bNodeLink *link = in_socket->link;
|
|
if (link == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
bNode *texture = link->fromnode;
|
|
if (texture == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (texture->type != SH_NODE_TEX_IMAGE) {
|
|
return nullptr;
|
|
}
|
|
|
|
Image *image = (Image *)texture->id;
|
|
return image;
|
|
}
|
|
|
|
static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col)
|
|
{
|
|
bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0);
|
|
float *fcol = (float *)socket->default_value;
|
|
fcol[0] = col.getRed();
|
|
fcol[1] = col.getGreen();
|
|
fcol[2] = col.getBlue();
|
|
|
|
return socket;
|
|
}
|
|
|
|
void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot)
|
|
{
|
|
int locy = -300 * (node_map.size() - 2);
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient");
|
|
set_color(node, col);
|
|
/* TODO: Connect node */
|
|
}
|
|
/* texture */
|
|
else if (cot.isTexture()) {
|
|
add_texture_node(cot, -300, locy, "Ambient");
|
|
/* TODO: Connect node */
|
|
}
|
|
}
|
|
|
|
void MaterialNode::set_reflective(COLLADAFW::ColorOrTexture &cot)
|
|
{
|
|
int locy = -300 * (node_map.size() - 2);
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
bNode *node = add_node(SH_NODE_RGB, -300, locy, "Reflective");
|
|
set_color(node, col);
|
|
/* TODO: Connect node */
|
|
}
|
|
/* texture */
|
|
else if (cot.isTexture()) {
|
|
add_texture_node(cot, -300, locy, "Reflective");
|
|
/* TODO: Connect node */
|
|
}
|
|
}
|
|
|
|
void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot)
|
|
{
|
|
int locy = -300 * (node_map.size() - 2);
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission");
|
|
float *fcol = (float *)socket->default_value;
|
|
|
|
fcol[0] = col.getRed();
|
|
fcol[1] = col.getGreen();
|
|
fcol[2] = col.getBlue();
|
|
fcol[3] = col.getAlpha();
|
|
}
|
|
// texture
|
|
else if (cot.isTexture()) {
|
|
bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
|
|
if (texture_node != nullptr) {
|
|
add_link(texture_node, "Color", shader_node, "Emission");
|
|
}
|
|
}
|
|
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission Strength");
|
|
if (socket) {
|
|
*(float *)socket->default_value = 1.0f;
|
|
}
|
|
}
|
|
|
|
void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot)
|
|
{
|
|
if (effect == nullptr) {
|
|
return;
|
|
}
|
|
|
|
int locy = -300 * (node_map.size() - 2);
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = effect->getTransparent().getColor();
|
|
float alpha = effect->getTransparency().getFloatValue();
|
|
|
|
if (col.isValid()) {
|
|
alpha *= col.getAlpha(); /* Assuming A_ONE opaque mode */
|
|
}
|
|
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha");
|
|
((bNodeSocketValueFloat *)socket->default_value)->value = alpha;
|
|
}
|
|
/* texture */
|
|
else if (cot.isTexture()) {
|
|
add_texture_node(cot, -300, locy, "Alpha");
|
|
/* TODO: Connect node */
|
|
}
|
|
}
|
|
|
|
void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot)
|
|
{
|
|
bool has_specularity = true;
|
|
int locy = -300 * (node_map.size() - 2);
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
|
|
if (col.getRed() == 0 && col.getGreen() == 0 && col.getBlue() == 0) {
|
|
has_specularity = false;
|
|
}
|
|
else {
|
|
bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
|
|
set_color(node, col);
|
|
/* TODO: Connect node */
|
|
}
|
|
}
|
|
else if (cot.isTexture()) {
|
|
add_texture_node(cot, -300, locy, "Specular");
|
|
/* TODO: Connect node */
|
|
}
|
|
else {
|
|
/* no specular term) */
|
|
has_specularity = false;
|
|
}
|
|
|
|
if (!has_specularity) {
|
|
/* If specularity is black or not defined reset the Specular value to 0
|
|
* TODO: This is a solution only for a corner case. We must find a better
|
|
* way to handle specularity in general. Also note that currently we
|
|
* do not export specularity values, see EffectExporter::operator() */
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Specular");
|
|
((bNodeSocketValueFloat *)socket->default_value)->value = 0.0f;
|
|
}
|
|
}
|
|
|
|
bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot,
|
|
int locx,
|
|
int locy,
|
|
std::string label)
|
|
{
|
|
if (effect == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
UidImageMap &image_map = *uid_image_map;
|
|
|
|
COLLADAFW::Texture ctex = cot.getTexture();
|
|
|
|
COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray();
|
|
COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
|
|
|
|
const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage();
|
|
|
|
if (image_map.find(ima_uid) == image_map.end()) {
|
|
fprintf(stderr, "Couldn't find an image by UID.\n");
|
|
return nullptr;
|
|
}
|
|
|
|
Image *ima = image_map[ima_uid];
|
|
bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label);
|
|
texture_node->id = &ima->id;
|
|
return texture_node;
|
|
}
|