USD: Export Cycles Shader networks as USD material networks. #119180
|
@ -189,6 +189,8 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
|||
const bool export_shapekeys = RNA_boolean_get(op->ptr, "export_shapekeys");
|
||||
const bool only_deform_bones = RNA_boolean_get(op->ptr, "only_deform_bones");
|
||||
|
||||
const bool generate_cycles_shaders = RNA_boolean_get(op->ptr, "generate_cycles_shaders");
|
||||
|
||||
char root_prim_path[FILE_MAX];
|
||||
RNA_string_get(op->ptr, "root_prim_path", root_prim_path);
|
||||
process_prim_path(root_prim_path);
|
||||
|
@ -212,6 +214,7 @@ static int wm_usd_export_exec(bContext *C, wmOperator *op)
|
|||
export_textures,
|
||||
overwrite_textures,
|
||||
relative_paths,
|
||||
generate_cycles_shaders,
|
||||
};
|
||||
|
||||
STRNCPY(params.root_prim_path, root_prim_path);
|
||||
|
@ -259,6 +262,7 @@ static void wm_usd_export_draw(bContext * /*C*/, wmOperator *op)
|
|||
box = uiLayoutBox(layout);
|
||||
col = uiLayoutColumnWithHeading(box, true, IFACE_("Materials"));
|
||||
uiItemR(col, ptr, "generate_preview_surface", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
uiItemR(col, ptr, "generate_cycles_shaders", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
const bool export_mtl = RNA_boolean_get(ptr, "export_materials");
|
||||
uiLayoutSetActive(col, export_mtl);
|
||||
|
||||
|
@ -420,6 +424,12 @@ void WM_OT_usd_export(wmOperatorType *ot)
|
|||
"Generate an approximate USD Preview Surface shader "
|
||||
"representation of a Principled BSDF node network");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"generate_cycles_shaders",
|
||||
false,
|
||||
"Export Cycles Shaders",
|
||||
"Export Cycles shader nodes to USD");
|
||||
|
||||
RNA_def_boolean(ot->srna,
|
||||
"export_textures",
|
||||
true,
|
||||
|
|
|
@ -97,6 +97,7 @@ set(SRC
|
|||
intern/usd_writer_armature.cc
|
||||
intern/usd_writer_camera.cc
|
||||
intern/usd_writer_curves.cc
|
||||
intern/usd_writer_cycles.cc
|
||||
intern/usd_writer_hair.cc
|
||||
intern/usd_writer_light.cc
|
||||
intern/usd_writer_material.cc
|
||||
|
@ -137,6 +138,7 @@ set(SRC
|
|||
intern/usd_writer_armature.hh
|
||||
intern/usd_writer_camera.hh
|
||||
intern/usd_writer_curves.hh
|
||||
intern/usd_writer_cycles.hh
|
||||
intern/usd_writer_hair.hh
|
||||
intern/usd_writer_light.hh
|
||||
intern/usd_writer_material.hh
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "usd_asset_utils.hh"
|
||||
#include "usd.hh"
|
||||
|
||||
#include <pxr/usd/ar/asset.h>
|
||||
#include <pxr/usd/ar/packageUtils.h>
|
||||
#include <pxr/usd/ar/resolver.h>
|
||||
#include <pxr/usd/ar/writableAsset.h>
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
|||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
static const char UDIM_PATTERN[] = "<UDIM>";
|
||||
|
@ -330,4 +332,29 @@ bool is_udim_path(const std::string &path)
|
|||
path.find(UDIM_PATTERN2) != std::string::npos;
|
||||
}
|
||||
|
||||
void USD_path_abs(char *path, const char *basepath, bool for_import)
|
||||
{
|
||||
if (!BLI_path_is_rel(path)) {
|
||||
pxr::ArResolvedPath resolved_path = for_import ? pxr::ArGetResolver().Resolve(path) :
|
||||
pxr::ArGetResolver().ResolveForNewAsset(path);
|
||||
|
||||
std::string path_str = resolved_path.GetPathString();
|
||||
|
||||
if (!path_str.empty()) {
|
||||
if (path_str.length() < FILE_MAX) {
|
||||
BLI_strncpy(path, path_str.c_str(), FILE_MAX);
|
||||
return;
|
||||
}
|
||||
WM_reportf(RPT_ERROR,
|
||||
"In %s: resolved path %s exceeds path buffer length.",
|
||||
__func__,
|
||||
path_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/* If we got here, the path couldn't be resolved by the ArResolver, so we
|
||||
* fall back on the standard Blender absolute path resolution. */
|
||||
BLI_path_abs(path, basepath);
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
|
|
@ -61,4 +61,6 @@ std::string import_asset(const char *src,
|
|||
*/
|
||||
bool is_udim_path(const std::string &path);
|
||||
|
||||
void USD_path_abs(char *path, const char *basepath, bool for_import);
|
||||
|
||||
} // namespace blender::io::usd
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,30 @@
|
|||
|
||||
#ifndef BLENDER_USD_WRITER_CYCLES_H
|
||||
#define BLENDER_USD_WRITER_CYCLES_H
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include <pxr/usd/usdShade/material.h>
|
||||
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
void create_usd_cycles_material(pxr::UsdStageRefPtr a_stage,
|
||||
Material *material,
|
||||
pxr::UsdShadeMaterial &usd_material,
|
||||
const USDExportParams &export_params);
|
||||
|
||||
void create_usd_cycles_material(pxr::UsdStageRefPtr a_stage,
|
||||
bNodeTree *ntree,
|
||||
pxr::UsdShadeMaterial &usd_material,
|
||||
const USDExportParams &export_params);
|
||||
|
||||
pxr::UsdShadeShader create_cycles_shader_node(pxr::UsdStageRefPtr a_stage,
|
||||
pxr::SdfPath &shaderPath,
|
||||
bNode *node,
|
||||
const USDExportParams &export_params);
|
||||
|
||||
}; // namespace namespace blender::io::usd
|
||||
|
||||
|
||||
#endif // BLENDER_USD_WRITER_CYCLES_H
|
|
@ -4,19 +4,28 @@
|
|||
|
||||
#include "usd_writer_material.hh"
|
||||
|
||||
#include "usd.hh"
|
||||
#include "usd_asset_utils.hh"
|
||||
#include "usd_exporter_context.hh"
|
||||
#include "usd_hook.hh"
|
||||
#include "usd_writer_cycles.hh"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_colorband.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_format.h"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_node_tree_update.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "IMB_colormanagement.hh"
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_memory_utils.hh"
|
||||
|
@ -25,14 +34,19 @@
|
|||
#include "BLI_string_utils.hh"
|
||||
|
||||
#include "DNA_material_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_packedFile_types.h"
|
||||
|
||||
#include "IMB_imbuf.hh"
|
||||
#include "IMB_imbuf_types.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "WM_types.hh"
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include <pxr/base/tf/stringUtils.h>
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#include "CLG_log.h"
|
||||
static CLG_LogRef LOG = {"io.usd"};
|
||||
|
||||
|
@ -79,12 +93,12 @@ static const pxr::TfToken clamp("clamp", pxr::TfToken::Immortal);
|
|||
static const pxr::TfToken repeat("repeat", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken wrapS("wrapS", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken wrapT("wrapT", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken emissiveColor("emissiveColor", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken in("in", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken translation("translation", pxr::TfToken::Immortal);
|
||||
static const pxr::TfToken rotation("rotation", pxr::TfToken::Immortal);
|
||||
} // namespace usdtokens
|
||||
|
||||
/* Cycles specific tokens. */
|
||||
namespace cyclestokens {
|
||||
static const pxr::TfToken UVMap("UVMap", pxr::TfToken::Immortal);
|
||||
} // namespace cyclestokens
|
||||
|
@ -121,8 +135,15 @@ static void create_uv_input(const USDExporterContext &usd_export_context,
|
|||
static void export_texture(const USDExporterContext &usd_export_context, bNode *node);
|
||||
static bNode *find_bsdf_node(Material *material);
|
||||
static void get_absolute_path(Image *ima, char *r_path);
|
||||
static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_export_context,
|
||||
bNode *node);
|
||||
std::string get_tex_image_asset_filepath(const USDExporterContext &usd_export_context,
|
||||
bNode *node);
|
||||
std::string get_tex_image_asset_filepath(Image *ima);
|
||||
std::string get_tex_image_asset_filepath(bNode *node,
|
||||
const pxr::UsdStageRefPtr stage,
|
||||
const USDExportParams &export_params);
|
||||
std::string get_tex_image_asset_filepath(const std::string &path,
|
||||
const pxr::UsdStageRefPtr stage,
|
||||
const USDExportParams &export_params);
|
||||
static const InputSpecMap &preview_surface_input_map();
|
||||
static bNodeLink *traverse_channel(bNodeSocket *input, short target_type);
|
||||
|
||||
|
@ -454,7 +475,6 @@ static void create_transform2d_shader(const USDExporterContext &usd_export_conte
|
|||
pxr::UsdShadeInput &usd_input,
|
||||
const pxr::TfToken &default_uv,
|
||||
ReportList *reports)
|
||||
|
||||
{
|
||||
bNode *mapping_node = (mapping_link && mapping_link->fromnode ? mapping_link->fromnode :
|
||||
nullptr);
|
||||
|
@ -624,6 +644,7 @@ static void export_in_memory_texture(Image *ima,
|
|||
|
||||
char export_path[FILE_MAX];
|
||||
BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name);
|
||||
BLI_string_replace_char(export_path, '\\', '/');
|
||||
|
||||
if (!allow_overwrite && BLI_exists(export_path)) {
|
||||
return;
|
||||
|
@ -648,8 +669,7 @@ static void get_absolute_path(Image *ima, char *r_path)
|
|||
{
|
||||
/* Make absolute source path. */
|
||||
BLI_strncpy(r_path, ima->filepath, FILE_MAX);
|
||||
BLI_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
|
||||
BLI_path_normalize(r_path);
|
||||
USD_path_abs(r_path, ID_BLEND_PATH_FROM_GLOBAL(&ima->id), false /* Not for import */);
|
||||
}
|
||||
|
||||
static pxr::TfToken get_node_tex_image_color_space(bNode *node)
|
||||
|
@ -716,6 +736,10 @@ static bNodeLink *traverse_channel(bNodeSocket *input, const short target_type)
|
|||
|
||||
/* Recursively traverse the linked node's sockets. */
|
||||
LISTBASE_FOREACH (bNodeSocket *, sock, &linked_node->inputs) {
|
||||
/* Apply heuristics to skip certain inputs. */
|
||||
if (strcmp(sock->name, "Factor") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (bNodeLink *found_link = traverse_channel(sock, target_type)) {
|
||||
return found_link;
|
||||
}
|
||||
|
@ -744,7 +768,7 @@ static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &u
|
|||
const int type)
|
||||
{
|
||||
pxr::SdfPath shader_path = material.GetPath().AppendChild(
|
||||
pxr::TfToken(pxr::TfMakeValidIdentifier(name)));
|
||||
pxr::TfToken("preview_" + pxr::TfMakeValidIdentifier(name)));
|
||||
pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(usd_export_context.stage, shader_path);
|
||||
|
||||
switch (type) {
|
||||
|
@ -816,7 +840,17 @@ static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &u
|
|||
return shader;
|
||||
}
|
||||
|
||||
static std::string get_tex_image_asset_filepath(Image *ima)
|
||||
static bool is_in_memory_texture(Image *ima)
|
||||
{
|
||||
return BKE_image_is_dirty(ima) || ima->source == IMA_SRC_GENERATED;
|
||||
}
|
||||
|
||||
static bool is_packed_texture(Image *ima)
|
||||
{
|
||||
return BKE_image_has_packedfile(ima);
|
||||
}
|
||||
|
||||
std::string get_tex_image_asset_filepath(Image *ima)
|
||||
{
|
||||
char filepath[FILE_MAX];
|
||||
get_absolute_path(ima, filepath);
|
||||
|
@ -824,6 +858,12 @@ static std::string get_tex_image_asset_filepath(Image *ima)
|
|||
return std::string(filepath);
|
||||
}
|
||||
|
||||
std::string get_tex_image_asset_filepath(const USDExporterContext &usd_export_context, bNode *node)
|
||||
{
|
||||
return get_tex_image_asset_filepath(
|
||||
node, usd_export_context.stage, usd_export_context.export_params);
|
||||
}
|
||||
|
||||
/* Gets an asset path for the given texture image node. The resulting path
|
||||
* may be absolute, relative to the USD file, or in a 'textures' directory
|
||||
* in the same directory as the USD file, depending on the export parameters.
|
||||
|
@ -831,8 +871,9 @@ static std::string get_tex_image_asset_filepath(Image *ima)
|
|||
* generated based on the image name for in-memory textures when exporting textures.
|
||||
* This function may return an empty string if the image does not have a filepath
|
||||
* assigned and no asset path could be determined. */
|
||||
static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_export_context,
|
||||
bNode *node)
|
||||
std::string get_tex_image_asset_filepath(bNode *node,
|
||||
const pxr::UsdStageRefPtr stage,
|
||||
const USDExportParams &export_params)
|
||||
{
|
||||
Image *ima = reinterpret_cast<Image *>(node->id);
|
||||
if (!ima) {
|
||||
|
@ -841,22 +882,29 @@ static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_ex
|
|||
|
||||
std::string path;
|
||||
|
||||
if (strlen(ima->filepath) > 0) {
|
||||
if (is_in_memory_texture(ima) || is_packed_texture(ima)) {
|
||||
path = get_in_memory_texture_filename(ima);
|
||||
}
|
||||
else if (strlen(ima->filepath) > 0) {
|
||||
/* Get absolute path. */
|
||||
path = get_tex_image_asset_filepath(ima);
|
||||
}
|
||||
else if (usd_export_context.export_params.export_textures) {
|
||||
/* Image has no filepath, but since we are exporting textures,
|
||||
* check if this is an in-memory texture for which we can
|
||||
* generate a file name. */
|
||||
path = get_in_memory_texture_filename(ima);
|
||||
}
|
||||
|
||||
return get_tex_image_asset_filepath(path, stage, export_params);
|
||||
}
|
||||
|
||||
/* Return a USD asset path referencing the given texture file. The resulting path
|
||||
* may be absolute, relative to the USD file, or in a 'textures' directory
|
||||
* in the same directory as the USD file, depending on the export parameters. */
|
||||
std::string get_tex_image_asset_filepath(const std::string &path,
|
||||
const pxr::UsdStageRefPtr stage,
|
||||
const USDExportParams &export_params)
|
||||
{
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (usd_export_context.export_params.export_textures) {
|
||||
if (export_params.export_textures) {
|
||||
/* The texture is exported to a 'textures' directory next to the
|
||||
* USD root layer. */
|
||||
|
||||
|
@ -864,35 +912,37 @@ static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_ex
|
|||
char file_path[FILE_MAX];
|
||||
BLI_path_split_file_part(path.c_str(), file_path, FILE_MAX);
|
||||
|
||||
if (usd_export_context.export_params.relative_paths) {
|
||||
if (export_params.relative_paths) {
|
||||
BLI_path_join(exp_path, FILE_MAX, ".", "textures", file_path);
|
||||
}
|
||||
else {
|
||||
/* Create absolute path in the textures directory. */
|
||||
std::string export_path = usd_export_context.export_file_path;
|
||||
if (export_path.empty()) {
|
||||
pxr::SdfLayerHandle layer = stage->GetRootLayer();
|
||||
std::string stage_path = layer->GetRealPath();
|
||||
if (stage_path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
char dir_path[FILE_MAX];
|
||||
BLI_path_split_dir_part(export_path.c_str(), dir_path, FILE_MAX);
|
||||
BLI_path_split_dir_part(stage_path.c_str(), dir_path, FILE_MAX);
|
||||
BLI_path_join(exp_path, FILE_MAX, dir_path, "textures", file_path);
|
||||
}
|
||||
BLI_string_replace_char(exp_path, '\\', '/');
|
||||
return exp_path;
|
||||
}
|
||||
|
||||
if (usd_export_context.export_params.relative_paths) {
|
||||
if (export_params.relative_paths) {
|
||||
/* Get the path relative to the USD. */
|
||||
std::string export_path = usd_export_context.export_file_path;
|
||||
if (export_path.empty()) {
|
||||
pxr::SdfLayerHandle layer = stage->GetRootLayer();
|
||||
std::string stage_path = layer->GetRealPath();
|
||||
if (stage_path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
char rel_path[FILE_MAX];
|
||||
STRNCPY(rel_path, path.c_str());
|
||||
|
||||
BLI_path_rel(rel_path, export_path.c_str());
|
||||
BLI_path_rel(rel_path, stage_path.c_str());
|
||||
if (!BLI_path_is_rel(rel_path)) {
|
||||
return path;
|
||||
}
|
||||
|
@ -1062,6 +1112,10 @@ pxr::UsdShadeMaterial create_usd_material(const USDExporterContext &usd_export_c
|
|||
pxr::UsdShadeMaterial usd_material = pxr::UsdShadeMaterial::Define(usd_export_context.stage,
|
||||
usd_path);
|
||||
|
||||
if (material->use_nodes && usd_export_context.export_params.generate_cycles_shaders) {
|
||||
create_usd_cycles_material(
|
||||
usd_export_context.stage, material, usd_material, usd_export_context.export_params);
|
||||
}
|
||||
if (material->use_nodes && usd_export_context.export_params.generate_preview_surface) {
|
||||
create_usd_preview_surface_material(
|
||||
usd_export_context, material, usd_material, active_uv, reports);
|
||||
|
|
|
@ -84,6 +84,7 @@ struct USDExportParams {
|
|||
bool export_textures = true;
|
||||
bool overwrite_textures = true;
|
||||
bool relative_paths = true;
|
||||
bool generate_cycles_shaders = false;
|
||||
char root_prim_path[1024] = ""; /* FILE_MAX */
|
||||
|
||||
/** Communication structure between the wmJob management code and the worker code. Currently used
|
||||
|
|
Loading…
Reference in New Issue