I also changed the Collada importer scuh that the diffuse Alpha is now set for the generated PBSDF Shader AND for the base color. Note: we can not export the base color AND the color from the PBSDF Shaders at the same time. And when importing we do not know if the color was originally coming from a base material or from a shader. Setting them both in import seems to give the most consistent results.
397 lines
12 KiB
C++
397 lines
12 KiB
C++
/*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "Materials.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
|
|
}
|
|
|
|
// returns null if material already has a node tree
|
|
bNodeTree *MaterialNode::prepare_material_nodetree()
|
|
{
|
|
if (material->nodetree) {
|
|
ntree = material->nodetree;
|
|
return NULL;
|
|
}
|
|
|
|
material->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree");
|
|
material->use_nodes = true;
|
|
ntree = material->nodetree;
|
|
return ntree;
|
|
}
|
|
|
|
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::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.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color");
|
|
float *fcol = (float *)socket->default_value;
|
|
|
|
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 if (cot.isTexture()) {
|
|
bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color");
|
|
if (texture_node != NULL) {
|
|
add_link(texture_node, 0, shader_node, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
else if (cot.isTexture()) {
|
|
bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
|
|
if (texture_node != NULL) {
|
|
add_link(texture_node, 0, shader_node, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
int locy = -300 * (node_map.size() - 2);
|
|
if (cot.isColor()) {
|
|
COLLADAFW::Color col = cot.getColor();
|
|
bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular");
|
|
set_color(node, col);
|
|
// TODO: Connect node
|
|
}
|
|
// texture
|
|
else if (cot.isTexture()) {
|
|
add_texture_node(cot, -300, locy, "Specular");
|
|
// TODO: Connect node
|
|
}
|
|
}
|
|
|
|
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 NULL;
|
|
}
|
|
|
|
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;
|
|
}
|