diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index f501a01e14b..60fa2fc11b3 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -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 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; - - SocketType::Type type = xml_read_socket_type(param, "type"); - if(type == SocketType::UNDEFINED) - continue; - - osl->add_input(ustring(name).c_str(), type); + snode = ((OSLShaderManager*)manager)->osl_node(filepath); + + if(!snode) { + fprintf(stderr, "Failed to create OSL node from \"%s\".\n", filepath.c_str()); + } } - 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); + else { + fprintf(stderr, "OSL node missing \"src\" attribute.\n"); } } - - snode = osl; + else { + fprintf(stderr, "OSL node without using --shadingsys osl.\n"); + } } else if(string_iequals(node.name(), "sky_texture")) { SkyTextureNode *sky = new SkyTextureNode(); diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 5f16c1b6738..eb96756201a 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -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; diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index ea68a671029..a73204bc190 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -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); diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 43dc1dd26fe..9ece9b86eac 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -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 { diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/render/osl.cpp index c1112ce44f6..1cfe3fb38e2 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/render/osl.cpp @@ -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 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_) diff --git a/intern/cycles/render/osl.h b/intern/cycles/render/osl.h index 110897ff300..13b9d6307f9 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/render/osl.h @@ -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();