Hydra: Unify image writing code between USD and Hydra #115397

Open
Bogdan Nagirniak wants to merge 9 commits from BogdanNagirniak/blender:hydra-image-export into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
14 changed files with 376 additions and 437 deletions

View File

@ -89,6 +89,7 @@ set(SRC
intern/usd_writer_camera.cc
intern/usd_writer_curves.cc
intern/usd_writer_hair.cc
intern/usd_writer_image.cc
intern/usd_writer_light.cc
intern/usd_writer_material.cc
intern/usd_writer_mesh.cc
@ -123,6 +124,7 @@ set(SRC
intern/usd_writer_camera.h
intern/usd_writer_curves.h
intern/usd_writer_hair.h
intern/usd_writer_image.h
intern/usd_writer_light.h
intern/usd_writer_material.h
intern/usd_writer_mesh.h
@ -151,7 +153,6 @@ if(WITH_HYDRA)
hydra/curves.cc
hydra/hydra_scene_delegate.cc
hydra/id.cc
hydra/image.cc
hydra/instancer.cc
hydra/light.cc
hydra/material.cc
@ -165,7 +166,6 @@ if(WITH_HYDRA)
hydra/curves.h
hydra/hydra_scene_delegate.h
hydra/id.h
hydra/image.h
hydra/instancer.h
hydra/light.h
hydra/material.h

View File

@ -8,6 +8,10 @@
#include "DNA_scene_types.h"
#include "BKE_appdir.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_set.hh"
#include "BLI_string.h"
@ -36,6 +40,25 @@ HydraSceneDelegate::HydraSceneDelegate(pxr::HdRenderIndex *parent_index,
{
instancer_data_ = std::make_unique<InstancerData>(this, instancer_prim_id());
world_data_ = std::make_unique<WorldData>(this, world_prim_id());
BKE_reports_init(&reports, RPT_PRINT | RPT_STORE);
}
HydraSceneDelegate::~HydraSceneDelegate()
{
BKE_reports_free(&reports);
}
std::string HydraSceneDelegate::cache_file_path(const std::string &file_name, bool mkdir)
{
char path[FILE_MAX];
BLI_path_join(path, sizeof(path), BKE_tempdir_session(), "hydra");
if (mkdir) {
BLI_dir_create_recursive(path);
}
if (!file_name.empty()) {
BLI_path_append(path, sizeof(path), file_name.c_str());
}
return path;
}
pxr::HdMeshTopology HydraSceneDelegate::GetMeshTopology(pxr::SdfPath const &id)

View File

@ -9,6 +9,8 @@
#include "BLI_map.hh"
#include "BKE_report.h"
#include "DEG_depsgraph.hh"
#include "CLG_log.h"
@ -52,6 +54,7 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
const View3D *view3d = nullptr;
Main *bmain = nullptr;
Scene *scene = nullptr;
ReportList reports;
ShadingSettings shading_settings;
bool use_materialx = true;
@ -64,7 +67,9 @@ class HydraSceneDelegate : public pxr::HdSceneDelegate {
public:
HydraSceneDelegate(pxr::HdRenderIndex *parent_index, pxr::SdfPath const &delegate_id);
~HydraSceneDelegate() override = default;
~HydraSceneDelegate() override;
static std::string cache_file_path(const std::string &file_name = "", bool mkdir = false);
/* Delegate methods */
pxr::HdMeshTopology GetMeshTopology(pxr::SdfPath const &id) override;

View File

@ -1,158 +0,0 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "image.h"
#include <pxr/imaging/hio/imageRegistry.h>
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BKE_appdir.h"
#include "BKE_image.h"
#include "BKE_image_format.h"
#include "BKE_image_save.h"
#include "BKE_main.hh"
#include "BKE_packedFile.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "hydra_scene_delegate.h"
namespace blender::io::hydra {
std::string image_cache_file_path()
{
char dir_path[FILE_MAX];
BLI_path_join(dir_path, sizeof(dir_path), BKE_tempdir_session(), "hydra", "image_cache");
return dir_path;
}
static std::string get_cache_file(const std::string &file_name, bool mkdir = true)
{
std::string dir_path = image_cache_file_path();
if (mkdir) {
BLI_dir_create_recursive(dir_path.c_str());
}
char file_path[FILE_MAX];
BLI_path_join(file_path, sizeof(file_path), dir_path.c_str(), file_name.c_str());
return file_path;
}
static std::string cache_image_file(
Main *bmain, Scene *scene, Image *image, ImageUser *iuser, bool check_exist)
{
std::string file_path;
ImageSaveOptions opts;
if (BKE_image_save_options_init(&opts, bmain, scene, image, iuser, false, false)) {
char file_name[32];
const char *r_ext = BLI_path_extension_or_end(image->id.name);
if (!pxr::HioImageRegistry::GetInstance().IsSupportedImageFile(image->id.name)) {
BKE_image_path_ext_from_imformat(&scene->r.im_format, &r_ext);
opts.im_format = scene->r.im_format;
}
SNPRINTF(file_name, "img_%p%s", image, r_ext);
file_path = get_cache_file(file_name);
if (check_exist && BLI_exists(file_path.c_str())) {
return file_path;
}
opts.save_copy = true;
STRNCPY(opts.filepath, file_path.c_str());
if (BKE_image_save(nullptr, bmain, image, iuser, &opts)) {
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s -> %s", image->id.name, file_path.c_str());
}
else {
CLOG_ERROR(LOG_HYDRA_SCENE, "Can't save %s", file_path.c_str());
file_path = "";
}
}
BKE_image_save_options_free(&opts);
return file_path;
}
std::string cache_or_get_image_file(Main *bmain, Scene *scene, Image *image, ImageUser *iuser)
{
char str[FILE_MAX];
std::string file_path;
bool do_check_extension = false;
if (image->source == IMA_SRC_GENERATED) {
file_path = cache_image_file(bmain, scene, image, iuser, false);
}
else if (BKE_image_has_packedfile(image)) {
do_check_extension = true;
std::string dir_path = image_cache_file_path();
char *cached_path;
char subfolder[FILE_MAXDIR];
SNPRINTF(subfolder, "unpack_%p", image);
LISTBASE_FOREACH (ImagePackedFile *, ipf, &image->packedfiles) {
char path[FILE_MAX];
BLI_path_join(
path, sizeof(path), dir_path.c_str(), subfolder, BLI_path_basename(ipf->filepath));
cached_path = BKE_packedfile_unpack_to_file(nullptr,
BKE_main_blendfile_path(bmain),
dir_path.c_str(),
path,
ipf->packedfile,
PF_WRITE_LOCAL);
/* Take first successfully unpacked image. */
if (cached_path != nullptr) {
if (file_path.empty()) {
file_path = cached_path;
}
MEM_freeN(cached_path);
}
}
}
else {
do_check_extension = true;
BKE_image_user_file_path_ex(bmain, iuser, image, str, false, true);
file_path = str;
}
if (do_check_extension && !pxr::HioImageRegistry::GetInstance().IsSupportedImageFile(file_path))
{
file_path = cache_image_file(bmain, scene, image, iuser, true);
}
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s -> %s", image->id.name, file_path.c_str());
return file_path;
}
std::string cache_image_color(float color[4])
{
char name[128];
SNPRINTF(name,
"color_%02d%02d%02d.hdr",
int(color[0] * 255),
int(color[1] * 255),
int(color[2] * 255));
std::string file_path = get_cache_file(name);
if (BLI_exists(file_path.c_str())) {
return file_path;
}
ImBuf *ibuf = IMB_allocImBuf(4, 4, 32, IB_rectfloat);
IMB_rectfill(ibuf, color);
ibuf->ftype = IMB_FTYPE_RADHDR;
if (IMB_saveiff(ibuf, file_path.c_str(), IB_rectfloat)) {
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s", file_path.c_str());
}
else {
CLOG_ERROR(LOG_HYDRA_SCENE, "Can't save %s", file_path.c_str());
file_path = "";
}
IMB_freeImBuf(ibuf);
return file_path;
}
} // namespace blender::io::hydra

View File

@ -1,21 +0,0 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <string>
struct Main;
struct Scene;
struct Image;
struct ImageUser;
namespace blender::io::hydra {
std::string image_cache_file_path();
std::string cache_or_get_image_file(Main *bmain, Scene *Scene, Image *image, ImageUser *iuser);
std::string cache_image_color(float color[4]);
} // namespace blender::io::hydra

View File

@ -31,9 +31,9 @@
#include "bpy_rna.h"
#include "hydra_scene_delegate.h"
#include "image.h"
#include "intern/usd_exporter_context.h"
#include "intern/usd_writer_image.h"
#include "intern/usd_writer_material.h"
#ifdef WITH_MATERIALX
@ -77,13 +77,19 @@ void MaterialData::init()
material_library_path,
get_time_code,
export_params,
image_cache_file_path()};
scene_delegate_->cache_file_path()};
/* Create USD material. */
pxr::UsdShadeMaterial usd_material;
#ifdef WITH_MATERIALX
if (scene_delegate_->use_materialx) {
MaterialX::DocumentPtr doc = blender::nodes::materialx::export_to_materialx(
scene_delegate_->depsgraph, (Material *)id, cache_or_get_image_file);
scene_delegate_->depsgraph, (Material *)id, [this](Image *image) {
return usd::export_texture(image,
this->scene_delegate_->cache_file_path(),
false,
true,
&this->scene_delegate_->reports);
});
pxr::UsdMtlxRead(doc, stage);
/* Logging stage: creating lambda stage_str() to not call stage->ExportToString()

View File

@ -3,6 +3,9 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "world.h"
#include "hydra_scene_delegate.h"
#include "intern/usd_writer_image.h"
#include <pxr/base/gf/rotation.h>
#include <pxr/base/gf/vec2f.h>
@ -15,17 +18,19 @@
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "BLI_fileops.h"
#include "BLI_math_rotation.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_studiolight.h"
#include "NOD_shader.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "hydra_scene_delegate.h"
#include "image.h"
#include "NOD_shader.h"
/* TODO: add custom `tftoken` "transparency"? */
@ -39,6 +44,35 @@ WorldData::WorldData(HydraSceneDelegate *scene_delegate, pxr::SdfPath const &pri
prim_type_ = pxr::HdPrimTypeTokens->domeLight;
}
static std::string cache_image_color(float color[4])
{
char name[128];
SNPRINTF(name,
"color_%02d%02d%02d.hdr",
int(color[0] * 255),
int(color[1] * 255),
int(color[2] * 255));
std::string file_path = HydraSceneDelegate::cache_file_path(name, true);
if (BLI_exists(file_path.c_str())) {
return file_path;
}
ImBuf *ibuf = IMB_allocImBuf(4, 4, 32, IB_rectfloat);
IMB_rectfill(ibuf, color);
ibuf->ftype = IMB_FTYPE_RADHDR;
if (IMB_saveiff(ibuf, file_path.c_str(), IB_rectfloat)) {
CLOG_INFO(LOG_HYDRA_SCENE, 1, "%s", file_path.c_str());
}
else {
CLOG_ERROR(LOG_HYDRA_SCENE, "Can't save %s", file_path.c_str());
file_path = "";
}
IMB_freeImBuf(ibuf);
return file_path;
}
void WorldData::init()
{
data_.clear();
@ -93,11 +127,10 @@ void WorldData::init()
if (!color_input.directly_linked_links().is_empty()) {
bNode *color_input_node = color_input.directly_linked_links()[0]->fromnode;
if (ELEM(color_input_node->type, SH_NODE_TEX_IMAGE, SH_NODE_TEX_ENVIRONMENT)) {
NodeTexImage *tex = static_cast<NodeTexImage *>(color_input_node->storage);
Image *image = (Image *)color_input_node->id;
if (image) {
std::string image_path = cache_or_get_image_file(
scene_delegate_->bmain, scene_delegate_->scene, image, &tex->iuser);
std::string image_path = usd::export_texture(
image, scene_delegate_->cache_file_path(), false, true, nullptr);
if (!image_path.empty()) {
texture_file = pxr::SdfAssetPath(image_path, image_path);
}

View File

@ -0,0 +1,259 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_writer_image.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BKE_image.h"
#include "BKE_image_format.h"
#include "BKE_image_save.h"
#include "BKE_main.hh"
#include "BKE_packedFile.h"
#include "BKE_report.h"
#include "BLI_memory_utils.hh"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
namespace blender::io::usd {
/* Get the absolute filepath of the given image. Assumes
* r_path result array is of length FILE_MAX. */
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);
}
std::string get_tex_image_asset_filepath(Image *ima)
{
char filepath[FILE_MAX];
get_absolute_path(ima, filepath);
return std::string(filepath);
}
std::string get_in_memory_texture_filename(Image *ima)
{
bool is_dirty = BKE_image_is_dirty(ima);
bool is_generated = ima->source == IMA_SRC_GENERATED;
bool is_packed = BKE_image_has_packedfile(ima);
if (!(is_generated || is_dirty || is_packed)) {
return "";
}
/* Determine the correct file extension from the image format. */
ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
if (!imbuf) {
return "";
}
ImageFormatData imageFormat;
BKE_image_format_from_imbuf(&imageFormat, imbuf);
BKE_image_release_ibuf(ima, imbuf, nullptr);
char file_name[FILE_MAX];
/* Use the image name for the file name. */
STRNCPY(file_name, ima->id.name + 2);
BKE_image_path_ext_from_imformat_ensure(file_name, sizeof(file_name), &imageFormat);
return file_name;
}
/* Copy the given image to the destination directory. */
static std::string copy_single_file(Image *ima,
const std::string &dest_dir,
const bool allow_overwrite,
ReportList *reports)
{
char source_path[FILE_MAX];
get_absolute_path(ima, source_path);
char file_name[FILE_MAX];
BLI_path_split_file_part(source_path, file_name, FILE_MAX);
char dest_path[FILE_MAX];
BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name);
if (!allow_overwrite && BLI_exists(dest_path)) {
return "";
}
if (BLI_path_cmp_normalized(source_path, dest_path) == 0) {
/* Source and destination paths are the same, don't copy. */
return "";
}
BKE_reportf(reports, RPT_INFO, "Copying texture from %s to %s", source_path, dest_path);
/* Copy the file. */
if (BLI_copy(source_path, dest_path) != 0) {
BKE_reportf(reports,
RPT_WARNING,
"USD export: could not copy texture from %s to %s",
source_path,
dest_path);
return "";
}
return dest_path;
}
static std::string export_in_memory_texture(Image *ima,
const std::string &export_dir,
const bool allow_overwrite,
ReportList *reports)
{
char image_abs_path[FILE_MAX];
char file_name[FILE_MAX];
if (strlen(ima->filepath) > 0) {
get_absolute_path(ima, image_abs_path);
BLI_path_split_file_part(image_abs_path, file_name, FILE_MAX);
}
else {
/* Use the image name for the file name. */
STRNCPY(file_name, ima->id.name + 2);
}
ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
BLI_SCOPED_DEFER([&]() { BKE_image_release_ibuf(ima, imbuf, nullptr); });
if (!imbuf) {
return "";
}
ImageFormatData imageFormat;
BKE_image_format_from_imbuf(&imageFormat, imbuf);
/* This image in its current state only exists in Blender memory.
* So we have to export it. The export will keep the image state intact,
* so the exported file will not be associated with the image. */
BKE_image_path_ext_from_imformat_ensure(file_name, sizeof(file_name), &imageFormat);
char export_path[FILE_MAX];
BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name);
if (!allow_overwrite && BLI_exists(export_path) && !BKE_image_is_dirty(ima)) {
return export_path;
}
if ((BLI_path_cmp_normalized(export_path, image_abs_path) == 0) && BLI_exists(image_abs_path)) {
/* As a precaution, don't overwrite the original path. */
return image_abs_path;
}
BKE_reportf(reports, RPT_INFO, "Exporting in-memory texture to %s", export_path);
if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) {
BKE_reportf(
reports, RPT_WARNING, "USD export: couldn't export in-memory texture to %s", export_path);
}
return export_path;
}
/* If the given image is tiled, copy the image tiles to the given
* destination directory. */
static std::string copy_tiled_textures(Image *ima,
const std::string &dest_dir,
const bool allow_overwrite,
ReportList *reports)
{
char src_path[FILE_MAX];
get_absolute_path(ima, src_path);
eUDIM_TILE_FORMAT tile_format;
char *udim_pattern = BKE_image_get_tile_strformat(src_path, &tile_format);
/* Only <UDIM> tile formats are supported by USD right now. */
if (tile_format != UDIM_TILE_FORMAT_UDIM) {
BKE_reportf(reports, RPT_WARNING, "Unsupported tile format for `%s`", src_path);
MEM_SAFE_FREE(udim_pattern);
return "";
}
std::string dest_path;
/* Copy all tiles. */
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
char src_tile_path[FILE_MAX];
BKE_image_set_filepath_from_tile_number(
src_tile_path, udim_pattern, tile_format, tile->tile_number);
char dest_filename[FILE_MAXFILE];
BLI_path_split_file_part(src_tile_path, dest_filename, sizeof(dest_filename));
char dest_tile_path[FILE_MAX];
BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename);
if (!allow_overwrite && BLI_exists(dest_tile_path)) {
continue;
}
if (BLI_path_cmp_normalized(src_tile_path, dest_tile_path) == 0) {
/* Source and destination paths are the same, don't copy. */
continue;
}
BKE_reportf(
reports, RPT_INFO, "Copying texture tile from %s to %s", src_tile_path, dest_tile_path);
/* Copy the file. */
if (BLI_copy(src_tile_path, dest_tile_path) != 0) {
BKE_reportf(reports,
RPT_WARNING,
"USD export: could not copy texture tile from %s to %s",
src_tile_path,
dest_tile_path);
}
if (dest_path.empty()) {
dest_path = dest_tile_path;
}
}
MEM_SAFE_FREE(udim_pattern);
return dest_path;
}
/* Export the given texture node's image to a 'textures' directory in the export path.
* Based on ImagesExporter::export_UV_Image() */
std::string export_texture(Image *ima,
const std::string &export_path,
bool allow_overwrite,
bool only_in_memory,
ReportList *reports)
{
const bool is_dirty = BKE_image_is_dirty(ima);
const bool is_generated = ima->source == IMA_SRC_GENERATED;
const bool is_packed = BKE_image_has_packedfile(ima);
std::string dest_path;
if (is_generated || is_dirty || is_packed) {
BLI_dir_create_recursive(export_path.c_str());
dest_path = export_in_memory_texture(ima, export_path, allow_overwrite, reports);
}
else if (only_in_memory) {
dest_path = get_tex_image_asset_filepath(ima);
}
else {
BLI_dir_create_recursive(export_path.c_str());
if (ima->source == IMA_SRC_TILED) {
dest_path = copy_tiled_textures(ima, export_path, allow_overwrite, reports);
}
else {
dest_path = copy_single_file(ima, export_path, allow_overwrite, reports);
}
}
return dest_path;
}
} // namespace blender::io::usd

View File

@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <string>
struct Image;
struct ReportList;
namespace blender::io::usd {
std::string get_tex_image_asset_filepath(Image *ima);
/* Generate a file name for an in-memory image that doesn't have a
* filepath already defined. */
std::string get_in_memory_texture_filename(Image *ima);
/* Export the given texture */
std::string export_texture(Image *ima,
const std::string &export_path,
bool allow_overwrite,
bool only_in_memory,
ReportList *reports);
} // namespace blender::io::usd

View File

@ -3,6 +3,7 @@
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "usd_writer_material.h"
#include "usd_writer_image.h"
#include "usd.h"
#include "usd_exporter_context.h"
@ -121,7 +122,6 @@ static void create_uv_input(const USDExporterContext &usd_export_context,
ReportList *reports);
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);
static InputSpecMap &preview_surface_input_map();
@ -505,98 +505,6 @@ static void create_uv_input(const USDExporterContext &usd_export_context,
usd_export_context, uvmap_link, usd_material, usd_input, default_uv, reports);
}
/* Generate a file name for an in-memory image that doesn't have a
* filepath already defined. */
static std::string get_in_memory_texture_filename(Image *ima)
{
bool is_dirty = BKE_image_is_dirty(ima);
bool is_generated = ima->source == IMA_SRC_GENERATED;
bool is_packed = BKE_image_has_packedfile(ima);
if (!(is_generated || is_dirty || is_packed)) {
return "";
}
/* Determine the correct file extension from the image format. */
ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
if (!imbuf) {
return "";
}
ImageFormatData imageFormat;
BKE_image_format_from_imbuf(&imageFormat, imbuf);
BKE_image_release_ibuf(ima, imbuf, nullptr);
char file_name[FILE_MAX];
/* Use the image name for the file name. */
STRNCPY(file_name, ima->id.name + 2);
BKE_image_path_ext_from_imformat_ensure(file_name, sizeof(file_name), &imageFormat);
return file_name;
}
static void export_in_memory_texture(Image *ima,
const std::string &export_dir,
const bool allow_overwrite,
ReportList *reports)
{
char image_abs_path[FILE_MAX];
char file_name[FILE_MAX];
if (strlen(ima->filepath) > 0) {
get_absolute_path(ima, image_abs_path);
BLI_path_split_file_part(image_abs_path, file_name, FILE_MAX);
}
else {
/* Use the image name for the file name. */
STRNCPY(file_name, ima->id.name + 2);
}
ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
BLI_SCOPED_DEFER([&]() { BKE_image_release_ibuf(ima, imbuf, nullptr); });
if (!imbuf) {
return;
}
ImageFormatData imageFormat;
BKE_image_format_from_imbuf(&imageFormat, imbuf);
/* This image in its current state only exists in Blender memory.
* So we have to export it. The export will keep the image state intact,
* so the exported file will not be associated with the image. */
BKE_image_path_ext_from_imformat_ensure(file_name, sizeof(file_name), &imageFormat);
char export_path[FILE_MAX];
BLI_path_join(export_path, FILE_MAX, export_dir.c_str(), file_name);
if (!allow_overwrite && BLI_exists(export_path)) {
return;
}
if ((BLI_path_cmp_normalized(export_path, image_abs_path) == 0) && BLI_exists(image_abs_path)) {
/* As a precaution, don't overwrite the original path. */
return;
}
std::cout << "Exporting in-memory texture to " << export_path << std::endl;
if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) {
BKE_reportf(
reports, RPT_WARNING, "USD export: couldn't export in-memory texture to %s", export_path);
}
}
/* Get the absolute filepath of the given image. Assumes
* r_path result array is of length FILE_MAX. */
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);
}
static pxr::TfToken get_node_tex_image_color_space(bNode *node)
{
if (!node->id) {
@ -761,14 +669,6 @@ static pxr::UsdShadeShader create_usd_preview_shader(const USDExporterContext &u
return shader;
}
static std::string get_tex_image_asset_filepath(Image *ima)
{
char filepath[FILE_MAX];
get_absolute_path(ima, filepath);
return std::string(filepath);
}
/* 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.
@ -848,98 +748,6 @@ static std::string get_tex_image_asset_filepath(const USDExporterContext &usd_ex
return path;
}
/* If the given image is tiled, copy the image tiles to the given
* destination directory. */
static void copy_tiled_textures(Image *ima,
const std::string &dest_dir,
const bool allow_overwrite,
ReportList *reports)
{
char src_path[FILE_MAX];
get_absolute_path(ima, src_path);
eUDIM_TILE_FORMAT tile_format;
char *udim_pattern = BKE_image_get_tile_strformat(src_path, &tile_format);
/* Only <UDIM> tile formats are supported by USD right now. */
if (tile_format != UDIM_TILE_FORMAT_UDIM) {
std::cout << "WARNING: unsupported tile format for `" << src_path << "`" << std::endl;
MEM_SAFE_FREE(udim_pattern);
return;
}
/* Copy all tiles. */
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
char src_tile_path[FILE_MAX];
BKE_image_set_filepath_from_tile_number(
src_tile_path, udim_pattern, tile_format, tile->tile_number);
char dest_filename[FILE_MAXFILE];
BLI_path_split_file_part(src_tile_path, dest_filename, sizeof(dest_filename));
char dest_tile_path[FILE_MAX];
BLI_path_join(dest_tile_path, FILE_MAX, dest_dir.c_str(), dest_filename);
if (!allow_overwrite && BLI_exists(dest_tile_path)) {
continue;
}
if (BLI_path_cmp_normalized(src_tile_path, dest_tile_path) == 0) {
/* Source and destination paths are the same, don't copy. */
continue;
}
std::cout << "Copying texture tile from " << src_tile_path << " to " << dest_tile_path
<< std::endl;
/* Copy the file. */
if (BLI_copy(src_tile_path, dest_tile_path) != 0) {
BKE_reportf(reports,
RPT_WARNING,
"USD export: could not copy texture tile from %s to %s",
src_tile_path,
dest_tile_path);
}
}
MEM_SAFE_FREE(udim_pattern);
}
/* Copy the given image to the destination directory. */
static void copy_single_file(Image *ima,
const std::string &dest_dir,
const bool allow_overwrite,
ReportList *reports)
{
char source_path[FILE_MAX];
get_absolute_path(ima, source_path);
char file_name[FILE_MAX];
BLI_path_split_file_part(source_path, file_name, FILE_MAX);
char dest_path[FILE_MAX];
BLI_path_join(dest_path, FILE_MAX, dest_dir.c_str(), file_name);
if (!allow_overwrite && BLI_exists(dest_path)) {
return;
}
if (BLI_path_cmp_normalized(source_path, dest_path) == 0) {
/* Source and destination paths are the same, don't copy. */
return;
}
std::cout << "Copying texture from " << source_path << " to " << dest_path << std::endl;
/* Copy the file. */
if (BLI_copy(source_path, dest_path) != 0) {
BKE_reportf(reports,
RPT_WARNING,
"USD export: could not copy texture from %s to %s",
source_path,
dest_path);
}
}
/* Export the given texture node's image to a 'textures' directory in the export path.
* Based on ImagesExporter::export_UV_Image() */
static void export_texture(const USDExporterContext &usd_export_context, bNode *node)
@ -953,38 +761,19 @@ static void export_texture(const USDExporterContext &usd_export_context, bNode *
return;
}
std::string export_path = usd_export_context.export_file_path;
if (export_path.empty()) {
if (usd_export_context.export_file_path.empty()) {
return;
}
char usd_dir_path[FILE_MAX];
BLI_path_split_dir_part(export_path.c_str(), usd_dir_path, FILE_MAX);
char tex_dir_path[FILE_MAX];
BLI_path_join(tex_dir_path, FILE_MAX, usd_dir_path, "textures", SEP_STR);
BLI_path_split_dir_part(usd_export_context.export_file_path.c_str(), tex_dir_path, FILE_MAX);
BLI_path_append_dir(tex_dir_path, FILE_MAX, "textures");
BLI_dir_create_recursive(tex_dir_path);
const bool is_dirty = BKE_image_is_dirty(ima);
const bool is_generated = ima->source == IMA_SRC_GENERATED;
const bool is_packed = BKE_image_has_packedfile(ima);
const bool allow_overwrite = usd_export_context.export_params.overwrite_textures;
std::string dest_dir(tex_dir_path);
if (is_generated || is_dirty || is_packed) {
export_in_memory_texture(
ima, dest_dir, allow_overwrite, usd_export_context.export_params.worker_status->reports);
}
else if (ima->source == IMA_SRC_TILED) {
copy_tiled_textures(
ima, dest_dir, allow_overwrite, usd_export_context.export_params.worker_status->reports);
}
else {
copy_single_file(
ima, dest_dir, allow_overwrite, usd_export_context.export_params.worker_status->reports);
}
export_texture(ima,
tex_dir_path,
usd_export_context.export_params.overwrite_textures,
false,
usd_export_context.export_params.worker_status->reports);
}
const pxr::TfToken token_for_input(const char *input_name)

View File

@ -18,7 +18,7 @@ extern struct CLG_LogRef *LOG_MATERIALX_SHADER;
class GroupNodeParser;
using ExportImageFunction = std::function<std::string(Main *, Scene *, Image *, ImageUser *)>;
using ExportImageFunction = std::function<std::string(Image *)>;
/**
* This is base abstraction class for parsing Blender nodes into MaterialX nodes.

View File

@ -143,9 +143,7 @@ NODE_SHADER_MATERIALX_BEGIN
std::string image_path = image->id.name;
if (export_image_fn_) {
Scene *scene = DEG_get_input_scene(depsgraph_);
Main *bmain = DEG_get_bmain(depsgraph_);
image_path = export_image_fn_(bmain, scene, image, &tex_env->iuser);
image_path = export_image_fn_(image);
}
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);

View File

@ -191,9 +191,7 @@ NODE_SHADER_MATERIALX_BEGIN
std::string image_path = image->id.name;
if (export_image_fn_) {
Scene *scene = DEG_get_input_scene(depsgraph_);
Main *bmain = DEG_get_bmain(depsgraph_);
image_path = export_image_fn_(bmain, scene, image, &tex_image->iuser);
image_path = export_image_fn_(image);
}
NodeItem vector = get_input_link("Vector", NodeItem::Type::Vector2);

View File

@ -19,8 +19,6 @@
#include "RNA_prototypes.h"
#include "hydra/image.h"
namespace blender::render::hydra {
template<typename T> T *pyrna_to_pointer(PyObject *pyobject, const StructRNA *rnatype)
@ -164,21 +162,6 @@ static PyObject *engine_set_render_setting_func(PyObject * /*self*/, PyObject *a
Py_RETURN_NONE;
}
static PyObject *cache_or_get_image_file_func(PyObject * /*self*/, PyObject *args)
{
PyObject *pycontext, *pyimage;
if (!PyArg_ParseTuple(args, "OO", &pycontext, &pyimage)) {
return nullptr;
}
bContext *context = static_cast<bContext *>(PyLong_AsVoidPtr(pycontext));
Image *image = static_cast<Image *>(PyLong_AsVoidPtr(pyimage));
std::string image_path = io::hydra::cache_or_get_image_file(
CTX_data_main(context), CTX_data_scene(context), image, nullptr);
return PyC_UnicodeFromBytes(image_path.c_str());
}
static PyMethodDef methods[] = {
{"engine_create", engine_create_func, METH_VARARGS, ""},
{"engine_free", engine_free_func, METH_VARARGS, ""},
@ -187,8 +170,6 @@ static PyMethodDef methods[] = {
{"engine_view_draw", engine_view_draw_func, METH_VARARGS, ""},
{"engine_set_render_setting", engine_set_render_setting_func, METH_VARARGS, ""},
{"cache_or_get_image_file", cache_or_get_image_file_func, METH_VARARGS, ""},
{nullptr, nullptr, 0, nullptr},
};