408 lines
12 KiB
C++
408 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 nullptr;
|
|
}
|
|
|
|
material->nodetree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
|
|
material->use_nodes = true;
|
|
ntree = material->nodetree;
|
|
return ntree;
|
|
}
|
|
|
|
void MaterialNode::update_material_nodetree()
|
|
{
|
|
ntreeUpdateTree(CTX_data_main(mContext), 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 != nullptr) {
|
|
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();
|
|
}
|
|
// texture
|
|
else if (cot.isTexture()) {
|
|
bNode *texture_node = add_texture_node(cot, -300, locy, "Emission");
|
|
if (texture_node != nullptr) {
|
|
add_link(texture_node, 0, shader_node, 0);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 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;
|
|
}
|