1
1
This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/io/usd/intern/usd_umm.cc
Michael Kowalski 362d5b42c8 USD: error importing texture from empty path.
Added check for empty path before attempting to import
textures.
2022-12-07 19:59:52 -05:00

899 lines
24 KiB
C++

/*
* 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.
*
*/
#ifdef WITH_PYTHON
# include "usd_umm.h"
# include "usd.h"
# include "usd_asset_utils.h"
# include "usd_exporter_context.h"
# include "usd_writer_material.h"
# include "DNA_material_types.h"
# include <pxr/usd/ar/resolver.h>
# include <iostream>
# include <vector>
# include "WM_api.h"
// The following is additional example code for invoking Python and
// a Blender Python operator from C++:
//#include "BPY_extern_python.h"
//#include "BPY_extern_run.h"
// const char *foo[] = { "bpy", 0 };
// BPY_run_string_eval(C, nullptr, "print('hi!!')");
// BPY_run_string_eval(C, foo, "bpy.ops.universalmaterialmap.instance_to_data_converter()");
// BPY_run_string_eval(C, nullptr, "print('test')");
namespace usdtokens {
// Render context names.
static const pxr::TfToken mdl("mdl", pxr::TfToken::Immortal);
} // end namespace usdtokens
static PyObject *g_umm_module = nullptr;
static const char *k_umm_module_name = "omni.universalmaterialmap.blender.material";
static const char *k_omni_pbr_mdl_name = "OmniPBR.mdl";
static const char *k_omni_pbr_name = "OmniPBR";
static const char *k_udim_tag = "<UDIM>";
using namespace blender::io::usd;
static std::string anchor_relative_path(pxr::UsdStagePtr stage, const std::string &asset_path)
{
if (asset_path.empty() || asset_path.front() != '.') {
return std::string();
}
// TODO(makowalski): avoid recomputing the USD path, if possible.
pxr::SdfLayerHandle layer = stage->GetRootLayer();
std::string stage_path = layer->GetRealPath();
if (stage_path.empty()) {
return asset_path;
}
#if PXR_VERSION >= 2111
return pxr::ArGetResolver().CreateIdentifier(asset_path, pxr::ArResolvedPath(stage_path));
#else
return pxr::ArGetResolver().AnchorRelativePath(stage_path, asset_path);
#endif
}
static void print_obj(PyObject *obj)
{
if (!obj) {
return;
}
PyObject *str = PyObject_Str(obj);
if (str && PyUnicode_Check(str)) {
std::cout << PyUnicode_AsUTF8(str) << std::endl;
Py_DECREF(str);
}
}
namespace {
enum eUMMNotification {
UMM_NOTIFICATION_NONE = 0,
UMM_NOTIFICATION_SUCCESS,
UMM_NOTIFICATION_FAILURE
};
} // anonymous namespace
static eUMMNotification report_notification(PyObject *dict)
{
if (!dict) {
return UMM_NOTIFICATION_NONE;
}
if (!PyDict_Check(dict)) {
return UMM_NOTIFICATION_NONE;
}
PyObject *notification_item = PyDict_GetItemString(dict, "umm_notification");
if (!notification_item) {
return UMM_NOTIFICATION_NONE;
}
PyObject *message_item = PyDict_GetItemString(dict, "message");
if (!message_item) {
return UMM_NOTIFICATION_NONE;
}
if (!PyUnicode_Check(notification_item)) {
std::cerr << "WARNING: 'umm_notification' value is not a string" << std::endl;
return UMM_NOTIFICATION_NONE;
}
const char *notification_str = PyUnicode_AsUTF8(notification_item);
if (!notification_str) {
std::cerr << "WARNING: couldn't get 'umm_notification' string value" << std::endl;
return UMM_NOTIFICATION_NONE;
}
if (strcmp(notification_str, "success") == 0) {
/* We don't report success, do nothing. */
return UMM_NOTIFICATION_SUCCESS;
}
if (!PyUnicode_Check(message_item)) {
std::cerr << "WARNING: 'message' value is not a string" << std::endl;
return UMM_NOTIFICATION_NONE;
}
const char *message_str = PyUnicode_AsUTF8(message_item);
if (!message_str) {
std::cerr << "WARNING: couldn't get 'message' string value" << std::endl;
return UMM_NOTIFICATION_NONE;
}
if (strlen(message_str) == 0) {
std::cerr << "WARNING: empty 'message' string value" << std::endl;
return UMM_NOTIFICATION_NONE;
}
if (strcmp(notification_str, "incomplete_process") == 0) {
WM_reportf(RPT_WARNING, message_str);
return UMM_NOTIFICATION_FAILURE;
}
if (strcmp(notification_str, "unexpected_error") == 0) {
WM_reportf(RPT_ERROR, message_str);
return UMM_NOTIFICATION_FAILURE;
}
std::cout << "WARNING: unknown notification type: " << notification_str << std::endl;
return UMM_NOTIFICATION_NONE;
}
static bool is_none_value(PyObject *tup)
{
if (!(tup && PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
return false;
}
PyObject *second = PyTuple_GetItem(tup, 1);
return second == Py_None;
}
/* Sets the source asset and source asset subidenifier properties on the given shader
* with values parsed from the given target_class string. */
static bool set_source_asset(pxr::UsdShadeShader &usd_shader, const std::string &target_class)
{
if (!usd_shader || target_class.empty()) {
return false;
}
// Split the target_class string on the '|' separator.
size_t sep = target_class.find_last_of("|");
if (sep == 0 || sep == std::string::npos) {
std::cout << "Couldn't parse target_class string " << target_class << std::endl;
return false;
}
std::string source_asset = target_class.substr(0, sep);
usd_shader.SetSourceAsset(pxr::SdfAssetPath(source_asset), usdtokens::mdl);
std::string source_asset_subidentifier = target_class.substr(sep + 1);
if (!source_asset_subidentifier.empty()) {
usd_shader.SetSourceAssetSubIdentifier(pxr::TfToken(source_asset_subidentifier),
usdtokens::mdl);
}
return true;
}
static bool get_data_name(PyObject *tup, std::string &r_name)
{
if (!(tup && PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
return false;
}
PyObject *first = PyTuple_GetItem(tup, 0);
if (first && PyUnicode_Check(first)) {
const char *name = PyUnicode_AsUTF8(first);
if (name) {
r_name = name;
return true;
}
}
return false;
}
static bool get_string_data(PyObject *tup, std::string &r_data)
{
if (!(tup && PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
return false;
}
PyObject *second = PyTuple_GetItem(tup, 1);
if (second && PyUnicode_Check(second)) {
const char *data = PyUnicode_AsUTF8(second);
if (data) {
r_data = data;
return true;
}
}
return false;
}
static bool get_float_data(PyObject *tup, float &r_data)
{
if (!(tup && PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
return false;
}
PyObject *second = PyTuple_GetItem(tup, 1);
if (second && PyFloat_Check(second)) {
r_data = static_cast<float>(PyFloat_AsDouble(second));
return true;
}
return false;
}
static bool get_float3_data(PyObject *tup, float r_data[3])
{
if (!(tup && PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
return false;
}
PyObject *second = PyTuple_GetItem(tup, 1);
if (second && PyTuple_Check(second) && PyTuple_Size(second) > 2) {
for (int i = 0; i < 3; ++i) {
PyObject *comp = PyTuple_GetItem(second, i);
if (comp && PyFloat_Check(comp)) {
r_data[i] = static_cast<float>(PyFloat_AsDouble(comp));
}
else {
return false;
}
}
return true;
}
return false;
}
static bool get_rgba_data(PyObject *tup, float r_data[4])
{
if (!(tup && PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
return false;
}
PyObject *second = PyTuple_GetItem(tup, 1);
if (!(second && PyTuple_Check(second))) {
return false;
}
Py_ssize_t size = PyTuple_Size(second);
if (size > 2) {
for (int i = 0; i < 3; ++i) {
PyObject *comp = PyTuple_GetItem(second, i);
if (comp && PyFloat_Check(comp)) {
r_data[i] = static_cast<float>(PyFloat_AsDouble(comp));
}
else {
return false;
}
}
if (size > 3) {
PyObject *alpha = PyTuple_GetItem(second, 3);
if (alpha && PyFloat_Check(alpha)) {
r_data[3] = static_cast<float>(PyFloat_AsDouble(alpha));
}
else {
return false;
}
}
else {
r_data[3] = 1.0;
}
return true;
}
return false;
}
/* Be sure to call PyGILState_Ensure() before calling this function. */
static bool ensure_module_loaded(bool warn = true)
{
if (!g_umm_module) {
g_umm_module = PyImport_ImportModule(k_umm_module_name);
if (!g_umm_module) {
if (warn) {
std::cout << "WARNING: couldn't load Python module " << k_umm_module_name << std::endl;
if (PyErr_Occurred()) {
PyErr_Print();
}
}
PyErr_Clear();
}
}
return g_umm_module != nullptr;
}
static void test_python()
{
PyGILState_STATE gilstate = PyGILState_Ensure();
PyObject *mod = PyImport_ImportModule("omni.universalmaterialmap.core.converter.util");
if (mod) {
const char *func_name = "get_conversion_manifest";
if (PyObject_HasAttrString(mod, func_name)) {
if (PyObject *func = PyObject_GetAttrString(mod, func_name)) {
PyObject *ret = PyObject_CallObject(func, nullptr);
Py_DECREF(func);
if (ret) {
print_obj(ret);
Py_DECREF(ret);
}
}
}
}
PyGILState_Release(gilstate);
}
static PyObject *get_shader_source_data(const USDImportParams &params,
const pxr::UsdShadeShader &usd_shader)
{
if (!usd_shader) {
return nullptr;
}
std::vector<PyObject *> tuple_items;
std::vector<pxr::UsdShadeInput> inputs = usd_shader.GetInputs();
for (auto input : inputs) {
if (!input) {
continue;
}
PyObject *tup = nullptr;
std::string name = input.GetBaseName().GetString();
if (name.empty()) {
continue;
}
pxr::UsdAttribute usd_attr;
bool have_connected_source = false;
if (input.HasConnectedSource()) {
pxr::UsdShadeConnectableAPI source;
pxr::TfToken source_name;
pxr::UsdShadeAttributeType source_type;
if (input.GetConnectedSource(&source, &source_name, &source_type)) {
usd_attr = source.GetInput(source_name).GetAttr();
have_connected_source = true;
}
else {
std::cerr << "ERROR: couldn't get connected source for usd shader input "
<< input.GetPrim().GetPath() << " " << input.GetFullName() << std::endl;
}
}
else {
usd_attr = input.GetAttr();
}
if (!usd_attr) {
std::cerr << "ERROR: couldn't get attribute for usd shader input " << input.GetPrim().GetPath()
<< " " << input.GetFullName() << std::endl;
continue;
}
pxr::VtValue val;
if (!usd_attr.Get(&val)) {
std::cerr << "ERROR: couldn't get value for usd shader input " << input.GetPrim().GetPath()
<< " " << input.GetFullName() << std::endl;
continue;
}
if (val.IsHolding<float>()) {
double dval = val.UncheckedGet<float>();
tup = Py_BuildValue("sd", name.c_str(), dval);
}
else if (val.IsHolding<int>()) {
int ival = val.UncheckedGet<int>();
tup = Py_BuildValue("si", name.c_str(), ival);
}
else if (val.IsHolding<bool>()) {
int ival = val.UncheckedGet<bool>();
tup = Py_BuildValue("si", name.c_str(), ival);
}
else if (val.IsHolding<pxr::SdfAssetPath>()) {
pxr::SdfAssetPath asset_path = val.Get<pxr::SdfAssetPath>();
std::string resolved_path = asset_path.GetResolvedPath();
if (resolved_path.empty()) {
/* If the path wasn't resolved, it could be because it's a UDIM path,
* so try to use the asset path directly, anchoring it if it's a relative path. */
resolved_path = anchor_relative_path(usd_shader.GetPrim().GetStage(),
asset_path.GetAssetPath());
}
if (params.import_textures && !resolved_path.empty()) {
resolved_path = usd_import_texture(
resolved_path.c_str(), params.import_textures_dir, params.overwrite_textures);
}
pxr::TfToken color_space_tok = usd_attr.GetColorSpace();
if (color_space_tok.IsEmpty() && have_connected_source) {
/* The connected asset input has no color space specified,
* so we also check the shader's asset input for this data. */
if (pxr::UsdAttribute input_attr = input.GetAttr()) {
color_space_tok = input_attr.GetColorSpace();
}
}
std::string color_space_str = !color_space_tok.IsEmpty() ? color_space_tok.GetString() :
"sRGB";
PyObject *tex_file_tup = Py_BuildValue("ss", resolved_path.c_str(), color_space_str.c_str());
tup = Py_BuildValue("sN", name.c_str(), tex_file_tup);
}
else if (val.IsHolding<pxr::GfVec3f>()) {
pxr::GfVec3f v3f = val.UncheckedGet<pxr::GfVec3f>();
pxr::GfVec3d v3d(v3f);
PyObject *v3_tup = Py_BuildValue("ddd", v3d[0], v3d[1], v3d[2]);
if (v3_tup) {
tup = Py_BuildValue("sN", name.c_str(), v3_tup);
}
else {
std::cout << "Couldn't build v3f tuple for " << usd_shader.GetPath() << " input "
<< input.GetFullName() << std::endl;
}
}
else if (val.IsHolding<pxr::GfVec2f>()) {
pxr::GfVec2f v2f = val.UncheckedGet<pxr::GfVec2f>();
/* std::cout << "Have v2f input " << v2f << " for "
<< usd_shader.GetPath() << " " << input.GetFullName() << std::endl;*/
pxr::GfVec2d v2d(v2f);
PyObject *v2_tup = Py_BuildValue("dd", v2d[0], v2d[1]);
if (v2_tup) {
tup = Py_BuildValue("sN", name.c_str(), v2_tup);
}
else {
std::cout << "Couldn't build v2f tuple for " << usd_shader.GetPath() << " input "
<< input.GetFullName() << std::endl;
}
}
if (tup) {
tuple_items.push_back(tup);
}
}
PyObject *ret = PyTuple_New(tuple_items.size());
if (!ret) {
return nullptr;
}
for (int i = 0; i < tuple_items.size(); ++i) {
if (PyTuple_SetItem(ret, i, tuple_items[i])) {
std::cout << "error setting tuple item" << std::endl;
}
}
return ret;
}
static bool import_material(const USDImportParams &params,
Material *mtl,
const pxr::UsdShadeShader &usd_shader,
const std::string &source_class)
{
if (!(usd_shader && mtl)) {
return false;
}
PyGILState_STATE gilstate = PyGILState_Ensure();
if (!ensure_module_loaded()) {
PyGILState_Release(gilstate);
return false;
}
const char *func_name = "apply_data_to_instance";
if (!PyObject_HasAttrString(g_umm_module, func_name)) {
std::cerr << "WARNING: UMM module has no attribute " << func_name << std::endl;
PyGILState_Release(gilstate);
return false;
}
PyObject *func = PyObject_GetAttrString(g_umm_module, func_name);
if (!func) {
std::cerr << "WARNING: Couldn't get UMM module attribute " << func_name << std::endl;
PyGILState_Release(gilstate);
return false;
}
PyObject *source_data = get_shader_source_data(params, usd_shader);
if (!source_data) {
std::cout << "WARNING: Couldn't get source data for shader " << usd_shader.GetPath()
<< std::endl;
PyGILState_Release(gilstate);
return false;
}
// std::cout << "source_data:\n";
// print_obj(source_data);
// Create the kwargs dictionary.
PyObject *kwargs = PyDict_New();
if (!kwargs) {
std::cout << "WARNING: Couldn't create kwargs dicsionary." << std::endl;
Py_DECREF(source_data);
PyGILState_Release(gilstate);
return false;
}
PyObject *instance_name = PyUnicode_FromString(mtl->id.name + 2);
PyDict_SetItemString(kwargs, "instance_name", instance_name);
Py_DECREF(instance_name);
PyObject *source_class_obj = PyUnicode_FromString(source_class.c_str());
PyDict_SetItemString(kwargs, "source_class", source_class_obj);
Py_DECREF(source_class_obj);
PyObject *render_context = PyUnicode_FromString("Blender");
PyDict_SetItemString(kwargs, "render_context", render_context);
Py_DECREF(render_context);
PyDict_SetItemString(kwargs, "source_data", source_data);
Py_DECREF(source_data);
std::cout << func_name << " arguments:\n";
print_obj(kwargs);
PyObject *empty_args = PyTuple_New(0);
PyObject *ret = PyObject_Call(func, empty_args, kwargs);
Py_DECREF(empty_args);
Py_DECREF(func);
bool success = ret != nullptr;
if (ret) {
std::cout << "result:\n";
print_obj(ret);
if (report_notification(ret) == UMM_NOTIFICATION_FAILURE) {
/* The function returned a notification object
* indicating a failure. */
success = false;
}
Py_DECREF(ret);
}
Py_DECREF(kwargs);
PyGILState_Release(gilstate);
return success;
}
static void set_shader_properties(const USDExporterContext &usd_export_context,
pxr::UsdShadeShader &usd_shader,
PyObject *data_list)
{
if (!(data_list && usd_shader)) {
return;
}
if (!PyList_Check(data_list)) {
return;
}
Py_ssize_t len = PyList_Size(data_list);
for (Py_ssize_t i = 0; i < len; ++i) {
PyObject *tup = PyList_GetItem(data_list, i);
if (!tup) {
continue;
}
std::string name;
if (!get_data_name(tup, name) || name.empty()) {
std::cout << "Couldn't get data name\n";
continue;
}
if (is_none_value(tup)) {
/* Receiving None values is not an error. */
continue;
}
if (name == "umm_target_class") {
std::string target_class;
if (!get_string_data(tup, target_class) || target_class.empty()) {
std::cout << "Couldn't get target class\n";
continue;
}
set_source_asset(usd_shader, target_class);
}
else {
if (!(PyTuple_Check(tup) && PyTuple_Size(tup) > 1)) {
std::cout << "Unexpected data item type or size:\n";
print_obj(tup);
continue;
}
PyObject *second = PyTuple_GetItem(tup, 1);
if (!second) {
std::cout << "Couldn't get second tuple value:\n";
print_obj(tup);
continue;
}
if (PyFloat_Check(second)) {
float fval = static_cast<float>(PyFloat_AsDouble(second));
usd_shader.CreateInput(pxr::TfToken(name), pxr::SdfValueTypeNames->Float).Set(fval);
}
else if (PyBool_Check(second)) {
bool bval = static_cast<bool>(PyLong_AsLong(second));
usd_shader.CreateInput(pxr::TfToken(name), pxr::SdfValueTypeNames->Bool).Set(bval);
}
else if (PyLong_Check(second)) {
int ival = static_cast<int>(PyLong_AsLong(second));
usd_shader.CreateInput(pxr::TfToken(name), pxr::SdfValueTypeNames->Int).Set(ival);
}
else if (PyList_Check(second) && PyList_Size(second) == 2) {
PyObject *item0 = PyList_GetItem(second, 0);
PyObject *item1 = PyList_GetItem(second, 1);
if (PyUnicode_Check(item0) && PyUnicode_Check(item1)) {
const char *asset = PyUnicode_AsUTF8(item0);
std::string asset_path = get_tex_image_asset_path(
asset, usd_export_context.stage, usd_export_context.export_params);
const char *color_space = PyUnicode_AsUTF8(item1);
pxr::UsdShadeInput asset_input = usd_shader.CreateInput(pxr::TfToken(name),
pxr::SdfValueTypeNames->Asset);
asset_input.Set(pxr::SdfAssetPath(asset_path));
asset_input.GetAttr().SetColorSpace(pxr::TfToken(color_space));
}
else if (PyFloat_Check(item0) && PyFloat_Check(item1)) {
float f0 = static_cast<float>(PyFloat_AsDouble(item0));
float f1 = static_cast<float>(PyFloat_AsDouble(item1));
usd_shader.CreateInput(pxr::TfToken(name), pxr::SdfValueTypeNames->Float2)
.Set(pxr::GfVec2f(f0, f1));
}
}
else if (PyTuple_Check(second) && PyTuple_Size(second) == 3) {
pxr::GfVec3f f3val;
for (int i = 0; i < 3; ++i) {
PyObject *comp = PyTuple_GetItem(second, i);
if (comp && PyFloat_Check(comp)) {
f3val[i] = static_cast<float>(PyFloat_AsDouble(comp));
}
else {
std::cout << "Couldn't parse color3f " << name << std::endl;
}
}
usd_shader.CreateInput(pxr::TfToken(name), pxr::SdfValueTypeNames->Color3f).Set(f3val);
}
else {
std::cout << "Can't handle value:\n";
print_obj(second);
}
}
}
}
namespace blender::io::usd {
bool umm_module_loaded()
{
PyGILState_STATE gilstate = PyGILState_Ensure();
bool loaded = ensure_module_loaded(false /* warn */);
PyGILState_Release(gilstate);
return loaded;
}
bool umm_import_mdl_material(const USDImportParams &params,
Material *mtl,
const pxr::UsdShadeMaterial &usd_material,
bool verbose,
bool *r_has_mdl)
{
if (!(mtl && usd_material)) {
return false;
}
/* Get the surface shader. */
pxr::UsdShadeShader surf_shader = usd_material.ComputeSurfaceSource(usdtokens::mdl);
if (surf_shader) {
/* Check if we have an mdl source asset. */
pxr::SdfAssetPath source_asset;
if (!surf_shader.GetSourceAsset(&source_asset, usdtokens::mdl)) {
if (verbose) {
std::cout << "No mdl source asset for shader " << surf_shader.GetPath() << std::endl;
}
if (r_has_mdl) {
*r_has_mdl = false;
}
return false;
}
pxr::TfToken source_asset_sub_identifier;
if (!surf_shader.GetSourceAssetSubIdentifier(&source_asset_sub_identifier, usdtokens::mdl)) {
if (verbose) {
std::cout << "No mdl source asset sub identifier for shader " << surf_shader.GetPath()
<< std::endl;
}
if (r_has_mdl) {
*r_has_mdl = false;
}
return false;
}
if (r_has_mdl) {
*r_has_mdl = true;
}
std::string path = source_asset.GetAssetPath();
// Get the filename component of the path.
size_t last_slash = path.find_last_of("/\\");
if (last_slash != std::string::npos) {
path = path.substr(last_slash + 1);
}
std::string source_class = path + "|" + source_asset_sub_identifier.GetString();
return import_material(params, mtl, surf_shader, source_class);
}
return false;
}
bool umm_export_material(const USDExporterContext &usd_export_context,
const Material *mtl,
pxr::UsdShadeShader &usd_shader,
const std::string &render_context)
{
if (!(usd_shader && mtl)) {
return false;
}
PyGILState_STATE gilstate = PyGILState_Ensure();
if (!ensure_module_loaded()) {
PyGILState_Release(gilstate);
return false;
}
const char *func_name = "convert_instance_to_data";
if (!PyObject_HasAttrString(g_umm_module, func_name)) {
std::cerr << "WARNING: UMM module has no attribute " << func_name << std::endl;
PyGILState_Release(gilstate);
return false;
}
PyObject *func = PyObject_GetAttrString(g_umm_module, func_name);
if (!func) {
std::cerr << "WARNING: Couldn't get UMM module attribute " << func_name << std::endl;
PyGILState_Release(gilstate);
return false;
}
// Create the kwargs dictionary.
PyObject *kwargs = PyDict_New();
if (!kwargs) {
std::cout << "WARNING: Couldn't create kwargs dicsionary." << std::endl;
PyGILState_Release(gilstate);
return false;
}
PyObject *instance_name = PyUnicode_FromString(mtl->id.name + 2);
PyDict_SetItemString(kwargs, "instance_name", instance_name);
Py_DECREF(instance_name);
PyObject *render_context_arg = PyUnicode_FromString(render_context.c_str());
PyDict_SetItemString(kwargs, "render_context", render_context_arg);
Py_DECREF(render_context_arg);
std::cout << func_name << " arguments:\n";
print_obj(kwargs);
PyObject *empty_args = PyTuple_New(0);
PyObject *ret = PyObject_Call(func, empty_args, kwargs);
Py_DECREF(empty_args);
Py_DECREF(func);
bool success = ret != nullptr;
if (ret) {
std::cout << "result:\n";
print_obj(ret);
if (report_notification(ret) == UMM_NOTIFICATION_FAILURE) {
/* The function returned a notification object
* indicating a failure. */
success = false;
}
else {
set_shader_properties(usd_export_context, usd_shader, ret);
}
Py_DECREF(ret);
}
Py_DECREF(kwargs);
PyGILState_Release(gilstate);
return success;
}
} // Namespace blender::io::usd
#endif // ifdef WITH_PYTHON