Files
test/source/blender/io/usd/hydra/material.cc

175 lines
5.8 KiB
C++

/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "material.hh"
#include "usd_private.hh"
#include <Python.h>
#include <unicodeobject.h>
#include <pxr/base/tf/stringUtils.h>
#include <pxr/imaging/hd/material.h>
#include <pxr/imaging/hd/renderDelegate.h>
#include <pxr/imaging/hd/tokens.h>
#include <pxr/usdImaging/usdImaging/materialParamUtils.h>
#ifdef WITH_MATERIALX
# include <pxr/usd/usdMtlx/reader.h>
# include <pxr/usd/usdMtlx/utils.h>
#endif
#include "DEG_depsgraph_query.hh"
#include "hydra_scene_delegate.hh"
#include "image.hh"
#include "intern/usd_exporter_context.hh"
#include "intern/usd_writer_material.hh"
#ifdef WITH_MATERIALX
# include "shader/materialx/material.h"
#endif
using namespace blender::io::usd;
namespace blender::io::hydra {
MaterialData::MaterialData(HydraSceneDelegate *scene_delegate,
const Material *material,
pxr::SdfPath const &prim_id)
: IdData(scene_delegate, &material->id, prim_id)
{
}
void MaterialData::init()
{
ID_LOGN(1, "");
double_sided = (((Material *)id)->blend_flag & MA_BL_CULL_BACKFACE) == 0;
material_network_map_ = pxr::VtValue();
/* Create temporary in memory stage. */
pxr::UsdStageRefPtr stage = pxr::UsdStage::CreateInMemory();
pxr::UsdTimeCode time = pxr::UsdTimeCode::Default();
auto get_time_code = [time]() { return time; };
pxr::SdfPath material_library_path("/_materials");
pxr::SdfPath material_path = material_library_path.AppendChild(
pxr::TfToken(prim_id.GetElementString()));
/* Create USD export content to reuse USD file export code. */
USDExportParams export_params;
export_params.relative_paths = false;
export_params.export_textures = false; /* Don't copy all textures, is slow. */
export_params.evaluation_mode = DEG_get_mode(scene_delegate_->depsgraph);
usd::USDExporterContext export_context{scene_delegate_->bmain,
scene_delegate_->depsgraph,
stage,
material_library_path,
get_time_code,
export_params,
blender::io::usd::image_cache_file_path(),
cache_or_get_image_file};
/* Create USD material. */
pxr::UsdShadeMaterial usd_material;
#ifdef WITH_MATERIALX
if (scene_delegate_->use_materialx) {
std::string material_name = pxr::TfMakeValidIdentifier(id->name);
blender::nodes::materialx::ExportParams materialx_export_params{
material_name, cache_or_get_image_file, "st", "UVMap"};
MaterialX::DocumentPtr doc = blender::nodes::materialx::export_to_materialx(
scene_delegate_->depsgraph, (Material *)id, materialx_export_params);
pxr::UsdMtlxRead(doc, stage);
/* Logging stage: creating lambda stage_str() to not call stage->ExportToString()
* if log won't be printed. */
auto stage_str = [&stage]() {
std::string str;
stage->ExportToString(&str);
return str;
};
ID_LOGN(2, "Stage:\n%s", stage_str().c_str());
if (pxr::UsdPrim materials = stage->GetPrimAtPath(pxr::SdfPath("/MaterialX/Materials"))) {
pxr::UsdPrimSiblingRange children = materials.GetChildren();
if (!children.empty()) {
usd_material = pxr::UsdShadeMaterial(*children.begin());
}
}
}
else
#endif
{
usd_material = usd::create_usd_material(
export_context, material_path, (Material *)id, "st", nullptr);
}
/* Convert USD material to Hydra material network map, adapted for render delegate. */
const pxr::HdRenderDelegate *render_delegate =
scene_delegate_->GetRenderIndex().GetRenderDelegate();
const pxr::TfTokenVector contextVector = render_delegate->GetMaterialRenderContexts();
pxr::TfTokenVector shaderSourceTypes = render_delegate->GetShaderSourceTypes();
pxr::HdMaterialNetworkMap network_map;
if (pxr::UsdShadeShader surface = usd_material.ComputeSurfaceSource(contextVector)) {
pxr::UsdImagingBuildHdMaterialNetworkFromTerminal(surface.GetPrim(),
pxr::HdMaterialTerminalTokens->surface,
shaderSourceTypes,
contextVector,
&network_map,
time);
}
material_network_map_ = pxr::VtValue(network_map);
}
void MaterialData::insert()
{
ID_LOGN(1, "");
scene_delegate_->GetRenderIndex().InsertSprim(
pxr::HdPrimTypeTokens->material, scene_delegate_, prim_id);
}
void MaterialData::remove()
{
ID_LOG(1, "");
scene_delegate_->GetRenderIndex().RemoveSprim(pxr::HdPrimTypeTokens->material, prim_id);
}
void MaterialData::update()
{
ID_LOGN(1, "");
bool prev_double_sided = double_sided;
init();
scene_delegate_->GetRenderIndex().GetChangeTracker().MarkSprimDirty(prim_id,
pxr::HdMaterial::AllDirty);
if (prev_double_sided != double_sided) {
for (auto &obj_data : scene_delegate_->objects_.values()) {
MeshData *m_data = dynamic_cast<MeshData *>(obj_data.get());
if (m_data) {
m_data->update_double_sided(this);
}
}
scene_delegate_->instancer_data_->update_double_sided(this);
}
}
pxr::VtValue MaterialData::get_data(pxr::TfToken const & /*key*/) const
{
return pxr::VtValue();
}
pxr::VtValue MaterialData::get_material_resource() const
{
return material_network_map_;
}
pxr::HdCullStyle MaterialData::cull_style() const
{
return double_sided ? pxr::HdCullStyle::HdCullStyleNothing : pxr::HdCullStyle::HdCullStyleBack;
}
} // namespace blender::io::hydra