[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [51838] trunk/blender/intern/cycles: Cycles OSL: shader script node

Brecht Van Lommel brechtvanlommel at pandora.be
Sat Nov 3 15:32:36 CET 2012


Revision: 51838
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=51838
Author:   blendix
Date:     2012-11-03 14:32:35 +0000 (Sat, 03 Nov 2012)
Log Message:
-----------
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.

Modified Paths:
--------------
    trunk/blender/intern/cycles/blender/CMakeLists.txt
    trunk/blender/intern/cycles/blender/addon/__init__.py
    trunk/blender/intern/cycles/blender/blender_python.cpp
    trunk/blender/intern/cycles/blender/blender_shader.cpp
    trunk/blender/intern/cycles/render/nodes.cpp
    trunk/blender/intern/cycles/render/nodes.h
    trunk/blender/intern/cycles/render/osl.cpp
    trunk/blender/intern/cycles/render/osl.h
    trunk/blender/intern/cycles/render/shader.h
    trunk/blender/intern/cycles/util/util_path.cpp
    trunk/blender/intern/cycles/util/util_path.h

Added Paths:
-----------
    trunk/blender/intern/cycles/blender/addon/osl.py

Modified: trunk/blender/intern/cycles/blender/CMakeLists.txt
===================================================================
--- trunk/blender/intern/cycles/blender/CMakeLists.txt	2012-11-03 14:32:26 UTC (rev 51837)
+++ trunk/blender/intern/cycles/blender/CMakeLists.txt	2012-11-03 14:32:35 UTC (rev 51838)
@@ -38,6 +38,7 @@
 	addon/__init__.py
 	addon/engine.py 
 	addon/enums.py
+	addon/osl.py
 	addon/presets.py
 	addon/properties.py
 	addon/ui.py

Modified: trunk/blender/intern/cycles/blender/addon/__init__.py
===================================================================
--- trunk/blender/intern/cycles/blender/addon/__init__.py	2012-11-03 14:32:26 UTC (rev 51837)
+++ trunk/blender/intern/cycles/blender/addon/__init__.py	2012-11-03 14:32:35 UTC (rev 51838)
@@ -71,7 +71,14 @@
     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()
     ui.register()
@@ -84,3 +91,4 @@
     properties.unregister()
     presets.unregister()
     bpy.utils.unregister_module(__name__)
+

Added: trunk/blender/intern/cycles/blender/addon/osl.py
===================================================================
--- trunk/blender/intern/cycles/blender/addon/osl.py	                        (rev 0)
+++ trunk/blender/intern/cycles/blender/addon/osl.py	2012-11-03 14:32:35 UTC (rev 51838)
@@ -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
+

Modified: trunk/blender/intern/cycles/blender/blender_python.cpp
===================================================================
--- trunk/blender/intern/cycles/blender/blender_python.cpp	2012-11-03 14:32:26 UTC (rev 51837)
+++ trunk/blender/intern/cycles/blender/blender_python.cpp	2012-11-03 14:32:35 UTC (rev 51838)
@@ -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 @@
 	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;
+

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list