diff --git a/release/scripts/modules/bpy_hydra.py b/release/scripts/modules/bpy_hydra.py new file mode 100644 index 000000000000..fc5700992293 --- /dev/null +++ b/release/scripts/modules/bpy_hydra.py @@ -0,0 +1,114 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +Implementation of class `HydraRenderEngine`. + +Render Blender addon with Hydra render delegate should inherit `HydraRenderEngine`. +Example: +``` +import bpy_hydra + +class CustomHydraRenderEngine(HydraRenderEngine): + bl_idname = 'CustomHydraRenderEngine' + bl_label = "Hydra: Custom" + bl_info = "Hydra Custom render delegate" + + delegate_id = 'HdCustomRendererPlugin' + + @classmethod + def register(cls): + super().register() + + bpy_hydra.register_plugins(["/path/to/plugin")], ["/additional/system/path")]) + + def get_delegate_settings(self, engine_type): + return { + 'setting1': 1, + 'setting2': "2", + } +``` +""" + +__all__ = ( + "HydraRenderEngine", + "export_mtlx", + "register_plugins", + "get_render_plugins", +) + +import bpy +import _bpy_hydra + +from _bpy_hydra import register_plugins, get_render_plugins + + +class HydraRenderEngine(bpy.types.RenderEngine): + """ Render addon with Hydra render delegate should inherit this class """ + + bl_use_shading_nodes_custom = False + + delegate_id = '' + engine_ptr = None + + def __del__(self): + if not self.engine_ptr: + return + + _bpy_hydra.engine_free(self.engine_ptr) + + @classmethod + def register(cls): + _bpy_hydra.init() + + @classmethod + def unregister(cls): + pass + + def get_delegate_settings(self, engine_type): + return {} + + # final render + def update(self, data, depsgraph): + pass + + def render(self, depsgraph): + engine_type = 'PREVIEW' if self.is_preview else 'FINAL' + + self.engine_ptr = _bpy_hydra.engine_create(self.as_pointer(), engine_type, self.delegate_id) + delegate_settings = self.get_delegate_settings(engine_type) + + _bpy_hydra.engine_sync(self.engine_ptr, depsgraph.as_pointer(), bpy.context.as_pointer(), delegate_settings) + _bpy_hydra.engine_render(self.engine_ptr, depsgraph.as_pointer()) + + # viewport render + def view_update(self, context, depsgraph): + if not self.engine_ptr: + self.engine_ptr = _bpy_hydra.engine_create(self.as_pointer(), 'VIEWPORT', self.delegate_id) + + delegate_settings = self.get_delegate_settings('VIEWPORT') + _bpy_hydra.engine_sync(self.engine_ptr, depsgraph.as_pointer(), context.as_pointer(), delegate_settings) + + def view_draw(self, context, depsgraph): + if not self.engine_ptr: + return + + _bpy_hydra.engine_view_draw(self.engine_ptr, depsgraph.as_pointer(), context.as_pointer()) + + +def export_mtlx(material): + """ Exports material to .mtlx file. It is called from Blender source code. """ + try: + import materialx.utils as mx_utils + + doc = mx_utils.export(material, None) + if not doc: + return "" + + mtlx_file = mx_utils.get_temp_file(".mtlx", material.name) + mx_utils.export_to_file(doc, mtlx_file, export_deps=True, copy_deps=False) + return str(mtlx_file) + + except ImportError: + print("ERROR: no MaterialX addon available") + + return "" diff --git a/release/scripts/modules/hydra.py b/release/scripts/modules/hydra.py deleted file mode 100644 index a44c2d68954f..000000000000 --- a/release/scripts/modules/hydra.py +++ /dev/null @@ -1,85 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# Copyright 2011-2022 Blender Foundation - -# - -import traceback - -import bpy -import _hydra - -from _hydra import register_plugins, get_render_plugins - - -class HydraRenderEngine(bpy.types.RenderEngine): - bl_use_preview = True - bl_use_shading_nodes_custom = False - - delegate_id = '' - engine_ptr = None - - def __del__(self): - if not self.engine_ptr: - return - - _hydra.engine_free(self.engine_ptr) - - @classmethod - def register(cls): - _hydra.init() - - @classmethod - def unregister(cls): - pass - - def get_delegate_settings(self, engine_type): - return {} - - # final render - def update(self, data, depsgraph): - pass - - def render(self, depsgraph): - engine_type = 'PREVIEW' if self.is_preview else 'FINAL' - - self.engine_ptr = _hydra.engine_create(self.as_pointer(), engine_type, self.delegate_id) - delegate_settings = self.get_delegate_settings(engine_type) - - _hydra.engine_sync(self.engine_ptr, depsgraph.as_pointer(), bpy.context.as_pointer(), delegate_settings) - _hydra.engine_render(self.engine_ptr, depsgraph.as_pointer()) - - # viewport render - def view_update(self, context, depsgraph): - if not self.engine_ptr: - self.engine_ptr = _hydra.engine_create(self.as_pointer(), 'VIEWPORT', self.delegate_id) - - delegate_settings = self.get_delegate_settings('VIEWPORT') - _hydra.engine_sync(self.engine_ptr, depsgraph.as_pointer(), context.as_pointer(), delegate_settings) - - def view_draw(self, context, depsgraph): - if not self.engine_ptr: - return - - _hydra.engine_view_draw(self.engine_ptr, depsgraph.as_pointer(), context.as_pointer()) - - -def export_mtlx(material_name): - try: - import materialx.utils as mx_utils - - material = bpy.data.materials[material_name] - doc = mx_utils.export(material, None) - if not doc: - return "" - - mtlx_file = mx_utils.get_temp_file(".mtlx", material.name) - mx_utils.export_to_file(doc, mtlx_file, export_deps=True, copy_deps=False) - return str(mtlx_file) - - except ImportError: - print("ERROR: no MaterialX addon available") - - except Exception as e: - print("ERROR:", e, traceback.format_exc()) - - return "" diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 7ac8b93d3b8b..64f6b2f982a5 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -256,8 +256,8 @@ static PyObject *CCL_initPython(void) #endif #ifdef WITH_HYDRA -/* defined in USD Hydra module */ -extern PyObject *Hydra_initPython(void); +/* defined in render_hydra module */ +extern PyObject *BPyInit_hydra(void); #endif static struct _inittab bpy_internal_modules[] = { @@ -290,7 +290,7 @@ static struct _inittab bpy_internal_modules[] = { {"gpu", BPyInit_gpu}, {"idprop", BPyInit_idprop}, #ifdef WITH_HYDRA - {"_hydra", Hydra_initPython}, + {"_bpy_hydra", BPyInit_hydra}, #endif {NULL, NULL}, }; diff --git a/source/blender/render/hydra/CMakeLists.txt b/source/blender/render/hydra/CMakeLists.txt index 91f300f78ac3..4f69734ced6d 100644 --- a/source/blender/render/hydra/CMakeLists.txt +++ b/source/blender/render/hydra/CMakeLists.txt @@ -34,7 +34,9 @@ set(INC ../../gpu ../../gpu/opengl ../../gpu/intern + ../../python/intern .. + ${CMAKE_BINARY_DIR}/source/blender/makesrna/intern ) set(INC_SYS diff --git a/source/blender/render/hydra/python.cc b/source/blender/render/hydra/python.cc index 61d8ededeb3e..c8530d58c44a 100644 --- a/source/blender/render/hydra/python.cc +++ b/source/blender/render/hydra/python.cc @@ -266,7 +266,7 @@ static PyMethodDef methods[] = { static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, - "_hydra", + "_bpy_hydra", "Hydra render API", -1, methods, @@ -282,7 +282,7 @@ static struct PyModuleDef module = { extern "C" { #endif -PyObject *Hydra_initPython(void) +PyObject *BPyInit_hydra(void) { PyObject *mod = PyModule_Create(&blender::render::hydra::module); return mod; diff --git a/source/blender/render/hydra/scene_delegate/material.cc b/source/blender/render/hydra/scene_delegate/material.cc index d32b634708cd..464d42ac4579 100644 --- a/source/blender/render/hydra/scene_delegate/material.cc +++ b/source/blender/render/hydra/scene_delegate/material.cc @@ -12,6 +12,10 @@ #include "BKE_lib_id.h" #include "BKE_material.h" +#include "MEM_guardedalloc.h" +#include "RNA_blender_cpp.h" +#include "bpy_rna.h" + #include "blender_scene_delegate.h" #include "material.h" #include "mtlx_hydra_adapter.h" @@ -74,14 +78,26 @@ void MaterialData::export_mtlx() gstate = PyGILState_Ensure(); PyObject *module, *dict, *func, *result; - module = PyImport_ImportModule("hydra"); + module = PyImport_ImportModule("bpy_hydra"); dict = PyModule_GetDict(module); func = PyDict_GetItemString(dict, "export_mtlx"); - result = PyObject_CallFunction(func, "s", name().c_str()); - std::string path = PyUnicode_AsUTF8(result); + PointerRNA materialptr; + RNA_pointer_create(NULL, &RNA_Material, id, &materialptr); + PyObject *material = pyrna_struct_CreatePyObject(&materialptr); - Py_DECREF(result); + result = PyObject_CallFunction(func, "O", material); + + Py_DECREF(material); + + std::string path; + if (result) { + path = PyUnicode_AsUTF8(result); + Py_DECREF(result); + } + else { + PyErr_Print(); + } Py_DECREF(module); PyGILState_Release(gstate);