Cycles OSL: shader script node
Documentation here: http://wiki.blender.org/index.php/Doc:2.6/Manual/Render/Cycles/Nodes/OSL http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.65/Cycles These changes require an OSL build from this repository: https://github.com/DingTo/OpenShadingLanguage The lib/ OSL has not been updated yet, so you might want to keep OSL disabled until that is done. Still todo: * Auto update for external .osl files not working currently, press update manually * Node could indicate better when a refresh is needed * Attributes like UV or generated coordinates may be missing when requested from an OSL shader, need a way to request them to be loaded by cycles * Expose string, enum and other non-socket parameters * Scons build support Thanks to Thomas, Lukas and Dalai for the implementation.
This commit is contained in:
		@@ -38,6 +38,7 @@ set(ADDON_FILES
 | 
			
		||||
	addon/__init__.py
 | 
			
		||||
	addon/engine.py 
 | 
			
		||||
	addon/enums.py
 | 
			
		||||
	addon/osl.py
 | 
			
		||||
	addon/presets.py
 | 
			
		||||
	addon/properties.py
 | 
			
		||||
	addon/ui.py
 | 
			
		||||
 
 | 
			
		||||
@@ -71,6 +71,13 @@ class CyclesRender(bpy.types.RenderEngine):
 | 
			
		||||
    def view_draw(self, context):
 | 
			
		||||
        engine.draw(self, context.region, context.space_data, context.region_data)
 | 
			
		||||
 | 
			
		||||
    def update_script_node(self, node):
 | 
			
		||||
        if engine.with_osl():
 | 
			
		||||
            from . import osl
 | 
			
		||||
            osl.update_script_node(node, self.report)
 | 
			
		||||
        else:
 | 
			
		||||
            self.report({'ERROR'}, "OSL support disabled in this build.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def register():
 | 
			
		||||
    properties.register()
 | 
			
		||||
@@ -84,3 +91,4 @@ def unregister():
 | 
			
		||||
    properties.unregister()
 | 
			
		||||
    presets.unregister()
 | 
			
		||||
    bpy.utils.unregister_module(__name__)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								intern/cycles/blender/addon/osl.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								intern/cycles/blender/addon/osl.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
#
 | 
			
		||||
# Copyright 2011, Blender Foundation.
 | 
			
		||||
#
 | 
			
		||||
# 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.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
# <pep8 compliant>
 | 
			
		||||
 | 
			
		||||
import bpy, _cycles, os, tempfile
 | 
			
		||||
 | 
			
		||||
# compile .osl file with given filepath to temporary .oso file
 | 
			
		||||
def osl_compile(input_path, report):
 | 
			
		||||
    output_file = tempfile.NamedTemporaryFile(mode='w', suffix=".oso", delete=False)
 | 
			
		||||
    output_path = output_file.name
 | 
			
		||||
    output_file.close()
 | 
			
		||||
 | 
			
		||||
    ok = _cycles.osl_compile(input_path, output_path)
 | 
			
		||||
 | 
			
		||||
    if ok:
 | 
			
		||||
        report({'INFO'}, "OSL shader compilation succeeded")
 | 
			
		||||
 | 
			
		||||
    return ok, output_path
 | 
			
		||||
 | 
			
		||||
# compile and update shader script node
 | 
			
		||||
def update_script_node(node, report):
 | 
			
		||||
    import os, shutil
 | 
			
		||||
 | 
			
		||||
    if node.mode == 'EXTERNAL':
 | 
			
		||||
        # compile external script file
 | 
			
		||||
        script_path = bpy.path.abspath(node.filepath)
 | 
			
		||||
        script_path_noext, script_ext = os.path.splitext(script_path)
 | 
			
		||||
 | 
			
		||||
        if script_ext == ".oso":
 | 
			
		||||
            # it's a .oso file, no need to compile
 | 
			
		||||
            ok, oso_path = True, script_path
 | 
			
		||||
            oso_file_remove = False
 | 
			
		||||
        elif script_ext == ".osl":
 | 
			
		||||
            # compile .osl file
 | 
			
		||||
            ok, oso_path = osl_compile(script_path, report)
 | 
			
		||||
            oso_file_remove = True
 | 
			
		||||
 | 
			
		||||
            if ok:
 | 
			
		||||
                # copy .oso from temporary path to .osl directory
 | 
			
		||||
                dst_path = script_path_noext + ".oso"
 | 
			
		||||
                try:
 | 
			
		||||
                    shutil.copy2(oso_path, dst_path)
 | 
			
		||||
                except:
 | 
			
		||||
                    report({'ERROR'}, "Failed to write .oso file next to external .osl file at " + dst_path)
 | 
			
		||||
        elif os.path.dirname(node.filepath) == "":
 | 
			
		||||
            # module in search path
 | 
			
		||||
            oso_path = node.filepath
 | 
			
		||||
            oso_file_remove = False
 | 
			
		||||
            ok = True
 | 
			
		||||
        else:
 | 
			
		||||
            # unknown
 | 
			
		||||
            report({'ERROR'}, "External shader script must have .osl or .oso extension, or be a module name")
 | 
			
		||||
            ok = False
 | 
			
		||||
 | 
			
		||||
        if ok:
 | 
			
		||||
            node.bytecode = ""
 | 
			
		||||
            node.bytecode_hash = ""
 | 
			
		||||
 | 
			
		||||
    elif node.mode == 'INTERNAL' and node.script:
 | 
			
		||||
        # internal script, we will store bytecode in the node
 | 
			
		||||
        script = node.script
 | 
			
		||||
        osl_path = bpy.path.abspath(script.filepath)
 | 
			
		||||
 | 
			
		||||
        if script.is_in_memory or script.is_dirty or script.is_modified or not os.path.exists(osl_path):
 | 
			
		||||
            # write text datablock contents to temporary file
 | 
			
		||||
            osl_file = tempfile.NamedTemporaryFile(mode='w', suffix=".osl", delete=True)
 | 
			
		||||
            osl_file.write(script.as_string())
 | 
			
		||||
            osl_file.flush()
 | 
			
		||||
            ok, oso_path = osl_compile(osl_file.name, report)
 | 
			
		||||
            oso_file_remove = False
 | 
			
		||||
            osl_file.close()
 | 
			
		||||
        else:
 | 
			
		||||
            # compile text datablock from disk directly
 | 
			
		||||
            ok, oso_path = osl_compile(osl_path, report)
 | 
			
		||||
            oso_file_remove = False
 | 
			
		||||
 | 
			
		||||
        if ok:
 | 
			
		||||
            # read bytecode
 | 
			
		||||
            try:
 | 
			
		||||
                oso = open(oso_path, 'r')
 | 
			
		||||
                node.bytecode = oso.read()
 | 
			
		||||
                oso.close()
 | 
			
		||||
            except:
 | 
			
		||||
                report({'ERROR'}, "Can't read OSO bytecode to store in node at " + oso_path)
 | 
			
		||||
                ok = False
 | 
			
		||||
    
 | 
			
		||||
    else:
 | 
			
		||||
        report({'WARNING'}, "No text or file specified in node, nothing to compile")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if ok:
 | 
			
		||||
        # now update node with new sockets
 | 
			
		||||
        ok = _cycles.osl_update_node(node.id_data.as_pointer(), node.as_pointer(), oso_path)
 | 
			
		||||
 | 
			
		||||
        if not ok:
 | 
			
		||||
            report({'ERROR'}, "OSL query failed to open " + oso_path)
 | 
			
		||||
    else:
 | 
			
		||||
        report({'ERROR'}, "OSL script compilation failed, see console for errors")
 | 
			
		||||
 | 
			
		||||
    # remove temporary oso file
 | 
			
		||||
    if oso_file_remove:
 | 
			
		||||
        try:
 | 
			
		||||
            os.remove(oso_path)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
    return ok
 | 
			
		||||
 | 
			
		||||
@@ -24,9 +24,17 @@
 | 
			
		||||
#include "blender_session.h"
 | 
			
		||||
 | 
			
		||||
#include "util_foreach.h"
 | 
			
		||||
#include "util_md5.h"
 | 
			
		||||
#include "util_opengl.h"
 | 
			
		||||
#include "util_path.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
#include "osl.h"
 | 
			
		||||
 | 
			
		||||
#include <OSL/oslquery.h>
 | 
			
		||||
#include <OSL/oslconfig.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
CCL_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
static PyObject *init_func(PyObject *self, PyObject *args)
 | 
			
		||||
@@ -163,6 +171,170 @@ static PyObject *available_devices_func(PyObject *self, PyObject *args)
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
static PyObject *osl_update_node_func(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
	PyObject *pynodegroup, *pynode;
 | 
			
		||||
	const char *filepath = NULL;
 | 
			
		||||
 | 
			
		||||
	if(!PyArg_ParseTuple(args, "OOs", &pynodegroup, &pynode, &filepath))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	/* RNA */
 | 
			
		||||
	PointerRNA nodeptr;
 | 
			
		||||
	RNA_pointer_create((ID*)PyLong_AsVoidPtr(pynodegroup), &RNA_ShaderNodeScript, (void*)PyLong_AsVoidPtr(pynode), &nodeptr);
 | 
			
		||||
	BL::ShaderNodeScript b_node(nodeptr);
 | 
			
		||||
 | 
			
		||||
	/* update bytecode hash */
 | 
			
		||||
	string bytecode = b_node.bytecode();
 | 
			
		||||
 | 
			
		||||
	if(!bytecode.empty()) {
 | 
			
		||||
		MD5Hash md5;
 | 
			
		||||
		md5.append((const uint8_t*)bytecode.c_str(), bytecode.size());
 | 
			
		||||
		b_node.bytecode_hash(md5.get_hex().c_str());
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		b_node.bytecode_hash("");
 | 
			
		||||
 | 
			
		||||
	/* query from file path */
 | 
			
		||||
	OSL::OSLQuery query;
 | 
			
		||||
 | 
			
		||||
	if(!OSLShaderManager::osl_query(query, filepath))
 | 
			
		||||
		Py_RETURN_FALSE;
 | 
			
		||||
 | 
			
		||||
	/* add new sockets from parameters */
 | 
			
		||||
	set<void*> used_sockets;
 | 
			
		||||
 | 
			
		||||
	for(int i = 0; i < query.nparams(); i++) {
 | 
			
		||||
		const OSL::OSLQuery::Parameter *param = query.getparam(i);
 | 
			
		||||
 | 
			
		||||
		/* skip unsupported types */
 | 
			
		||||
		if(param->varlenarray || param->isstruct || param->type.arraylen > 1)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* determine socket type */
 | 
			
		||||
		BL::NodeSocket::type_enum socket_type;
 | 
			
		||||
		float default_float4[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 | 
			
		||||
		float default_float = 0.0f;
 | 
			
		||||
		int default_int = 0;
 | 
			
		||||
		
 | 
			
		||||
		if(param->isclosure) {
 | 
			
		||||
			socket_type = BL::NodeSocket::type_SHADER;
 | 
			
		||||
		}
 | 
			
		||||
		else if(param->type.vecsemantics == TypeDesc::COLOR) {
 | 
			
		||||
			socket_type = BL::NodeSocket::type_RGBA;
 | 
			
		||||
 | 
			
		||||
			if(param->validdefault) {
 | 
			
		||||
				default_float4[0] = param->fdefault[0];
 | 
			
		||||
				default_float4[1] = param->fdefault[1];
 | 
			
		||||
				default_float4[2] = param->fdefault[2];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if(param->type.vecsemantics == TypeDesc::POINT ||
 | 
			
		||||
		        param->type.vecsemantics == TypeDesc::VECTOR ||
 | 
			
		||||
		        param->type.vecsemantics == TypeDesc::NORMAL) {
 | 
			
		||||
			socket_type = BL::NodeSocket::type_VECTOR;
 | 
			
		||||
 | 
			
		||||
			if(param->validdefault) {
 | 
			
		||||
				default_float4[0] = param->fdefault[0];
 | 
			
		||||
				default_float4[1] = param->fdefault[1];
 | 
			
		||||
				default_float4[2] = param->fdefault[2];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else if(param->type.aggregate == TypeDesc::SCALAR) {
 | 
			
		||||
			if(param->type.basetype == TypeDesc::INT) {
 | 
			
		||||
				socket_type = BL::NodeSocket::type_INT;
 | 
			
		||||
				if(param->validdefault)
 | 
			
		||||
					default_int = param->idefault[0];
 | 
			
		||||
			}
 | 
			
		||||
			else if(param->type.basetype == TypeDesc::FLOAT) {
 | 
			
		||||
				socket_type = BL::NodeSocket::type_VALUE;
 | 
			
		||||
				if(param->validdefault)
 | 
			
		||||
					default_float = param->fdefault[0];
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* find socket socket */
 | 
			
		||||
		BL::NodeSocket b_sock = b_node.find_socket(param->name.c_str(), param->isoutput);
 | 
			
		||||
 | 
			
		||||
		/* remove if type no longer matches */
 | 
			
		||||
		if(b_sock && b_sock.type() != socket_type) {
 | 
			
		||||
			b_node.remove_socket(b_sock);
 | 
			
		||||
			b_sock = BL::NodeSocket(PointerRNA_NULL);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* create new socket */
 | 
			
		||||
		if(!b_sock) {
 | 
			
		||||
			b_sock = b_node.add_socket(param->name.c_str(), socket_type, param->isoutput);
 | 
			
		||||
 | 
			
		||||
			/* set default value */
 | 
			
		||||
			if(socket_type == BL::NodeSocket::type_VALUE) {
 | 
			
		||||
				BL::NodeSocketFloatNone b_float_sock(b_sock.ptr);
 | 
			
		||||
				b_float_sock.default_value(default_float);
 | 
			
		||||
			}
 | 
			
		||||
			else if(socket_type == BL::NodeSocket::type_INT) {
 | 
			
		||||
				BL::NodeSocketIntNone b_int_sock(b_sock.ptr);
 | 
			
		||||
				b_int_sock.default_value(default_int);
 | 
			
		||||
			}
 | 
			
		||||
			else if(socket_type == BL::NodeSocket::type_RGBA) {
 | 
			
		||||
				BL::NodeSocketRGBA b_rgba_sock(b_sock.ptr);
 | 
			
		||||
				b_rgba_sock.default_value(default_float4);
 | 
			
		||||
			}
 | 
			
		||||
			else if(socket_type == BL::NodeSocket::type_VECTOR) {
 | 
			
		||||
				BL::NodeSocketVectorNone b_vector_sock(b_sock.ptr);
 | 
			
		||||
				b_vector_sock.default_value(default_float4);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		used_sockets.insert(b_sock.ptr.data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* remove unused parameters */
 | 
			
		||||
	bool removed;
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		BL::Node::inputs_iterator b_input;
 | 
			
		||||
		BL::Node::outputs_iterator b_output;
 | 
			
		||||
 | 
			
		||||
		removed = false;
 | 
			
		||||
 | 
			
		||||
		for (b_node.inputs.begin(b_input); b_input != b_node.inputs.end(); ++b_input) {
 | 
			
		||||
			if(used_sockets.find(b_input->ptr.data) == used_sockets.end()) {
 | 
			
		||||
				b_node.remove_socket(*b_input);
 | 
			
		||||
				removed = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for (b_node.outputs.begin(b_output); b_output != b_node.outputs.end(); ++b_output) {
 | 
			
		||||
			if(used_sockets.find(b_output->ptr.data) == used_sockets.end()) {
 | 
			
		||||
				b_node.remove_socket(*b_output);
 | 
			
		||||
				removed = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} while(removed);
 | 
			
		||||
 | 
			
		||||
	Py_RETURN_TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static PyObject *osl_compile_func(PyObject *self, PyObject *args)
 | 
			
		||||
{
 | 
			
		||||
	const char *inputfile = NULL, *outputfile = NULL;
 | 
			
		||||
 | 
			
		||||
	if(!PyArg_ParseTuple(args, "ss", &inputfile, &outputfile))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	
 | 
			
		||||
	/* return */
 | 
			
		||||
	if(!OSLShaderManager::osl_compile(inputfile, outputfile))
 | 
			
		||||
		Py_RETURN_FALSE;
 | 
			
		||||
 | 
			
		||||
	Py_RETURN_TRUE;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static PyMethodDef methods[] = {
 | 
			
		||||
	{"init", init_func, METH_VARARGS, ""},
 | 
			
		||||
	{"create", create_func, METH_VARARGS, ""},
 | 
			
		||||
@@ -170,6 +342,10 @@ static PyMethodDef methods[] = {
 | 
			
		||||
	{"render", render_func, METH_O, ""},
 | 
			
		||||
	{"draw", draw_func, METH_VARARGS, ""},
 | 
			
		||||
	{"sync", sync_func, METH_O, ""},
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
	{"osl_update_node", osl_update_node_func, METH_VARARGS, ""},
 | 
			
		||||
	{"osl_compile", osl_compile_func, METH_VARARGS, ""},
 | 
			
		||||
#endif
 | 
			
		||||
	{"available_devices", available_devices_func, METH_NOARGS, ""},
 | 
			
		||||
	{NULL, NULL, 0, NULL},
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
#include "graph.h"
 | 
			
		||||
#include "light.h"
 | 
			
		||||
#include "nodes.h"
 | 
			
		||||
#include "osl.h"
 | 
			
		||||
#include "scene.h"
 | 
			
		||||
#include "shader.h"
 | 
			
		||||
 | 
			
		||||
@@ -159,7 +160,7 @@ static void get_tex_mapping(TextureMapping *mapping, BL::ShaderNodeMapping b_map
 | 
			
		||||
		mapping->max = get_float3(b_mapping.max());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ShaderNode *add_node(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNode b_node)
 | 
			
		||||
static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, BL::ShaderNode b_node)
 | 
			
		||||
{
 | 
			
		||||
	ShaderNode *node = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -413,6 +414,58 @@ static ShaderNode *add_node(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph
 | 
			
		||||
			node = new BumpNode();
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case BL::ShaderNode::type_SCRIPT: {
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
			if(scene->params.shadingsystem != SceneParams::OSL)
 | 
			
		||||
				break;
 | 
			
		||||
 | 
			
		||||
			/* create script node */
 | 
			
		||||
			BL::ShaderNodeScript b_script_node(b_node);
 | 
			
		||||
			OSLScriptNode *script_node = new OSLScriptNode();
 | 
			
		||||
			
 | 
			
		||||
			/* 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!
 | 
			
		||||
			 * Socket names must be stored in the extra lists instead. */
 | 
			
		||||
			BL::Node::inputs_iterator b_input;
 | 
			
		||||
 | 
			
		||||
			for (b_script_node.inputs.begin(b_input); b_input != b_script_node.inputs.end(); ++b_input) {
 | 
			
		||||
				script_node->input_names.push_back(ustring(b_input->name()));
 | 
			
		||||
				ShaderInput *input = script_node->add_input(script_node->input_names.back().c_str(), convert_socket_type(b_input->type()));
 | 
			
		||||
				set_default_value(input, *b_input);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			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->output_names.push_back(ustring(b_output->name()));
 | 
			
		||||
				script_node->add_output(script_node->output_names.back().c_str(), convert_socket_type(b_output->type()));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* load bytecode or filepath */
 | 
			
		||||
			OSLShaderManager *manager = (OSLShaderManager*)scene->shader_manager;
 | 
			
		||||
			string bytecode_hash = b_script_node.bytecode_hash();
 | 
			
		||||
 | 
			
		||||
			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 = blender_absolute_path(b_data, b_ntree, b_script_node.filepath());
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			node = script_node;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case BL::ShaderNode::type_TEX_IMAGE: {
 | 
			
		||||
			BL::ShaderNodeTexImage b_image_node(b_node);
 | 
			
		||||
			BL::Image b_image(b_image_node.image());
 | 
			
		||||
@@ -576,7 +629,7 @@ static SocketPair node_socket_map_pair(PtrNodeMap& node_map, BL::Node b_node, BL
 | 
			
		||||
	return SocketPair(node_map[b_node.ptr.data], name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void add_nodes(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, PtrSockMap& sockets_map)
 | 
			
		||||
static void add_nodes(Scene *scene, BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *graph, BL::ShaderNodeTree b_ntree, PtrSockMap& sockets_map)
 | 
			
		||||
{
 | 
			
		||||
	/* add nodes */
 | 
			
		||||
	BL::ShaderNodeTree::nodes_iterator b_node;
 | 
			
		||||
@@ -651,10 +704,10 @@ static void add_nodes(BL::BlendData b_data, BL::Scene b_scene, ShaderGraph *grap
 | 
			
		||||
				set_default_value(proxy->inputs[0], b_output->group_socket());
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			add_nodes(b_data, b_scene, graph, b_group_ntree, group_sockmap);
 | 
			
		||||
			add_nodes(scene, b_data, b_scene, graph, b_group_ntree, group_sockmap);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			ShaderNode *node = add_node(b_data, b_scene, graph, BL::ShaderNode(*b_node));
 | 
			
		||||
			ShaderNode *node = add_node(scene, b_data, b_scene, graph, b_ntree, BL::ShaderNode(*b_node));
 | 
			
		||||
			
 | 
			
		||||
			if(node) {
 | 
			
		||||
				BL::Node::inputs_iterator b_input;
 | 
			
		||||
@@ -744,7 +797,7 @@ void BlenderSync::sync_materials()
 | 
			
		||||
				PtrSockMap sock_to_node;
 | 
			
		||||
				BL::ShaderNodeTree b_ntree(b_mat->node_tree());
 | 
			
		||||
 | 
			
		||||
				add_nodes(b_data, b_scene, graph, b_ntree, sock_to_node);
 | 
			
		||||
				add_nodes(scene, b_data, b_scene, graph, b_ntree, sock_to_node);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				ShaderNode *closure, *out;
 | 
			
		||||
@@ -785,7 +838,7 @@ void BlenderSync::sync_world()
 | 
			
		||||
			PtrSockMap sock_to_node;
 | 
			
		||||
			BL::ShaderNodeTree b_ntree(b_world.node_tree());
 | 
			
		||||
 | 
			
		||||
			add_nodes(b_data, b_scene, graph, b_ntree, sock_to_node);
 | 
			
		||||
			add_nodes(scene, b_data, b_scene, graph, b_ntree, sock_to_node);
 | 
			
		||||
		}
 | 
			
		||||
		else if(b_world) {
 | 
			
		||||
			ShaderNode *closure, *out;
 | 
			
		||||
@@ -844,7 +897,7 @@ void BlenderSync::sync_lamps()
 | 
			
		||||
				PtrSockMap sock_to_node;
 | 
			
		||||
				BL::ShaderNodeTree b_ntree(b_lamp->node_tree());
 | 
			
		||||
 | 
			
		||||
				add_nodes(b_data, b_scene, graph, b_ntree, sock_to_node);
 | 
			
		||||
				add_nodes(scene, b_data, b_scene, graph, b_ntree, sock_to_node);
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				ShaderNode *closure, *out;
 | 
			
		||||
 
 | 
			
		||||
@@ -2922,5 +2922,25 @@ void SetNormalNode::compile(OSLCompiler& compiler)
 | 
			
		||||
	compiler.add(this, "node_set_normal"); 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* OSLScriptNode */
 | 
			
		||||
 | 
			
		||||
OSLScriptNode::OSLScriptNode()
 | 
			
		||||
: ShaderNode("osl_script")
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OSLScriptNode::compile(SVMCompiler& compiler)
 | 
			
		||||
{
 | 
			
		||||
	/* doesn't work for SVM, obviously ... */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OSLScriptNode::compile(OSLCompiler& compiler)
 | 
			
		||||
{
 | 
			
		||||
	if(!filepath.empty())
 | 
			
		||||
		compiler.add(this, filepath.c_str(), true);
 | 
			
		||||
	else
 | 
			
		||||
		compiler.add(this, bytecode_hash.c_str(), false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CCL_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -445,6 +445,18 @@ public:
 | 
			
		||||
	SHADER_NODE_CLASS(SetNormalNode)
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class OSLScriptNode : public ShaderNode {
 | 
			
		||||
public:
 | 
			
		||||
	SHADER_NODE_CLASS(OSLScriptNode)
 | 
			
		||||
	string filepath;
 | 
			
		||||
	string bytecode_hash;
 | 
			
		||||
	
 | 
			
		||||
	/* ShaderInput/ShaderOutput only stores a shallow string copy (const char *)!
 | 
			
		||||
	 * The actual socket names have to be stored externally to avoid memory errors. */
 | 
			
		||||
	vector<ustring> input_names;
 | 
			
		||||
	vector<ustring> output_names;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CCL_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
#endif /* __NODES_H__ */
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@
 | 
			
		||||
#include "osl_shader.h"
 | 
			
		||||
 | 
			
		||||
#include "util_foreach.h"
 | 
			
		||||
#include "util_md5.h"
 | 
			
		||||
#include "util_path.h"
 | 
			
		||||
#include "util_progress.h"
 | 
			
		||||
 | 
			
		||||
@@ -46,36 +47,8 @@ OSLShaderManager::OSLShaderManager()
 | 
			
		||||
{
 | 
			
		||||
	services = new OSLRenderServices();
 | 
			
		||||
 | 
			
		||||
	/* if we let OSL create it, it leaks */
 | 
			
		||||
	ts = TextureSystem::create(true);
 | 
			
		||||
	ts->attribute("automip",  1);
 | 
			
		||||
	ts->attribute("autotile", 64);
 | 
			
		||||
 | 
			
		||||
	ss = OSL::ShadingSystem::create(services, ts, &errhandler);
 | 
			
		||||
	ss->attribute("lockgeom", 1);
 | 
			
		||||
	ss->attribute("commonspace", "world");
 | 
			
		||||
	ss->attribute("optimize", 2);
 | 
			
		||||
	//ss->attribute("debug", 1);
 | 
			
		||||
	//ss->attribute("statistics:level", 1);
 | 
			
		||||
	ss->attribute("searchpath:shader", path_get("shader").c_str());
 | 
			
		||||
 | 
			
		||||
	/* our own ray types */
 | 
			
		||||
	static const char *raytypes[] = {
 | 
			
		||||
		"camera",		/* PATH_RAY_CAMERA */
 | 
			
		||||
		"reflection",	/* PATH_RAY_REFLECT */
 | 
			
		||||
		"refraction",	/* PATH_RAY_TRANSMIT */
 | 
			
		||||
		"diffuse",		/* PATH_RAY_DIFFUSE */
 | 
			
		||||
		"glossy",		/* PATH_RAY_GLOSSY */
 | 
			
		||||
		"singular",		/* PATH_RAY_SINGULAR */
 | 
			
		||||
		"transparent",	/* PATH_RAY_TRANSPARENT */
 | 
			
		||||
		"shadow",		/* PATH_RAY_SHADOW_OPAQUE */
 | 
			
		||||
		"shadow",		/* PATH_RAY_SHADOW_TRANSPARENT */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const int nraytypes = sizeof(raytypes)/sizeof(raytypes[0]);
 | 
			
		||||
	ss->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes);
 | 
			
		||||
 | 
			
		||||
	OSLShader::register_closures(ss);
 | 
			
		||||
	shading_system_init();
 | 
			
		||||
	texture_system_init();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OSLShaderManager::~OSLShaderManager()
 | 
			
		||||
@@ -87,13 +60,6 @@ OSLShaderManager::~OSLShaderManager()
 | 
			
		||||
 | 
			
		||||
void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress)
 | 
			
		||||
{
 | 
			
		||||
	/* test if we need to update */
 | 
			
		||||
	bool need_update = false;
 | 
			
		||||
 | 
			
		||||
	foreach(Shader *shader, scene->shaders)
 | 
			
		||||
		if(shader->need_update)
 | 
			
		||||
			need_update = true;
 | 
			
		||||
	
 | 
			
		||||
	if(!need_update)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
@@ -113,7 +79,7 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
 | 
			
		||||
		if(shader->sample_as_light && shader->has_surface_emission)
 | 
			
		||||
			scene->light_manager->need_update = true;
 | 
			
		||||
 | 
			
		||||
		OSLCompiler compiler((void*)ss);
 | 
			
		||||
		OSLCompiler compiler((void*)this, (void*)ss);
 | 
			
		||||
		compiler.background = (shader == scene->shaders[scene->default_background]);
 | 
			
		||||
		compiler.compile(og, shader);
 | 
			
		||||
	}
 | 
			
		||||
@@ -129,6 +95,8 @@ void OSLShaderManager::device_update(Device *device, DeviceScene *dscene, Scene
 | 
			
		||||
 | 
			
		||||
	foreach(Shader *shader, scene->shaders)
 | 
			
		||||
		shader->need_update = false;
 | 
			
		||||
 | 
			
		||||
	need_update = false;
 | 
			
		||||
	
 | 
			
		||||
	/* set texture system */
 | 
			
		||||
	scene->image_manager->set_osl_texture_system((void*)ts);
 | 
			
		||||
@@ -154,10 +122,165 @@ void OSLShaderManager::device_free(Device *device, DeviceScene *dscene)
 | 
			
		||||
	og->background_state.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OSLShaderManager::texture_system_init()
 | 
			
		||||
{
 | 
			
		||||
	/* if we let OSL create it, it leaks */
 | 
			
		||||
	ts = TextureSystem::create(true);
 | 
			
		||||
	ts->attribute("automip",  1);
 | 
			
		||||
	ts->attribute("autotile", 64);
 | 
			
		||||
 | 
			
		||||
	/* effectively unlimited for now, until we support proper mipmap lookups */
 | 
			
		||||
	ts->attribute("max_memory_MB", 16384);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OSLShaderManager::shading_system_init()
 | 
			
		||||
{
 | 
			
		||||
	ss = OSL::ShadingSystem::create(services, ts, &errhandler);
 | 
			
		||||
	ss->attribute("lockgeom", 1);
 | 
			
		||||
	ss->attribute("commonspace", "world");
 | 
			
		||||
	ss->attribute("optimize", 2);
 | 
			
		||||
	//ss->attribute("debug", 1);
 | 
			
		||||
	//ss->attribute("statistics:level", 1);
 | 
			
		||||
	ss->attribute("searchpath:shader", path_get("shader"));
 | 
			
		||||
 | 
			
		||||
	/* our own ray types */
 | 
			
		||||
	static const char *raytypes[] = {
 | 
			
		||||
		"camera",		/* PATH_RAY_CAMERA */
 | 
			
		||||
		"reflection",	/* PATH_RAY_REFLECT */
 | 
			
		||||
		"refraction",	/* PATH_RAY_TRANSMIT */
 | 
			
		||||
		"diffuse",		/* PATH_RAY_DIFFUSE */
 | 
			
		||||
		"glossy",		/* PATH_RAY_GLOSSY */
 | 
			
		||||
		"singular",		/* PATH_RAY_SINGULAR */
 | 
			
		||||
		"transparent",	/* PATH_RAY_TRANSPARENT */
 | 
			
		||||
		"shadow",		/* PATH_RAY_SHADOW_OPAQUE */
 | 
			
		||||
		"shadow",		/* PATH_RAY_SHADOW_TRANSPARENT */
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	const int nraytypes = sizeof(raytypes)/sizeof(raytypes[0]);
 | 
			
		||||
	ss->attribute("raytypes", TypeDesc(TypeDesc::STRING, nraytypes), raytypes);
 | 
			
		||||
 | 
			
		||||
	OSLShader::register_closures(ss);
 | 
			
		||||
 | 
			
		||||
	loaded_shaders.clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OSLShaderManager::osl_compile(const string& inputfile, const string& outputfile)
 | 
			
		||||
{
 | 
			
		||||
	vector<string> options;
 | 
			
		||||
	string stdosl_path;
 | 
			
		||||
 | 
			
		||||
	/* specify output file name */
 | 
			
		||||
	options.push_back("-o");
 | 
			
		||||
	options.push_back(outputfile);
 | 
			
		||||
 | 
			
		||||
	/* specify standard include path */
 | 
			
		||||
	options.push_back("-I" + path_get("shader"));
 | 
			
		||||
	stdosl_path = path_get("shader/stdosl.h");
 | 
			
		||||
 | 
			
		||||
	/* compile */
 | 
			
		||||
	OSL::OSLCompiler *compiler = OSL::OSLCompiler::create();
 | 
			
		||||
	bool ok = compiler->compile(inputfile, options, stdosl_path);
 | 
			
		||||
	delete compiler;
 | 
			
		||||
 | 
			
		||||
	return ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool OSLShaderManager::osl_query(OSL::OSLQuery& query, const string& filepath)
 | 
			
		||||
{
 | 
			
		||||
	string searchpath = path_user_get("shaders");
 | 
			
		||||
	return query.open(filepath, searchpath);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static string shader_filepath_hash(const string& filepath, uint64_t modified_time)
 | 
			
		||||
{
 | 
			
		||||
	/* compute a hash from filepath and modified time to detect changes */
 | 
			
		||||
	MD5Hash md5;
 | 
			
		||||
	md5.append((const uint8_t*)filepath.c_str(), filepath.size());
 | 
			
		||||
	md5.append((const uint8_t*)&modified_time, sizeof(modified_time));
 | 
			
		||||
 | 
			
		||||
	return md5.get_hex();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *OSLShaderManager::shader_test_loaded(const string& hash)
 | 
			
		||||
{
 | 
			
		||||
	set<string>::iterator it = loaded_shaders.find(hash);
 | 
			
		||||
	return (it == loaded_shaders.end())? NULL: it->c_str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *OSLShaderManager::shader_load_filepath(string filepath)
 | 
			
		||||
{
 | 
			
		||||
	size_t len = filepath.size();
 | 
			
		||||
	string extension = filepath.substr(len - 4);
 | 
			
		||||
	uint64_t modified_time = path_modified_time(filepath);
 | 
			
		||||
 | 
			
		||||
	if(extension == ".osl") {
 | 
			
		||||
		/* .OSL File */
 | 
			
		||||
		string osopath = filepath.substr(0, len - 4) + ".oso";
 | 
			
		||||
		uint64_t oso_modified_time = path_modified_time(osopath);
 | 
			
		||||
 | 
			
		||||
		/* test if we have loaded the corresponding .OSO already */
 | 
			
		||||
		if(oso_modified_time != 0) {
 | 
			
		||||
			const char *hash = shader_test_loaded(shader_filepath_hash(osopath, oso_modified_time));
 | 
			
		||||
 | 
			
		||||
			if(hash)
 | 
			
		||||
				return hash;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* autocompile .OSL to .OSO if needed */
 | 
			
		||||
		if(oso_modified_time == 0 || (oso_modified_time < modified_time)) {
 | 
			
		||||
			OSLShaderManager::osl_compile(filepath, osopath);
 | 
			
		||||
			modified_time = path_modified_time(osopath);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			modified_time = oso_modified_time;
 | 
			
		||||
 | 
			
		||||
		filepath = osopath;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		if(extension == ".oso") {
 | 
			
		||||
			/* .OSO File, nothing to do */
 | 
			
		||||
		}
 | 
			
		||||
		else if(path_dirname(filepath) == "") {
 | 
			
		||||
			/* .OSO File in search path */
 | 
			
		||||
			filepath = path_join(path_user_get("shaders"), filepath + ".oso");
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			/* unknown file */
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* test if we have loaded this .OSO already */
 | 
			
		||||
		const char *hash = shader_test_loaded(shader_filepath_hash(filepath, modified_time));
 | 
			
		||||
 | 
			
		||||
		if(hash)
 | 
			
		||||
			return hash;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* read oso bytecode from file */
 | 
			
		||||
	string bytecode_hash = shader_filepath_hash(filepath, modified_time);
 | 
			
		||||
	string bytecode;
 | 
			
		||||
 | 
			
		||||
	if(!path_read_text(filepath, bytecode)) {
 | 
			
		||||
		fprintf(stderr, "Cycles shader graph: failed to read file %s\n", filepath.c_str());
 | 
			
		||||
		loaded_shaders.insert(bytecode_hash); /* to avoid repeat tries */
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return shader_load_bytecode(bytecode_hash, bytecode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *OSLShaderManager::shader_load_bytecode(const string& hash, const string& bytecode)
 | 
			
		||||
{
 | 
			
		||||
	ss->LoadMemoryShader(hash.c_str(), bytecode.c_str());
 | 
			
		||||
 | 
			
		||||
	return loaded_shaders.insert(hash).first->c_str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Graph Compiler */
 | 
			
		||||
 | 
			
		||||
OSLCompiler::OSLCompiler(void *shadingsys_)
 | 
			
		||||
OSLCompiler::OSLCompiler(void *manager_, void *shadingsys_)
 | 
			
		||||
{
 | 
			
		||||
	manager = manager_;
 | 
			
		||||
	shadingsys = shadingsys_;
 | 
			
		||||
	current_type = SHADER_TYPE_SURFACE;
 | 
			
		||||
	current_shader = NULL;
 | 
			
		||||
@@ -238,10 +361,18 @@ bool OSLCompiler::node_skip_input(ShaderNode *node, ShaderInput *input)
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OSLCompiler::add(ShaderNode *node, const char *name)
 | 
			
		||||
void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
 | 
			
		||||
{
 | 
			
		||||
	OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys;
 | 
			
		||||
 | 
			
		||||
	/* load filepath */
 | 
			
		||||
	if(isfilepath) {
 | 
			
		||||
		name = ((OSLShaderManager*)manager)->shader_load_filepath(name);
 | 
			
		||||
 | 
			
		||||
		if(name == NULL)
 | 
			
		||||
			return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* pass in fixed parameter values */
 | 
			
		||||
	foreach(ShaderInput *input, node->inputs) {
 | 
			
		||||
		if(!input->link) {
 | 
			
		||||
@@ -510,82 +641,85 @@ void OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
 | 
			
		||||
 | 
			
		||||
void OSLCompiler::compile(OSLGlobals *og, Shader *shader)
 | 
			
		||||
{
 | 
			
		||||
	OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys;
 | 
			
		||||
	ShaderGraph *graph = shader->graph;
 | 
			
		||||
	ShaderNode *output = (graph)? graph->output(): NULL;
 | 
			
		||||
	if(shader->need_update) {
 | 
			
		||||
		OSL::ShadingSystem *ss = (OSL::ShadingSystem*)shadingsys;
 | 
			
		||||
		ShaderGraph *graph = shader->graph;
 | 
			
		||||
		ShaderNode *output = (graph)? graph->output(): NULL;
 | 
			
		||||
 | 
			
		||||
	/* copy graph for shader with bump mapping */
 | 
			
		||||
	if(output->input("Surface")->link && output->input("Displacement")->link)
 | 
			
		||||
		if(!shader->graph_bump)
 | 
			
		||||
			shader->graph_bump = shader->graph->copy();
 | 
			
		||||
		/* copy graph for shader with bump mapping */
 | 
			
		||||
		if(output->input("Surface")->link && output->input("Displacement")->link)
 | 
			
		||||
			if(!shader->graph_bump)
 | 
			
		||||
				shader->graph_bump = shader->graph->copy();
 | 
			
		||||
 | 
			
		||||
	/* finalize */
 | 
			
		||||
	shader->graph->finalize(false, true);
 | 
			
		||||
	if(shader->graph_bump)
 | 
			
		||||
		shader->graph_bump->finalize(true, true);
 | 
			
		||||
		/* finalize */
 | 
			
		||||
		shader->graph->finalize(false, true);
 | 
			
		||||
		if(shader->graph_bump)
 | 
			
		||||
			shader->graph_bump->finalize(true, true);
 | 
			
		||||
 | 
			
		||||
	current_shader = shader;
 | 
			
		||||
		current_shader = shader;
 | 
			
		||||
 | 
			
		||||
	shader->has_surface = false;
 | 
			
		||||
	shader->has_surface_emission = false;
 | 
			
		||||
	shader->has_surface_transparent = false;
 | 
			
		||||
	shader->has_volume = false;
 | 
			
		||||
	shader->has_displacement = false;
 | 
			
		||||
		shader->has_surface = false;
 | 
			
		||||
		shader->has_surface_emission = false;
 | 
			
		||||
		shader->has_surface_transparent = false;
 | 
			
		||||
		shader->has_volume = false;
 | 
			
		||||
		shader->has_displacement = false;
 | 
			
		||||
 | 
			
		||||
	/* generate surface shader */
 | 
			
		||||
	if(shader->used && graph && output->input("Surface")->link) {
 | 
			
		||||
		compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
 | 
			
		||||
		og->surface_state.push_back(ss->state());
 | 
			
		||||
		/* generate surface shader */
 | 
			
		||||
		if(shader->used && graph && output->input("Surface")->link) {
 | 
			
		||||
			compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
 | 
			
		||||
			shader->osl_surface_ref = ss->state();
 | 
			
		||||
 | 
			
		||||
		if(shader->graph_bump) {
 | 
			
		||||
			if(shader->graph_bump) {
 | 
			
		||||
				ss->clear_state();
 | 
			
		||||
				compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			shader->osl_surface_bump_ref = ss->state();
 | 
			
		||||
			ss->clear_state();
 | 
			
		||||
 | 
			
		||||
			shader->has_surface = true;
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			shader->osl_surface_ref = OSL::ShadingAttribStateRef();
 | 
			
		||||
			shader->osl_surface_bump_ref = OSL::ShadingAttribStateRef();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/* generate volume shader */
 | 
			
		||||
		if(shader->used && graph && output->input("Volume")->link) {
 | 
			
		||||
			compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
 | 
			
		||||
			shader->has_volume = true;
 | 
			
		||||
 | 
			
		||||
			shader->osl_volume_ref = ss->state();
 | 
			
		||||
			ss->clear_state();
 | 
			
		||||
			compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
 | 
			
		||||
			og->surface_state.push_back(ss->state());
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			og->surface_state.push_back(ss->state());
 | 
			
		||||
			shader->osl_volume_ref = OSL::ShadingAttribStateRef();
 | 
			
		||||
 | 
			
		||||
		ss->clear_state();
 | 
			
		||||
 | 
			
		||||
		shader->has_surface = true;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		og->surface_state.push_back(OSL::ShadingAttribStateRef());
 | 
			
		||||
		og->surface_state.push_back(OSL::ShadingAttribStateRef());
 | 
			
		||||
		/* generate displacement shader */
 | 
			
		||||
		if(shader->used && graph && output->input("Displacement")->link) {
 | 
			
		||||
			compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
 | 
			
		||||
			shader->has_displacement = true;
 | 
			
		||||
			shader->osl_displacement_ref = ss->state();
 | 
			
		||||
			ss->clear_state();
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			shader->osl_displacement_ref = OSL::ShadingAttribStateRef();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* generate volume shader */
 | 
			
		||||
	if(shader->used && graph && output->input("Volume")->link) {
 | 
			
		||||
		compile_type(shader, shader->graph, SHADER_TYPE_VOLUME);
 | 
			
		||||
		shader->has_volume = true;
 | 
			
		||||
	/* push state to array for lookup */
 | 
			
		||||
	og->surface_state.push_back(shader->osl_surface_ref);
 | 
			
		||||
	og->surface_state.push_back(shader->osl_surface_bump_ref);
 | 
			
		||||
 | 
			
		||||
		og->volume_state.push_back(ss->state());
 | 
			
		||||
		og->volume_state.push_back(ss->state());
 | 
			
		||||
		ss->clear_state();
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		og->volume_state.push_back(OSL::ShadingAttribStateRef());
 | 
			
		||||
		og->volume_state.push_back(OSL::ShadingAttribStateRef());
 | 
			
		||||
	}
 | 
			
		||||
	og->volume_state.push_back(shader->osl_volume_ref);
 | 
			
		||||
	og->volume_state.push_back(shader->osl_volume_ref);
 | 
			
		||||
 | 
			
		||||
	/* generate displacement shader */
 | 
			
		||||
	if(shader->used && graph && output->input("Displacement")->link) {
 | 
			
		||||
		compile_type(shader, shader->graph, SHADER_TYPE_DISPLACEMENT);
 | 
			
		||||
		shader->has_displacement = true;
 | 
			
		||||
 | 
			
		||||
		og->displacement_state.push_back(ss->state());
 | 
			
		||||
		og->displacement_state.push_back(ss->state());
 | 
			
		||||
		ss->clear_state();
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		og->displacement_state.push_back(OSL::ShadingAttribStateRef());
 | 
			
		||||
		og->displacement_state.push_back(OSL::ShadingAttribStateRef());
 | 
			
		||||
	}
 | 
			
		||||
	og->displacement_state.push_back(shader->osl_displacement_ref);
 | 
			
		||||
	og->displacement_state.push_back(shader->osl_displacement_ref);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
void OSLCompiler::add(ShaderNode *node, const char *name)
 | 
			
		||||
void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,14 @@
 | 
			
		||||
#define __OSL_H__
 | 
			
		||||
 | 
			
		||||
#include "util_set.h"
 | 
			
		||||
#include "util_string.h"
 | 
			
		||||
 | 
			
		||||
#include "shader.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
#include <OSL/oslcomp.h>
 | 
			
		||||
#include <OSL/oslexec.h>
 | 
			
		||||
#include <OSL/oslquery.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
CCL_NAMESPACE_BEGIN
 | 
			
		||||
@@ -52,11 +55,24 @@ public:
 | 
			
		||||
	void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress);
 | 
			
		||||
	void device_free(Device *device, DeviceScene *dscene);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	/* osl compile and query */
 | 
			
		||||
	static bool osl_compile(const string& inputfile, const string& outputfile);
 | 
			
		||||
	static bool osl_query(OSL::OSLQuery& query, const string& filepath);
 | 
			
		||||
 | 
			
		||||
	/* shader file loading, all functions return pointer to hash string if found */
 | 
			
		||||
	const char *shader_test_loaded(const string& hash);
 | 
			
		||||
	const char *shader_load_bytecode(const string& hash, const string& bytecode);
 | 
			
		||||
	const char *shader_load_filepath(string filepath);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
	void texture_system_init();
 | 
			
		||||
	void shading_system_init();
 | 
			
		||||
 | 
			
		||||
	OSL::ShadingSystem *ss;
 | 
			
		||||
	OSL::TextureSystem *ts;
 | 
			
		||||
	OSLRenderServices *services;
 | 
			
		||||
	OSL::ErrorHandler errhandler;
 | 
			
		||||
	set<string> loaded_shaders;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
@@ -65,10 +81,10 @@ private:
 | 
			
		||||
 | 
			
		||||
class OSLCompiler {
 | 
			
		||||
public:
 | 
			
		||||
	OSLCompiler(void *shadingsys);
 | 
			
		||||
	OSLCompiler(void *manager, void *shadingsys);
 | 
			
		||||
	void compile(OSLGlobals *og, Shader *shader);
 | 
			
		||||
 | 
			
		||||
	void add(ShaderNode *node, const char *name);
 | 
			
		||||
	void add(ShaderNode *node, const char *name, bool isfilepath = false);
 | 
			
		||||
 | 
			
		||||
	void parameter(const char *name, float f);
 | 
			
		||||
	void parameter_color(const char *name, float3 f);
 | 
			
		||||
@@ -104,6 +120,7 @@ private:
 | 
			
		||||
	void generate_nodes(const set<ShaderNode*>& nodes);
 | 
			
		||||
 | 
			
		||||
	void *shadingsys;
 | 
			
		||||
	void *manager;
 | 
			
		||||
	ShaderType current_type;
 | 
			
		||||
	Shader *current_shader;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,10 @@
 | 
			
		||||
#include "util_string.h"
 | 
			
		||||
#include "util_types.h"
 | 
			
		||||
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
#include <OSL/oslexec.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
CCL_NAMESPACE_BEGIN
 | 
			
		||||
 | 
			
		||||
class Device;
 | 
			
		||||
@@ -78,6 +82,14 @@ public:
 | 
			
		||||
	/* determined before compiling */
 | 
			
		||||
	bool used;
 | 
			
		||||
 | 
			
		||||
#ifdef WITH_OSL
 | 
			
		||||
	/* osl shading state references */
 | 
			
		||||
	OSL::ShadingAttribStateRef osl_surface_ref;
 | 
			
		||||
	OSL::ShadingAttribStateRef osl_surface_bump_ref;
 | 
			
		||||
	OSL::ShadingAttribStateRef osl_volume_ref;
 | 
			
		||||
	OSL::ShadingAttribStateRef osl_displacement_ref;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	Shader();
 | 
			
		||||
	~Shader();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -170,7 +170,7 @@ bool path_read_binary(const string& path, vector<uint8_t>& binary)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool path_read_text(const string& path, string& text)
 | 
			
		||||
bool path_read_text(const string& path, string& text)
 | 
			
		||||
{
 | 
			
		||||
	vector<uint8_t> binary;
 | 
			
		||||
 | 
			
		||||
@@ -184,6 +184,14 @@ static bool path_read_text(const string& path, string& text)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint64_t path_modified_time(const string& path)
 | 
			
		||||
{
 | 
			
		||||
	if(boost::filesystem::exists(path))
 | 
			
		||||
		return (uint64_t)boost::filesystem::last_write_time(path);
 | 
			
		||||
	
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
string path_source_replace_includes(const string& source_, const string& path)
 | 
			
		||||
{
 | 
			
		||||
	/* our own little c preprocessor that replaces #includes with the file
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,9 @@ string path_files_md5_hash(const string& dir);
 | 
			
		||||
void path_create_directories(const string& path);
 | 
			
		||||
bool path_write_binary(const string& path, const vector<uint8_t>& binary);
 | 
			
		||||
bool path_read_binary(const string& path, vector<uint8_t>& binary);
 | 
			
		||||
bool path_read_text(const string& path, string& text);
 | 
			
		||||
 | 
			
		||||
uint64_t path_modified_time(const string& path);
 | 
			
		||||
 | 
			
		||||
string path_source_replace_includes(const string& source, const string& path);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user