forked from bartvdbraak/blender
Code refactor: centralize OSL node creation in shader manager.
This commit is contained in:
parent
af073e149b
commit
a70a435f28
@ -31,6 +31,7 @@
|
||||
#include "mesh.h"
|
||||
#include "nodes.h"
|
||||
#include "object.h"
|
||||
#include "osl.h"
|
||||
#include "shader.h"
|
||||
#include "scene.h"
|
||||
|
||||
@ -250,35 +251,6 @@ static bool xml_read_enum_value(int *value, NodeEnum& enm, pugi::xml_node node,
|
||||
return false;
|
||||
}
|
||||
|
||||
static SocketType::Type xml_read_socket_type(pugi::xml_node node, const char *name)
|
||||
{
|
||||
pugi::xml_attribute attr = node.attribute(name);
|
||||
|
||||
if(attr) {
|
||||
string value = attr.value();
|
||||
if(string_iequals(value, "float"))
|
||||
return SocketType::FLOAT;
|
||||
else if(string_iequals(value, "int"))
|
||||
return SocketType::INT;
|
||||
else if(string_iequals(value, "color"))
|
||||
return SocketType::COLOR;
|
||||
else if(string_iequals(value, "vector"))
|
||||
return SocketType::VECTOR;
|
||||
else if(string_iequals(value, "point"))
|
||||
return SocketType::POINT;
|
||||
else if(string_iequals(value, "normal"))
|
||||
return SocketType::NORMAL;
|
||||
else if(string_iequals(value, "closure color"))
|
||||
return SocketType::CLOSURE;
|
||||
else if(string_iequals(value, "string"))
|
||||
return SocketType::STRING;
|
||||
else
|
||||
fprintf(stderr, "Unknown shader socket type \"%s\" for attribute \"%s\".\n", value.c_str(), name);
|
||||
}
|
||||
|
||||
return SocketType::UNDEFINED;
|
||||
}
|
||||
|
||||
/* Camera */
|
||||
|
||||
static void xml_read_camera(XMLReadState& state, pugi::xml_node node)
|
||||
@ -313,6 +285,7 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
|
||||
{
|
||||
xml_read_node(state, shader, graph_node);
|
||||
|
||||
ShaderManager *manager = state.scene->shader_manager;
|
||||
ShaderGraph *graph = new ShaderGraph();
|
||||
|
||||
map<string, ShaderNode*> nodemap;
|
||||
@ -352,45 +325,27 @@ static void xml_read_shader_graph(XMLReadState& state, Shader *shader, pugi::xml
|
||||
snode = env;
|
||||
}
|
||||
else if(string_iequals(node.name(), "osl_shader")) {
|
||||
OSLScriptNode *osl = new OSLScriptNode();
|
||||
if(manager->use_osl()) {
|
||||
std::string filepath;
|
||||
|
||||
/* Source */
|
||||
xml_read_string(&osl->filepath, node, "src");
|
||||
if(path_is_relative(osl->filepath)) {
|
||||
osl->filepath = path_join(state.base, osl->filepath);
|
||||
if(xml_read_string(&filepath, node, "src")) {
|
||||
if(path_is_relative(filepath)) {
|
||||
filepath = path_join(state.base, filepath);
|
||||
}
|
||||
|
||||
/* Generate inputs/outputs from node sockets
|
||||
*
|
||||
* Note: ShaderInput/ShaderOutput store shallow string copies only!
|
||||
* So we register them as ustring to ensure the pointer stays valid. */
|
||||
/* read input values */
|
||||
for(pugi::xml_node param = node.first_child(); param; param = param.next_sibling()) {
|
||||
if(string_iequals(param.name(), "input")) {
|
||||
string name;
|
||||
if(!xml_read_string(&name, param, "name"))
|
||||
continue;
|
||||
snode = ((OSLShaderManager*)manager)->osl_node(filepath);
|
||||
|
||||
SocketType::Type type = xml_read_socket_type(param, "type");
|
||||
if(type == SocketType::UNDEFINED)
|
||||
continue;
|
||||
|
||||
osl->add_input(ustring(name).c_str(), type);
|
||||
}
|
||||
else if(string_iequals(param.name(), "output")) {
|
||||
string name;
|
||||
if(!xml_read_string(&name, param, "name"))
|
||||
continue;
|
||||
|
||||
SocketType::Type type = xml_read_socket_type(param, "type");
|
||||
if(type == SocketType::UNDEFINED)
|
||||
continue;
|
||||
|
||||
osl->add_output(ustring(name).c_str(), type);
|
||||
if(!snode) {
|
||||
fprintf(stderr, "Failed to create OSL node from \"%s\".\n", filepath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
snode = osl;
|
||||
else {
|
||||
fprintf(stderr, "OSL node missing \"src\" attribute.\n");
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "OSL node without using --shadingsys osl.\n");
|
||||
}
|
||||
}
|
||||
else if(string_iequals(node.name(), "sky_texture")) {
|
||||
SkyTextureNode *sky = new SkyTextureNode();
|
||||
|
@ -148,29 +148,6 @@ static SocketType::Type convert_socket_type(BL::NodeSocket& b_socket)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_OSL
|
||||
static SocketType::Type convert_osl_socket_type(OSL::OSLQuery& query,
|
||||
BL::NodeSocket& b_socket)
|
||||
{
|
||||
SocketType::Type socket_type = convert_socket_type(b_socket);
|
||||
if(socket_type == SocketType::VECTOR) {
|
||||
/* TODO(sergey): Do we need compatible_name() here? */
|
||||
const OSL::OSLQuery::Parameter *param = query.getparam(b_socket.name());
|
||||
assert(param != NULL);
|
||||
if(param != NULL) {
|
||||
if(param->type.vecsemantics == TypeDesc::POINT) {
|
||||
socket_type = SocketType::POINT;
|
||||
}
|
||||
else if(param->type.vecsemantics == TypeDesc::NORMAL) {
|
||||
socket_type = SocketType::NORMAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return socket_type;
|
||||
}
|
||||
#endif /* WITH_OSL */
|
||||
|
||||
static void set_default_value(ShaderInput *input,
|
||||
BL::NodeSocket& b_sock,
|
||||
BL::BlendData& b_data,
|
||||
@ -582,62 +559,17 @@ static ShaderNode *add_node(Scene *scene,
|
||||
if(scene->shader_manager->use_osl()) {
|
||||
/* create script node */
|
||||
BL::ShaderNodeScript b_script_node(b_node);
|
||||
OSLScriptNode *script_node = new OSLScriptNode();
|
||||
|
||||
OSLShaderManager *manager = (OSLShaderManager*)scene->shader_manager;
|
||||
string bytecode_hash = b_script_node.bytecode_hash();
|
||||
|
||||
/* Gather additional information from the shader, such as
|
||||
* input/output type info needed for proper node construction.
|
||||
*/
|
||||
OSL::OSLQuery query;
|
||||
string absolute_filepath;
|
||||
|
||||
if(!bytecode_hash.empty()) {
|
||||
query.open_bytecode(b_script_node.bytecode());
|
||||
node = manager->osl_node("", bytecode_hash, b_script_node.bytecode());
|
||||
}
|
||||
else {
|
||||
absolute_filepath = blender_absolute_path(b_data, b_ntree, b_script_node.filepath());
|
||||
OSLShaderManager::osl_query(query, absolute_filepath);
|
||||
string absolute_filepath = blender_absolute_path(b_data, b_ntree, b_script_node.filepath());
|
||||
node = manager->osl_node(absolute_filepath, "");
|
||||
}
|
||||
/* TODO(sergey): Add proper query info error parsing. */
|
||||
|
||||
/* Generate inputs/outputs from node sockets
|
||||
*
|
||||
* Note: the node sockets are generated from OSL parameters,
|
||||
* so the names match those of the corresponding parameters exactly.
|
||||
*
|
||||
* Note 2: ShaderInput/ShaderOutput store shallow string copies only!
|
||||
* So we register them as ustring to ensure the pointer stays valid. */
|
||||
BL::Node::inputs_iterator b_input;
|
||||
|
||||
for(b_script_node.inputs.begin(b_input); b_input != b_script_node.inputs.end(); ++b_input) {
|
||||
ShaderInput *input = script_node->add_input(ustring(b_input->name()).c_str(),
|
||||
convert_osl_socket_type(query, *b_input));
|
||||
set_default_value(input, *b_input, b_data, b_ntree);
|
||||
}
|
||||
|
||||
BL::Node::outputs_iterator b_output;
|
||||
|
||||
for(b_script_node.outputs.begin(b_output); b_output != b_script_node.outputs.end(); ++b_output) {
|
||||
script_node->add_output(ustring(b_output->name()).c_str(),
|
||||
convert_osl_socket_type(query, *b_output));
|
||||
}
|
||||
|
||||
/* load bytecode or filepath */
|
||||
if(!bytecode_hash.empty()) {
|
||||
/* loaded bytecode if not already done */
|
||||
if(!manager->shader_test_loaded(bytecode_hash))
|
||||
manager->shader_load_bytecode(bytecode_hash, b_script_node.bytecode());
|
||||
|
||||
script_node->bytecode_hash = bytecode_hash;
|
||||
}
|
||||
else {
|
||||
/* set filepath */
|
||||
script_node->filepath = absolute_filepath;
|
||||
}
|
||||
|
||||
node = script_node;
|
||||
}
|
||||
#else
|
||||
(void)b_data;
|
||||
|
@ -4516,20 +4516,29 @@ void SetNormalNode::compile(OSLCompiler& compiler)
|
||||
compiler.add(this, "node_set_normal");
|
||||
}
|
||||
|
||||
/* OSLScriptNode */
|
||||
/* OSLNode */
|
||||
|
||||
OSLScriptNode::OSLScriptNode()
|
||||
: ShaderNode("osl_script")
|
||||
OSLNode::OSLNode()
|
||||
: ShaderNode("osl_shader")
|
||||
{
|
||||
special_type = SHADER_SPECIAL_TYPE_SCRIPT;
|
||||
}
|
||||
|
||||
void OSLScriptNode::compile(SVMCompiler& /*compiler*/)
|
||||
OSLNode::~OSLNode()
|
||||
{
|
||||
}
|
||||
|
||||
OSLNode* OSLNode::create(size_t)
|
||||
{
|
||||
return new OSLNode();
|
||||
}
|
||||
|
||||
void OSLNode::compile(SVMCompiler&)
|
||||
{
|
||||
/* doesn't work for SVM, obviously ... */
|
||||
}
|
||||
|
||||
void OSLScriptNode::compile(OSLCompiler& compiler)
|
||||
void OSLNode::compile(OSLCompiler& compiler)
|
||||
{
|
||||
if(!filepath.empty())
|
||||
compiler.add(this, filepath.c_str(), true);
|
||||
|
@ -966,17 +966,22 @@ public:
|
||||
SHADER_NODE_CLASS(SetNormalNode)
|
||||
};
|
||||
|
||||
class OSLScriptNode : public ShaderNode {
|
||||
class OSLNode : public ShaderNode {
|
||||
public:
|
||||
SHADER_NODE_CLASS(OSLScriptNode)
|
||||
static OSLNode *create(size_t num_inputs);
|
||||
~OSLNode();
|
||||
|
||||
SHADER_NODE_BASE_CLASS(OSLNode)
|
||||
|
||||
/* ideally we could beter detect this, but we can't query this now */
|
||||
bool has_spatial_varying() { return true; }
|
||||
virtual bool equals(const ShaderNode * /*other*/) { return false; }
|
||||
|
||||
string filepath;
|
||||
string bytecode_hash;
|
||||
|
||||
virtual bool equals(const ShaderNode * /*other*/) { return false; }
|
||||
private:
|
||||
OSLNode();
|
||||
};
|
||||
|
||||
class NormalMapNode : public ShaderNode {
|
||||
|
@ -394,16 +394,143 @@ const char *OSLShaderManager::shader_load_bytecode(const string& hash, const str
|
||||
{
|
||||
ss->LoadMemoryCompiledShader(hash.c_str(), bytecode.c_str());
|
||||
|
||||
/* this is a bit weak, but works */
|
||||
OSLShaderInfo info;
|
||||
|
||||
if(!info.query.open_bytecode(bytecode)) {
|
||||
fprintf(stderr, "OSL query error: %s\n", info.query.geterror().c_str());
|
||||
}
|
||||
|
||||
/* this is a bit weak, but works */
|
||||
info.has_surface_emission = (bytecode.find("\"emission\"") != string::npos);
|
||||
info.has_surface_transparent = (bytecode.find("\"transparent\"") != string::npos);
|
||||
info.has_surface_bssrdf = (bytecode.find("\"bssrdf\"") != string::npos);
|
||||
|
||||
loaded_shaders[hash] = info;
|
||||
|
||||
return loaded_shaders.find(hash)->first.c_str();
|
||||
}
|
||||
|
||||
OSLNode *OSLShaderManager::osl_node(const std::string& filepath,
|
||||
const std::string& bytecode_hash,
|
||||
const std::string& bytecode)
|
||||
{
|
||||
/* create query */
|
||||
const char *hash;
|
||||
|
||||
if(!filepath.empty()) {
|
||||
hash = shader_load_filepath(filepath);
|
||||
}
|
||||
else {
|
||||
hash = shader_test_loaded(bytecode_hash);
|
||||
if(!hash)
|
||||
hash = shader_load_bytecode(bytecode_hash, bytecode);
|
||||
}
|
||||
|
||||
if(!hash) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OSLShaderInfo *info = shader_loaded_info(hash);
|
||||
|
||||
/* count number of inputs */
|
||||
size_t num_inputs = 0;
|
||||
|
||||
for(int i = 0; i < info->query.nparams(); i++) {
|
||||
const OSL::OSLQuery::Parameter *param = info->query.getparam(i);
|
||||
|
||||
/* skip unsupported types */
|
||||
if(param->varlenarray || param->isstruct || param->type.arraylen > 1)
|
||||
continue;
|
||||
|
||||
if(!param->isoutput)
|
||||
num_inputs++;
|
||||
}
|
||||
|
||||
/* create node */
|
||||
OSLNode *node = OSLNode::create(num_inputs);
|
||||
|
||||
/* add new sockets from parameters */
|
||||
set<void*> used_sockets;
|
||||
|
||||
for(int i = 0; i < info->query.nparams(); i++) {
|
||||
const OSL::OSLQuery::Parameter *param = info->query.getparam(i);
|
||||
|
||||
/* skip unsupported types */
|
||||
if(param->varlenarray || param->isstruct || param->type.arraylen > 1)
|
||||
continue;
|
||||
|
||||
SocketType::Type socket_type;
|
||||
|
||||
if(param->isclosure) {
|
||||
socket_type = SocketType::CLOSURE;
|
||||
}
|
||||
else if(param->type.vecsemantics != TypeDesc::NOSEMANTICS) {
|
||||
if(param->type.vecsemantics == TypeDesc::COLOR)
|
||||
socket_type = SocketType::COLOR;
|
||||
else if(param->type.vecsemantics == TypeDesc::POINT)
|
||||
socket_type = SocketType::POINT;
|
||||
else if(param->type.vecsemantics == TypeDesc::VECTOR)
|
||||
socket_type = SocketType::VECTOR;
|
||||
else if(param->type.vecsemantics == TypeDesc::NORMAL)
|
||||
socket_type = SocketType::NORMAL;
|
||||
else
|
||||
continue;
|
||||
|
||||
if(!param->isoutput && param->validdefault) {
|
||||
node->add_input(param->name.c_str(), socket_type, make_float3(param->fdefault[0], param->fdefault[1], param->fdefault[2]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(param->type.aggregate == TypeDesc::SCALAR) {
|
||||
if(param->type.basetype == TypeDesc::INT) {
|
||||
socket_type = SocketType::INT;
|
||||
|
||||
if(!param->isoutput && param->validdefault) {
|
||||
node->add_input(param->name.c_str(), socket_type, (float)param->idefault[0]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(param->type.basetype == TypeDesc::FLOAT) {
|
||||
socket_type = SocketType::FLOAT;
|
||||
|
||||
if(!param->isoutput && param->validdefault) {
|
||||
node->add_input(param->name.c_str(), socket_type, param->fdefault[0]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(param->type.basetype == TypeDesc::STRING) {
|
||||
socket_type = SocketType::STRING;
|
||||
|
||||
if(!param->isoutput && param->validdefault) {
|
||||
node->add_input(param->name.c_str(), socket_type);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
if(param->isoutput) {
|
||||
node->add_output(param->name.c_str(), socket_type);
|
||||
}
|
||||
else {
|
||||
node->add_input(param->name.c_str(), socket_type);
|
||||
}
|
||||
}
|
||||
|
||||
/* set bytcode hash or filepath */
|
||||
if(!bytecode_hash.empty()) {
|
||||
node->bytecode_hash = bytecode_hash;
|
||||
}
|
||||
else {
|
||||
node->filepath = filepath;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/* Graph Compiler */
|
||||
|
||||
OSLCompiler::OSLCompiler(void *manager_, void *shadingsys_, ImageManager *image_manager_)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "util_thread.h"
|
||||
|
||||
#include "graph.h"
|
||||
#include "nodes.h"
|
||||
#include "shader.h"
|
||||
|
||||
#ifdef WITH_OSL
|
||||
@ -54,6 +55,7 @@ struct OSLShaderInfo {
|
||||
has_surface_bssrdf(false)
|
||||
{}
|
||||
|
||||
OSL::OSLQuery query;
|
||||
bool has_surface_emission;
|
||||
bool has_surface_transparent;
|
||||
bool has_surface_bssrdf;
|
||||
@ -83,6 +85,11 @@ public:
|
||||
const char *shader_load_filepath(string filepath);
|
||||
OSLShaderInfo *shader_loaded_info(const string& hash);
|
||||
|
||||
/* create OSL node using OSLQuery */
|
||||
OSLNode *osl_node(const std::string& filepath,
|
||||
const std::string& bytecode_hash = "",
|
||||
const std::string& bytecode = "");
|
||||
|
||||
protected:
|
||||
void texture_system_init();
|
||||
void texture_system_free();
|
||||
|
Loading…
Reference in New Issue
Block a user