Hydra: Unify image writing code between USD and Hydra #115397
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue