Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael A. Kowalski <makowalski@nvidia.com>2020-11-22 06:07:52 +0300
committerMichael A. Kowalski <makowalski@nvidia.com>2020-11-22 06:07:52 +0300
commit1813c23df29a4632392e42853d2106e539744df4 (patch)
tree84f24feb54de4a8a2ec308e48d45fdd9bb91c281
parent33e144284567407c300b833aae8bfac6ac6be1cc (diff)
UsdPreviewSurface shader network import.
Added experimental code to import UsdPreviewSurface shaders as Blender Principled BSDF node networks.
-rw-r--r--source/blender/editors/io/io_usd.c13
-rw-r--r--source/blender/io/usd/import/usd_material_importer.cc424
-rw-r--r--source/blender/io/usd/import/usd_material_importer.h50
-rw-r--r--source/blender/io/usd/usd.h1
4 files changed, 476 insertions, 12 deletions
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index ab40321e318..09452d46ced 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -281,6 +281,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
const float light_intensity_scale = RNA_float_get(op->ptr, "light_intensity_scale");
+ const bool import_usdpreview = RNA_boolean_get(op->ptr, "import_usdpreview");
+
/* Switch out of edit mode to avoid being stuck in it (T54326). */
Object *obedit = CTX_data_edit_object(C);
if (obedit) {
@@ -293,7 +295,8 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
scale,
debug,
use_instancing,
- light_intensity_scale};
+ light_intensity_scale,
+ import_usdpreview};
bool ok = USD_import(C, filename, &params, as_background_job);
@@ -322,6 +325,7 @@ static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op)
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Experimental"), ICON_NONE);
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
+ uiItemR(box, ptr, "import_usdpreview", 0, NULL, ICON_NONE);
}
void WM_OT_usd_import(wmOperatorType *ot)
@@ -383,6 +387,13 @@ void WM_OT_usd_import(wmOperatorType *ot)
"Value by which to scale the intensity of imported lights",
0.0001f,
1000.0f);
+
+ RNA_def_boolean(
+ ot->srna,
+ "import_usdpreview",
+ false,
+ "Import UsdPreviewSurface",
+ "When checked, convert UsdPreviewSurface shaders to Principled BSD shader networks.");
}
#endif /* WITH_USD */
diff --git a/source/blender/io/usd/import/usd_material_importer.cc b/source/blender/io/usd/import/usd_material_importer.cc
index b347cdd488d..82741274eea 100644
--- a/source/blender/io/usd/import/usd_material_importer.cc
+++ b/source/blender/io/usd/import/usd_material_importer.cc
@@ -19,32 +19,131 @@
#include "usd_material_importer.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "BKE_image.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
+#include "BKE_node.h"
#include "BKE_object.h"
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_string.h"
+#include <pxr/base/gf/vec3f.h>
#include <iostream>
+#include <vector>
-namespace blender::io::usd {
+namespace usdtokens {
-USDMaterialImporter::USDMaterialImporter(const USDImporterContext &context, Main *bmain)
- : context_(context)
- , bmain_(bmain)
+// Parameter names
+static const pxr::TfToken a("a", pxr::TfToken::Immortal);
+static const pxr::TfToken b("b", pxr::TfToken::Immortal);
+static const pxr::TfToken clearcoat("clearcoat", pxr::TfToken::Immortal);
+static const pxr::TfToken clearcoatRoughness("clearcoatRoughness", pxr::TfToken::Immortal);
+static const pxr::TfToken diffuseColor("diffuseColor", pxr::TfToken::Immortal);
+static const pxr::TfToken emissiveColor("emissiveColor", pxr::TfToken::Immortal);
+static const pxr::TfToken file("file", pxr::TfToken::Immortal);
+static const pxr::TfToken g("g", pxr::TfToken::Immortal);
+static const pxr::TfToken ior("ior", pxr::TfToken::Immortal);
+static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal);
+static const pxr::TfToken normal("normal", pxr::TfToken::Immortal);
+static const pxr::TfToken occlusion("occlusion", pxr::TfToken::Immortal);
+static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal);
+static const pxr::TfToken r("r", pxr::TfToken::Immortal);
+static const pxr::TfToken result("result", pxr::TfToken::Immortal);
+static const pxr::TfToken rgb("rgb", pxr::TfToken::Immortal);
+static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal);
+static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
+static const pxr::TfToken specularColor("specularColor", pxr::TfToken::Immortal);
+static const pxr::TfToken st("st", pxr::TfToken::Immortal);
+static const pxr::TfToken varname("varname", pxr::TfToken::Immortal);
+
+// USD shader names.
+static const pxr::TfToken UsdPreviewSurface("UsdPreviewSurface", pxr::TfToken::Immortal);
+static const pxr::TfToken UsdPrimvarReader_float2("UsdPrimvarReader_float2",
+ pxr::TfToken::Immortal);
+static const pxr::TfToken UsdUVTexture("UsdUVTexture", pxr::TfToken::Immortal);
+} // namespace usdtokens
+
+static bNode *add_node(const bContext *C, bNodeTree *ntree, int type, float locx, float locy)
+{
+ bNode *new_node = nodeAddStaticNode(C, ntree, type);
+
+ if (new_node) {
+ new_node->locx = locx;
+ new_node->locy = locy;
+ }
+
+ return new_node;
+}
+
+static void link_nodes(
+ bNodeTree *ntree, bNode *source, const char *sock_out, bNode *dest, const char *sock_in)
+{
+ bNodeSocket *source_socket = nodeFindSocket(source, SOCK_OUT, sock_out);
+
+ if (!source_socket) {
+ std::cerr << "PROGRAMMER ERROR: Couldn't find output socket " << sock_out << std::endl;
+ return;
+ }
+
+ bNodeSocket *dest_socket = nodeFindSocket(dest, SOCK_IN, sock_in);
+
+ if (!dest_socket) {
+ std::cerr << "PROGRAMMER ERROR: Couldn't find input socket " << sock_in << std::endl;
+ return;
+ }
+
+ nodeAddLink(ntree, source, source_socket, dest, dest_socket);
+}
+
+static pxr::UsdShadeShader get_source_shader(const pxr::UsdShadeConnectableAPI &source,
+ pxr::TfToken in_shader_id)
{
+ if (source && source.IsShader()) {
+ pxr::UsdShadeShader source_shader(source.GetPrim());
+ if (source_shader) {
+ pxr::TfToken shader_id;
+ if (source_shader.GetShaderId(&shader_id) && shader_id == in_shader_id) {
+ return source_shader;
+ }
+ }
+ }
+ return pxr::UsdShadeShader();
}
-USDMaterialImporter::~USDMaterialImporter()
+namespace blender::io::usd {
+
+namespace {
+
+void compute_node_loc(
+ int column, float node_height, float &r_locx, float &r_locy, NodePlacementContext &r_ctx)
{
+ r_locx = r_ctx.origx - column * r_ctx.horizontal_step;
+
+ if (column >= r_ctx.column_offsets.size()) {
+ r_ctx.column_offsets.push_back(0.0f);
+ }
+
+ r_locy = r_ctx.origy - r_ctx.column_offsets[column];
+
+ r_ctx.column_offsets[column] += node_height + 10.0f;
}
+} // namespace
+
+USDMaterialImporter::USDMaterialImporter(const USDImporterContext &context, Main *bmain)
+ : context_(context), bmain_(bmain)
+{
+}
Material *USDMaterialImporter::add_material(const pxr::UsdShadeMaterial &usd_material) const
{
@@ -54,7 +153,320 @@ Material *USDMaterialImporter::add_material(const pxr::UsdShadeMaterial &usd_mat
std::string mtl_name = usd_material.GetPrim().GetName().GetString().c_str();
- return BKE_material_add(bmain_, mtl_name.c_str());
+ /* Create the material. */
+ Material *mtl = BKE_material_add(bmain_, mtl_name.c_str());
+
+ /* Optionally, create shader nodes to represent a UsdPreviewSurface. */
+ if (context_.import_params.import_usdpreview) {
+ import_usd_preview(mtl, usd_material);
+ }
+
+ return mtl;
+}
+
+/* Convert a UsdPreviewSurface shader network to Blender nodes.
+ * The logic doesn't yet handle converting arbitrary prim var reader nodes. */
+
+void USDMaterialImporter::import_usd_preview(Material *mtl,
+ const pxr::UsdShadeMaterial &usd_material) const
+{
+ if (!usd_material) {
+ return;
+ }
+
+ /* Get the surface shader. */
+ pxr::UsdShadeShader surf_shader = usd_material.ComputeSurfaceSource();
+
+ if (surf_shader) {
+ /* Check if we have a UsdPreviewSurface shader. */
+ pxr::TfToken shader_id;
+ if (surf_shader.GetShaderId(&shader_id) && shader_id == usdtokens::UsdPreviewSurface) {
+ import_usd_preview(mtl, surf_shader);
+ }
+ }
+}
+
+/* Create the Principled BSDF shader node network. */
+void USDMaterialImporter::import_usd_preview(Material *mtl,
+ const pxr::UsdShadeShader &usd_shader) const
+{
+ if (!(bmain_ && mtl && usd_shader)) {
+ return;
+ }
+
+ /* Create the Material's node tree containing the principled
+ * and output shader. */
+
+ bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree");
+ mtl->nodetree = ntree;
+ mtl->use_nodes = true;
+
+ bNode *principled = add_node(NULL, ntree, SH_NODE_BSDF_PRINCIPLED, 0.0f, 300.0f);
+
+ if (!principled) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_BSDF_PRINCIPLED node for USD shader "
+ << usd_shader.GetPath() << std::endl;
+ return;
+ }
+
+ bNode *output = add_node(NULL, ntree, SH_NODE_OUTPUT_MATERIAL, 300.0f, 300.0f);
+
+ if (!output) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_OUTPUT_MATERIAL node for USD shader "
+ << usd_shader.GetPath() << std::endl;
+ return;
+ }
+
+ link_nodes(ntree, principled, "BSDF", output, "Surface");
+
+ /* Set up the principled shader inputs. */
+
+ /* The following keep track of the locations for adding
+ * input nodes. */
+
+ NodePlacementContext context(0.0f, 300.0);
+ int column = 0;
+
+ /* Set the principled shader inputs. */
+
+ if (pxr::UsdShadeInput diffuse_input = usd_shader.GetInput(usdtokens::diffuseColor)) {
+ set_node_input(diffuse_input, principled, "Base Color", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput emissive_input = usd_shader.GetInput(usdtokens::emissiveColor)) {
+ set_node_input(emissive_input, principled, "Emission", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput specular_input = usd_shader.GetInput(usdtokens::specularColor)) {
+ set_node_input(specular_input, principled, "Specular", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput metallic_input = usd_shader.GetInput(usdtokens::metallic)) {
+ ;
+ set_node_input(metallic_input, principled, "Metallic", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput roughness_input = usd_shader.GetInput(usdtokens::roughness)) {
+ set_node_input(roughness_input, principled, "Roughness", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput clearcoat_input = usd_shader.GetInput(usdtokens::clearcoat)) {
+ set_node_input(clearcoat_input, principled, "Clearcoat", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput clearcoat_roughness_input = usd_shader.GetInput(
+ usdtokens::clearcoatRoughness)) {
+ set_node_input(
+ clearcoat_roughness_input, principled, "Clearcoat Roughness", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput opacity_input = usd_shader.GetInput(usdtokens::opacity)) {
+ set_node_input(opacity_input, principled, "Alpha", ntree, column, context);
+ }
+
+ if (pxr::UsdShadeInput ior_input = usd_shader.GetInput(usdtokens::ior)) {
+ set_node_input(ior_input, principled, "IOR", ntree, column, context);
+ }
+
+ // TODO(makowalsk): Verify whether the normal input can be set this way.
+ /*if (pxr::UsdShadeInput normal_input = usd_shader.GetInput(usdtokens::normal)) {
+ set_node_input(normal_input, principled, "Normal", ntree, locx, locy);
+ }*/
+
+ nodeSetActive(ntree, output);
+}
+
+/* Convert the given USD shader input to an input on the given node. */
+void USDMaterialImporter::set_node_input(const pxr::UsdShadeInput &usd_input,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext &r_ctx) const
+{
+ if (!(usd_input && dest_node)) {
+ return;
+ }
+
+ if (usd_input.HasConnectedSource()) {
+ pxr::UsdShadeConnectableAPI source;
+ pxr::TfToken source_name;
+ pxr::UsdShadeAttributeType source_type;
+
+ usd_input.GetConnectedSource(&source, &source_name, &source_type);
+
+ if (!(source && source.IsShader())) {
+ return;
+ }
+
+ pxr::UsdShadeShader source_shader(source.GetPrim());
+
+ if (!source_shader) {
+ return;
+ }
+
+ pxr::TfToken shader_id;
+ if (!source_shader.GetShaderId(&shader_id)) {
+ std::cerr << "ERROR: couldn't get shader id for source shader "
+ << source_shader.GetPrim().GetPath() << std::endl;
+ return;
+ }
+
+ /* For now, only convert UsdUVTexture and UsdPrimvarReader_float2 inputs. */
+ if (shader_id == usdtokens::UsdUVTexture) {
+ std::cerr << "Have UsdUVTexture shader input.\n";
+ convert_usd_uv_texture(
+ source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx);
+ }
+ if (shader_id == usdtokens::UsdPrimvarReader_float2) {
+ std::cerr << "Have UsdPrimvarReader_float2 shader input.\n";
+ convert_usd_primvar_reader(
+ source_shader, source_name, dest_node, dest_socket_name, ntree, column + 1, r_ctx);
+ }
+ }
+ else {
+ bNodeSocket *sock = nodeFindSocket(dest_node, SOCK_IN, dest_socket_name);
+ if (!sock) {
+ std::cerr << "ERROR: couldn't get destination node socket " << dest_socket_name << std::endl;
+ return;
+ }
+
+ pxr::VtValue val;
+ if (!usd_input.Get(&val)) {
+ std::cerr << "ERROR: couldn't get value for usd shader input "
+ << usd_input.GetPrim().GetPath() << std::endl;
+ return;
+ }
+
+ switch (sock->type) {
+ case SOCK_FLOAT:
+ if (val.IsHolding<float>()) {
+ float fval = val.Get<float>();
+ ((bNodeSocketValueFloat *)sock->default_value)->value = fval;
+ }
+ else if (val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f v3f = val.Get<pxr::GfVec3f>();
+ float average = (v3f[0] + v3f[1] + v3f[2]) / 3.0f;
+ ((bNodeSocketValueFloat *)sock->default_value)->value = average;
+ }
+ break;
+ case SOCK_RGBA:
+ if (val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f v3f = val.Get<pxr::GfVec3f>();
+ copy_v3_v3(((bNodeSocketValueRGBA *)sock->default_value)->value, v3f.data());
+ }
+ break;
+ case SOCK_VECTOR:
+ if (val.IsHolding<pxr::GfVec3f>()) {
+ pxr::GfVec3f v3f = val.Get<pxr::GfVec3f>();
+ copy_v3_v3(((bNodeSocketValueVector *)sock->default_value)->value, v3f.data());
+ }
+ else if (val.IsHolding<pxr::GfVec2f>()) {
+ pxr::GfVec2f v2f = val.Get<pxr::GfVec2f>();
+ copy_v2_v2(((bNodeSocketValueVector *)sock->default_value)->value, v2f.data());
+ }
+ break;
+ default:
+ std::cerr << "WARNING: unexpected type " << sock->idname << " for destination node socket "
+ << dest_socket_name << std::endl;
+ break;
+ }
+ }
+}
+
+void USDMaterialImporter::convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext &r_ctx) const
+{
+ if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_) {
+ return;
+ }
+
+ float locx = 0.0f;
+ float locy = 0.0f;
+ compute_node_loc(column, 300.0, locx, locy, r_ctx);
+
+ // Create the Texture Image node.
+ bNode *tex_image = add_node(NULL, ntree, SH_NODE_TEX_IMAGE, locx, locy);
+
+ if (!tex_image) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_TEX_IMAGE for node input " << dest_socket_name
+ << std::endl;
+ return;
+ }
+
+ // Try to load the texture image.
+ pxr::UsdShadeInput file_input = usd_shader.GetInput(usdtokens::file);
+ if (file_input) {
+ pxr::VtValue file_val;
+ if (file_input.Get(&file_val) && file_val.IsHolding<pxr::SdfAssetPath>()) {
+ std::string file_path = file_val.Get<pxr::SdfAssetPath>().GetResolvedPath();
+ std::cout << file_path << std::endl;
+ if (!file_path.empty()) {
+ const char *im_file = file_path.c_str();
+ Image *image = BKE_image_load_exists(bmain_, im_file);
+ tex_image->id = &image->id;
+ }
+ }
+ }
+
+ // Connect to destination node input.
+
+ // Get the source socket name.
+ std::string source_socket_name = usd_source_name == usdtokens::a ? "Alpha" : "Color";
+
+ link_nodes(ntree, tex_image, source_socket_name.c_str(), dest_node, dest_socket_name);
+
+ // Connect the texture image node "Vector" input.
+ if (pxr::UsdShadeInput st_input = usd_shader.GetInput(usdtokens::st)) {
+ set_node_input(st_input, tex_image, "Vector", ntree, column, r_ctx);
+ }
+}
+
+void USDMaterialImporter::convert_usd_primvar_reader(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext &r_ctx) const
+{
+ if (!usd_shader || !dest_node || !ntree || !dest_socket_name || !bmain_) {
+ return;
+ }
+
+ float locx = 0.0f;
+ float locy = 0.0f;
+ compute_node_loc(column, 300.0f, locx, locy, r_ctx);
+
+ // Create the UV Map node.
+ bNode *uv_map = add_node(NULL, ntree, SH_NODE_UVMAP, locx, locy);
+
+ if (!uv_map) {
+ std::cerr << "ERROR: Couldn't create SH_NODE_UVMAP for node input " << dest_socket_name
+ << std::endl;
+ return;
+ }
+
+ // Set the texmap name.
+ pxr::UsdShadeInput varname_input = usd_shader.GetInput(usdtokens::varname);
+ if (varname_input) {
+ pxr::VtValue varname_val;
+ if (varname_input.Get(&varname_val) && varname_val.IsHolding<pxr::TfToken>()) {
+ std::string varname = varname_val.Get<pxr::TfToken>().GetString();
+ if (!varname.empty()) {
+ NodeShaderUVMap *storage = (NodeShaderUVMap *)uv_map->storage;
+ BLI_strncpy(storage->uv_map, varname.c_str(), sizeof(storage->uv_map));
+ }
+ }
+ }
+
+ // Connect to destination node input.
+ link_nodes(ntree, uv_map, "UV", dest_node, dest_socket_name);
}
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/import/usd_material_importer.h b/source/blender/io/usd/import/usd_material_importer.h
index bdb5c9fca76..e6fd9ba4d47 100644
--- a/source/blender/io/usd/import/usd_material_importer.h
+++ b/source/blender/io/usd/import/usd_material_importer.h
@@ -24,15 +24,30 @@
struct Main;
struct Material;
+struct bNode;
+struct bNodeTree;
namespace blender::io::usd {
-/* Implements logic to create Blender materials
- * from USD materials. */
+struct NodePlacementContext {
+ float origx;
+ float origy;
+ std::vector<float> column_offsets;
+ const float horizontal_step;
+
+ NodePlacementContext(float in_origx, float in_origy, float in_horizontal_step = 300.0f)
+ : origx(in_origx),
+ origy(in_origy),
+ column_offsets(64, 0.0f),
+ horizontal_step(in_horizontal_step)
+ {
+ }
+};
+
+/* Converts USD materials to Blender representation. */
class USDMaterialImporter {
protected:
-
USDImporterContext context_;
Main *bmain_;
@@ -40,9 +55,34 @@ class USDMaterialImporter {
public:
USDMaterialImporter(const USDImporterContext &context, Main *bmain);
- ~USDMaterialImporter();
-
Material *add_material(const pxr::UsdShadeMaterial &usd_material) const;
+
+ void import_usd_preview(Material *mtl, const pxr::UsdShadeMaterial &usd_material) const;
+
+ void import_usd_preview(Material *mtl, const pxr::UsdShadeShader &usd_material) const;
+
+ void set_node_input(const pxr::UsdShadeInput &usd_input,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext &r_ctx) const;
+
+ void convert_usd_uv_texture(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext &r_ctx) const;
+
+ void convert_usd_primvar_reader(const pxr::UsdShadeShader &usd_shader,
+ const pxr::TfToken &usd_source_name,
+ bNode *dest_node,
+ const char *dest_socket_name,
+ bNodeTree *ntree,
+ int column,
+ NodePlacementContext &r_ctx) const;
};
} // namespace blender::io::usd
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index 33b44b6fe67..d42cc9152b4 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -62,6 +62,7 @@ struct USDImportParams {
bool debug;
bool use_instancing;
float light_intensity_scale;
+ bool import_usdpreview;
};
/* The USD_import function takes a as_background_job parameter, and returns a boolean.