Hydra: Unify image writing code between USD and Hydra #115397
|
@ -91,6 +91,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
|
||||
|
@ -125,6 +126,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
|
||||
|
|
|
@ -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.h"
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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)) {
|
||||
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 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 image_abs_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 "";
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
MEM_SAFE_FREE(udim_pattern);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* 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,
|
||||
ReportList *reports)
|
||||
{
|
||||
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_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);
|
||||
|
||||
std::string dest_dir(tex_dir_path);
|
||||
std::string dest_path;
|
||||
|
||||
if (is_generated || is_dirty || is_packed) {
|
||||
dest_path = export_in_memory_texture(ima, dest_dir, allow_overwrite, reports);
|
||||
}
|
||||
else if (ima->source == IMA_SRC_TILED) {
|
||||
dest_path = copy_tiled_textures(ima, dest_dir, allow_overwrite, reports);
|
||||
}
|
||||
else {
|
||||
dest_path = copy_single_file(ima, dest_dir, allow_overwrite, reports);
|
||||
}
|
||||
return dest_path;
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
|
@ -0,0 +1,22 @@
|
|||
/* 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);
|
||||
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,
|
||||
ReportList *reports);
|
||||
|
||||
} // namespace blender::io::usd
|
|
@ -3,13 +3,12 @@
|
|||
* 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"
|
||||
#include "usd_hook.h"
|
||||
|
||||
#include "hydra/image.h"
|
||||
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_image_format.h"
|
||||
#include "BKE_main.h"
|
||||
|
@ -113,7 +112,6 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
|
|||
const pxr::TfToken &default_uv);
|
||||
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();
|
||||
|
@ -400,98 +398,6 @@ static void create_uvmap_shader(const USDExporterContext &usd_export_context,
|
|||
}
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
|
@ -617,14 +523,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.
|
||||
|
@ -704,98 +602,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)
|
||||
|
@ -814,7 +620,10 @@ static void export_texture(const USDExporterContext &usd_export_context, bNode *
|
|||
return;
|
||||
}
|
||||
|
||||
hydra::export_texture(ima, export_path, usd_export_context.export_params.overwrite_textures);
|
||||
export_texture(ima,
|
||||
export_path,
|
||||
usd_export_context.export_params.overwrite_textures,
|
||||
usd_export_context.export_params.worker_status->reports);
|
||||
}
|
||||
|
||||
const pxr::TfToken token_for_input(const char *input_name)
|
||||
|
|
Loading…
Reference in New Issue