Metal: Fix possible uniform lookup issue. #105239
|
@ -491,7 +491,7 @@ class TOPBAR_MT_file_export(Menu):
|
|||
self.layout.operator("wm.alembic_export", text="Alembic (.abc)")
|
||||
if bpy.app.build_options.usd:
|
||||
self.layout.operator(
|
||||
"wm.usd_export", text="Universal Scene Description (.usd, .usdc, .usda)")
|
||||
"wm.usd_export", text="Universal Scene Description (.usd*)")
|
||||
|
||||
if bpy.app.build_options.io_gpencil:
|
||||
# Pugixml lib dependency
|
||||
|
|
|
@ -57,8 +57,8 @@ int BLI_delete(const char *file, bool dir, bool recursive) ATTR_NONNULL();
|
|||
* \return zero on success (matching 'remove' behavior).
|
||||
*/
|
||||
int BLI_delete_soft(const char *file, const char **error_message) ATTR_NONNULL();
|
||||
int BLI_path_move(const char *path, const char *to) ATTR_NONNULL();
|
||||
#if 0 /* Unused */
|
||||
int BLI_move(const char *path, const char *to) ATTR_NONNULL();
|
||||
int BLI_create_symlink(const char *path, const char *to) ATTR_NONNULL();
|
||||
#endif
|
||||
|
||||
|
@ -142,6 +142,18 @@ double BLI_dir_free_space(const char *dir) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(
|
|||
*/
|
||||
char *BLI_current_working_dir(char *dir, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
eFileAttributes BLI_file_attributes(const char *path);
|
||||
/**
|
||||
* Changes the current working directory to the provided path.
|
||||
*
|
||||
* Usage of this function is strongly discouraged as it is not thread safe. It will likely cause
|
||||
* issues if there is an operation on another thread that does not expect the current working
|
||||
* directory to change. This has been added to support USDZ export, which has a problematic
|
||||
* "feature" described in this issue https://projects.blender.org/blender/blender/issues/99807. It
|
||||
* will be removed if it is possible to resolve that issue upstream in the USD library.
|
||||
*
|
||||
* \return true on success, false otherwise.
|
||||
*/
|
||||
bool BLI_change_working_dir(const char *dir);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -458,9 +458,7 @@ int BLI_delete_soft(const char *file, const char **error_message)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* Not used anywhere! */
|
||||
# if 0
|
||||
int BLI_move(const char *file, const char *to)
|
||||
int BLI_path_move(const char *file, const char *to)
|
||||
{
|
||||
char str[MAXPATHLEN + 12];
|
||||
int err;
|
||||
|
@ -490,7 +488,6 @@ int BLI_move(const char *file, const char *to)
|
|||
|
||||
return err;
|
||||
}
|
||||
# endif
|
||||
|
||||
int BLI_copy(const char *file, const char *to)
|
||||
{
|
||||
|
@ -822,8 +819,8 @@ static int delete_soft(const char *file, const char **error_message)
|
|||
|
||||
Class NSStringClass = objc_getClass("NSString");
|
||||
SEL stringWithUTF8StringSel = sel_registerName("stringWithUTF8String:");
|
||||
id pathString = ((
|
||||
id(*)(Class, SEL, const char *))objc_msgSend)(NSStringClass, stringWithUTF8StringSel, file);
|
||||
id pathString = ((id(*)(Class, SEL, const char *))objc_msgSend)(
|
||||
NSStringClass, stringWithUTF8StringSel, file);
|
||||
|
||||
Class NSFileManagerClass = objc_getClass("NSFileManager");
|
||||
SEL defaultManagerSel = sel_registerName("defaultManager");
|
||||
|
@ -834,8 +831,8 @@ static int delete_soft(const char *file, const char **error_message)
|
|||
id nsurl = ((id(*)(Class, SEL, id))objc_msgSend)(NSURLClass, fileURLWithPathSel, pathString);
|
||||
|
||||
SEL trashItemAtURLSel = sel_registerName("trashItemAtURL:resultingItemURL:error:");
|
||||
BOOL deleteSuccessful = ((
|
||||
BOOL(*)(id, SEL, id, id, id))objc_msgSend)(fileManager, trashItemAtURLSel, nsurl, nil, nil);
|
||||
BOOL deleteSuccessful = ((BOOL(*)(id, SEL, id, id, id))objc_msgSend)(
|
||||
fileManager, trashItemAtURLSel, nsurl, nil, nil);
|
||||
|
||||
if (deleteSuccessful) {
|
||||
ret = 0;
|
||||
|
@ -1123,8 +1120,6 @@ static int copy_single_file(const char *from, const char *to)
|
|||
return RecursiveOp_Callback_OK;
|
||||
}
|
||||
|
||||
/* Not used anywhere! */
|
||||
# if 0
|
||||
static int move_callback_pre(const char *from, const char *to)
|
||||
{
|
||||
int ret = rename(from, to);
|
||||
|
@ -1149,7 +1144,7 @@ static int move_single_file(const char *from, const char *to)
|
|||
|
||||
/* if *file represents a directory, moves all its contents into *to, else renames
|
||||
* file itself to *to. */
|
||||
int BLI_move(const char *file, const char *to)
|
||||
int BLI_path_move(const char *file, const char *to)
|
||||
{
|
||||
int ret = recursive_operation(file, to, move_callback_pre, move_single_file, NULL);
|
||||
|
||||
|
@ -1159,7 +1154,6 @@ int BLI_move(const char *file, const char *to)
|
|||
|
||||
return ret;
|
||||
}
|
||||
# endif
|
||||
|
||||
static const char *check_destination(const char *file, const char *to)
|
||||
{
|
||||
|
|
|
@ -54,11 +54,36 @@
|
|||
#include "BLI_linklist.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#if !defined(__APPLE__)
|
||||
/* The implementation for Apple lives in storage_apple.mm.*/
|
||||
bool BLI_change_working_dir(const char *dir)
|
||||
{
|
||||
BLI_assert(BLI_thread_is_main());
|
||||
|
||||
if (!BLI_is_dir(dir)) {
|
||||
return false;
|
||||
}
|
||||
# if defined(WIN32)
|
||||
wchar_t wdir[FILE_MAX];
|
||||
if (conv_utf_8_to_16(dir, wdir, ARRAY_SIZE(wdir)) != 0) {
|
||||
return false;
|
||||
}
|
||||
return _wchdir(wdir) == 0;
|
||||
# else
|
||||
int result = chdir(dir);
|
||||
if (result == 0) {
|
||||
BLI_setenv("PWD", dir);
|
||||
}
|
||||
return result == 0;
|
||||
# endif
|
||||
}
|
||||
|
||||
char *BLI_current_working_dir(char *dir, const size_t maxncpy)
|
||||
{
|
||||
#if defined(WIN32)
|
||||
# if defined(WIN32)
|
||||
wchar_t path[MAX_PATH];
|
||||
if (_wgetcwd(path, MAX_PATH)) {
|
||||
if (BLI_strncpy_wchar_as_utf8(dir, path, maxncpy) != maxncpy) {
|
||||
|
@ -66,7 +91,7 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
|
|||
}
|
||||
}
|
||||
return NULL;
|
||||
#else
|
||||
# else
|
||||
const char *pwd = BLI_getenv("PWD");
|
||||
if (pwd) {
|
||||
size_t srclen = BLI_strnlen(pwd, maxncpy);
|
||||
|
@ -77,8 +102,9 @@ char *BLI_current_working_dir(char *dir, const size_t maxncpy)
|
|||
return NULL;
|
||||
}
|
||||
return getcwd(dir, maxncpy);
|
||||
#endif
|
||||
# endif
|
||||
}
|
||||
#endif /* !defined (__APPLE__) */
|
||||
|
||||
double BLI_dir_free_space(const char *dir)
|
||||
{
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
|
||||
/* Extended file attribute used by OneDrive to mark placeholder files. */
|
||||
static const char *ONEDRIVE_RECALLONOPEN_ATTRIBUTE = "com.microsoft.OneDrive.RecallOnOpen";
|
||||
|
@ -185,3 +187,27 @@ const char *BLI_expand_tilde(const char *path_with_tilde)
|
|||
}
|
||||
return path_expanded;
|
||||
}
|
||||
|
||||
char *BLI_current_working_dir(char *dir, const size_t maxncpy) {
|
||||
/* Can't just copy to the *dir pointer, as [path getCString gets grumpy.*/
|
||||
char path_expanded[PATH_MAX];
|
||||
@autoreleasepool {
|
||||
NSString *path = [[NSFileManager defaultManager] currentDirectoryPath];
|
||||
const size_t length = maxncpy > PATH_MAX ? PATH_MAX : maxncpy;
|
||||
[path getCString:path_expanded maxLength:length encoding:NSUTF8StringEncoding];
|
||||
BLI_strncpy(dir, path_expanded, maxncpy);
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
bool BLI_change_working_dir(const char* dir) {
|
||||
@autoreleasepool {
|
||||
NSString* path = [[NSString alloc] initWithUTF8String: dir];
|
||||
if ([[NSFileManager defaultManager] changeCurrentDirectoryPath: path] == YES) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,33 @@
|
|||
/* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
namespace blender::tests {
|
||||
|
||||
class ChangeWorkingDirectoryTest : public testing::Test {
|
||||
public:
|
||||
std::string test_temp_dir;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
/* Must use because BLI_change_working_dir() checks that we are on the main thread. */
|
||||
BLI_threadapi_init();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
if (!test_temp_dir.empty()) {
|
||||
BLI_delete(test_temp_dir.c_str(), true, false);
|
||||
}
|
||||
|
||||
BLI_threadapi_exit();
|
||||
}
|
||||
};
|
||||
|
||||
TEST(fileops, fstream_open_string_filename)
|
||||
{
|
||||
const std::string test_files_dir = blender::tests::flags_test_asset_dir();
|
||||
|
@ -37,4 +59,62 @@ TEST(fileops, fstream_open_charptr_filename)
|
|||
/* Reading the file not tested here. That's deferred to `std::fstream` anyway. */
|
||||
}
|
||||
|
||||
TEST_F(ChangeWorkingDirectoryTest, change_working_directory)
|
||||
{
|
||||
char original_cwd_buff[FILE_MAX];
|
||||
char *original_cwd = BLI_current_working_dir(original_cwd_buff, sizeof(original_cwd_buff));
|
||||
|
||||
ASSERT_FALSE(original_cwd == nullptr) << "Unable to get the current working directory.";
|
||||
/* While some implementation of `getcwd` (or similar) may return allocated memory in some cases,
|
||||
* in the context of `BLI_current_working_dir` usages, this is not expected and should not
|
||||
* happen. */
|
||||
ASSERT_TRUE(original_cwd == original_cwd_buff)
|
||||
<< "Returned CWD path unexpectedly different than given char buffer.";
|
||||
|
||||
std::string temp_file_name(std::tmpnam(nullptr));
|
||||
test_temp_dir = temp_file_name + "_новый";
|
||||
|
||||
if (BLI_exists(test_temp_dir.c_str())) {
|
||||
BLI_delete(test_temp_dir.c_str(), true, false);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(BLI_change_working_dir(test_temp_dir.c_str()))
|
||||
<< "changing directory to a non-existent directory is expected to fail.";
|
||||
|
||||
ASSERT_TRUE(BLI_dir_create_recursive(test_temp_dir.c_str()))
|
||||
<< "temporary directory should have been created successfully.";
|
||||
|
||||
ASSERT_TRUE(BLI_change_working_dir(test_temp_dir.c_str()))
|
||||
<< "temporary directory should succeed changing directory.";
|
||||
|
||||
char new_cwd_buff[FILE_MAX];
|
||||
char *new_cwd = BLI_current_working_dir(new_cwd_buff, sizeof(new_cwd_buff));
|
||||
ASSERT_FALSE(new_cwd == nullptr) << "Unable to get the current working directory.";
|
||||
ASSERT_TRUE(new_cwd == new_cwd_buff)
|
||||
<< "Returned CWD path unexpectedly different than given char buffer.";
|
||||
|
||||
#ifdef __APPLE__
|
||||
/* The name returned by std::tmpnam is fine but the Apple OS method
|
||||
* picks the true var folder, not the alias, meaning the below
|
||||
* comparison always fails unless we prepend with the correct value. */
|
||||
test_temp_dir = "/private" + test_temp_dir;
|
||||
#endif // #ifdef __APPLE__
|
||||
|
||||
ASSERT_EQ(BLI_path_cmp_normalized(new_cwd, test_temp_dir.c_str()), 0)
|
||||
<< "the path of the current working directory should equal the path of the temporary "
|
||||
"directory that was created.";
|
||||
|
||||
ASSERT_TRUE(BLI_change_working_dir(original_cwd))
|
||||
<< "changing directory back to the original working directory should succeed.";
|
||||
|
||||
char final_cwd_buff[FILE_MAX];
|
||||
char *final_cwd = BLI_current_working_dir(final_cwd_buff, sizeof(final_cwd_buff));
|
||||
ASSERT_FALSE(final_cwd == nullptr) << "Unable to get the current working directory.";
|
||||
ASSERT_TRUE(final_cwd == final_cwd_buff)
|
||||
<< "Returned CWD path unexpectedly different than given char buffer.";
|
||||
|
||||
ASSERT_EQ(BLI_path_cmp_normalized(final_cwd, original_cwd), 0)
|
||||
<< "The final CWD path should be the same as the original CWD path.";
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
|
|
@ -226,7 +226,7 @@ static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op)
|
|||
char filepath[FILE_MAX];
|
||||
RNA_string_get(op->ptr, "filepath", filepath);
|
||||
|
||||
if (!BLI_path_extension_check_n(filepath, ".usd", ".usda", ".usdc", NULL)) {
|
||||
if (!BLI_path_extension_check_n(filepath, ".usd", ".usda", ".usdc", ".usdz", NULL)) {
|
||||
BLI_path_extension_ensure(filepath, FILE_MAX, ".usdc");
|
||||
RNA_string_set(op->ptr, "filepath", filepath);
|
||||
return true;
|
||||
|
|
|
@ -151,7 +151,7 @@ if(WITH_GTESTS)
|
|||
tests/usd_stage_creation_test.cc
|
||||
tests/usd_tests_common.cc
|
||||
tests/usd_tests_common.h
|
||||
|
||||
tests/usd_usdz_export_test.cc
|
||||
intern/usd_writer_material.h
|
||||
)
|
||||
if(USD_IMAGING_HEADERS)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <pxr/usd/usd/primRange.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#include <pxr/usd/usdGeom/tokens.h>
|
||||
#include <pxr/usd/usdUtils/dependencies.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
|
@ -41,21 +42,88 @@ struct ExportJobData {
|
|||
Depsgraph *depsgraph;
|
||||
wmWindowManager *wm;
|
||||
|
||||
char filepath[FILE_MAX];
|
||||
char unarchived_filepath[FILE_MAX]; /* unarchived_filepath is used for usda/usdc/usd export. */
|
||||
char usdz_filepath[FILE_MAX];
|
||||
USDExportParams params;
|
||||
|
||||
bool export_ok;
|
||||
timeit::TimePoint start_time;
|
||||
|
||||
bool targets_usdz() const
|
||||
{
|
||||
return usdz_filepath[0] != '\0';
|
||||
}
|
||||
|
||||
const char *export_filepath() const
|
||||
{
|
||||
if (targets_usdz()) {
|
||||
return usdz_filepath;
|
||||
}
|
||||
return unarchived_filepath;
|
||||
}
|
||||
};
|
||||
|
||||
static void report_job_duration(const ExportJobData *data)
|
||||
{
|
||||
timeit::Nanoseconds duration = timeit::Clock::now() - data->start_time;
|
||||
std::cout << "USD export of '" << data->filepath << "' took ";
|
||||
const char *export_filepath = data->export_filepath();
|
||||
std::cout << "USD export of '" << export_filepath << "' took ";
|
||||
timeit::print_duration(duration);
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* For usdz export, we must first create a usd/a/c file and then covert it to usdz. In Blender's
|
||||
* case, we first create a usdc file in Blender's temporary working directory, and store the path
|
||||
* to the usdc file in `unarchived_filepath`. This function then does the conversion of that usdc
|
||||
* file into usdz.
|
||||
*
|
||||
* \return true when the conversion from usdc to usdz is successful.
|
||||
*/
|
||||
static bool perform_usdz_conversion(const ExportJobData *data)
|
||||
{
|
||||
char usdc_temp_dir[FILE_MAX], usdc_file[FILE_MAX];
|
||||
BLI_split_dirfile(data->unarchived_filepath, usdc_temp_dir, usdc_file, FILE_MAX, FILE_MAX);
|
||||
|
||||
char usdz_file[FILE_MAX];
|
||||
BLI_split_file_part(data->usdz_filepath, usdz_file, FILE_MAX);
|
||||
|
||||
char original_working_dir_buff[FILE_MAX];
|
||||
char *original_working_dir = BLI_current_working_dir(original_working_dir_buff,
|
||||
sizeof(original_working_dir_buff));
|
||||
/* Buffer is expected to be returned by #BLI_current_working_dir, although in theory other
|
||||
* returns are possible on some platforms, this is not handled by this code. */
|
||||
BLI_assert(original_working_dir == original_working_dir_buff);
|
||||
|
||||
BLI_change_working_dir(usdc_temp_dir);
|
||||
|
||||
pxr::UsdUtilsCreateNewUsdzPackage(pxr::SdfAssetPath(usdc_file), usdz_file);
|
||||
BLI_change_working_dir(original_working_dir);
|
||||
|
||||
char usdz_temp_full_path[FILE_MAX];
|
||||
BLI_path_join(usdz_temp_full_path, FILE_MAX, usdc_temp_dir, usdz_file);
|
||||
|
||||
int result = 0;
|
||||
if (BLI_exists(data->usdz_filepath)) {
|
||||
result = BLI_delete(data->usdz_filepath, false, false);
|
||||
if (result != 0) {
|
||||
WM_reportf(
|
||||
RPT_ERROR, "USD Export: Unable to delete existing usdz file %s", data->usdz_filepath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
result = BLI_path_move(usdz_temp_full_path, data->usdz_filepath);
|
||||
if (result != 0) {
|
||||
WM_reportf(RPT_ERROR,
|
||||
"USD Export: Couldn't move new usdz file from temporary location %s to %s",
|
||||
usdz_temp_full_path,
|
||||
data->usdz_filepath);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void export_startjob(void *customdata,
|
||||
/* Cannot be const, this function implements wm_jobs_start_callback.
|
||||
* NOLINTNEXTLINE: readability-non-const-parameter. */
|
||||
|
@ -89,13 +157,14 @@ static void export_startjob(void *customdata,
|
|||
/* For restoring the current frame after exporting animation is done. */
|
||||
const int orig_frame = scene->r.cfra;
|
||||
|
||||
pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filepath);
|
||||
pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->unarchived_filepath);
|
||||
if (!usd_stage) {
|
||||
/* This happens when the USD JSON files cannot be found. When that happens,
|
||||
* the USD library doesn't know it has the functionality to write USDA and
|
||||
* USDC files, and creating a new UsdStage fails. */
|
||||
WM_reportf(
|
||||
RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filepath);
|
||||
WM_reportf(RPT_ERROR,
|
||||
"USD Export: unable to find suitable USD plugin to write %s",
|
||||
data->unarchived_filepath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -159,19 +228,51 @@ static void export_startjob(void *customdata,
|
|||
BKE_scene_graph_update_for_newframe(data->depsgraph);
|
||||
}
|
||||
|
||||
if (data->targets_usdz()) {
|
||||
bool usd_conversion_success = perform_usdz_conversion(data);
|
||||
if (!usd_conversion_success) {
|
||||
data->export_ok = false;
|
||||
*progress = 1.0f;
|
||||
*do_update = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data->export_ok = true;
|
||||
*progress = 1.0f;
|
||||
*do_update = true;
|
||||
}
|
||||
|
||||
static void export_endjob_usdz_cleanup(const ExportJobData *data)
|
||||
{
|
||||
if (!BLI_exists(data->unarchived_filepath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char dir[FILE_MAX];
|
||||
BLI_split_dir_part(data->unarchived_filepath, dir, FILE_MAX);
|
||||
|
||||
char usdc_temp_dir[FILE_MAX];
|
||||
BLI_path_join(usdc_temp_dir, FILE_MAX, BKE_tempdir_session(), "USDZ", SEP_STR);
|
||||
|
||||
BLI_assert_msg(BLI_strcasecmp(dir, usdc_temp_dir) == 0,
|
||||
"USD Export: Attempting to delete directory that doesn't match the expected "
|
||||
"temporary directory for usdz export.");
|
||||
BLI_delete(usdc_temp_dir, true, true);
|
||||
}
|
||||
|
||||
static void export_endjob(void *customdata)
|
||||
{
|
||||
ExportJobData *data = static_cast<ExportJobData *>(customdata);
|
||||
|
||||
DEG_graph_free(data->depsgraph);
|
||||
|
||||
if (!data->export_ok && BLI_exists(data->filepath)) {
|
||||
BLI_delete(data->filepath, false, false);
|
||||
if (data->targets_usdz()) {
|
||||
export_endjob_usdz_cleanup(data);
|
||||
}
|
||||
|
||||
if (!data->export_ok && BLI_exists(data->unarchived_filepath)) {
|
||||
BLI_delete(data->unarchived_filepath, false, false);
|
||||
}
|
||||
|
||||
G.is_rendering = false;
|
||||
|
@ -183,6 +284,36 @@ static void export_endjob(void *customdata)
|
|||
|
||||
} // namespace blender::io::usd
|
||||
|
||||
/* To create a usdz file, we must first create a .usd/a/c file and then covert it to .usdz. The
|
||||
* temporary files will be created in Blender's temporary session storage. The .usdz file will then
|
||||
* be moved to job->usdz_filepath. */
|
||||
static void create_temp_path_for_usdz_export(const char *filepath,
|
||||
blender::io::usd::ExportJobData *job)
|
||||
{
|
||||
char file[FILE_MAX];
|
||||
BLI_split_file_part(filepath, file, FILE_MAX);
|
||||
char *usdc_file = BLI_str_replaceN(file, ".usdz", ".usdc");
|
||||
|
||||
char usdc_temp_filepath[FILE_MAX];
|
||||
BLI_path_join(usdc_temp_filepath, FILE_MAX, BKE_tempdir_session(), "USDZ", usdc_file);
|
||||
|
||||
BLI_strncpy(job->unarchived_filepath, usdc_temp_filepath, strlen(usdc_temp_filepath) + 1);
|
||||
BLI_strncpy(job->usdz_filepath, filepath, strlen(filepath) + 1);
|
||||
|
||||
MEM_freeN(usdc_file);
|
||||
}
|
||||
|
||||
static void set_job_filepath(blender::io::usd::ExportJobData *job, const char *filepath)
|
||||
{
|
||||
if (BLI_path_extension_check_n(filepath, ".usdz", NULL)) {
|
||||
create_temp_path_for_usdz_export(filepath, job);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_strncpy(job->unarchived_filepath, filepath, sizeof(job->unarchived_filepath));
|
||||
job->usdz_filepath[0] = '\0';
|
||||
}
|
||||
|
||||
bool USD_export(bContext *C,
|
||||
const char *filepath,
|
||||
const USDExportParams *params,
|
||||
|
@ -197,7 +328,7 @@ bool USD_export(bContext *C,
|
|||
job->bmain = CTX_data_main(C);
|
||||
job->wm = CTX_wm_manager(C);
|
||||
job->export_ok = false;
|
||||
BLI_strncpy(job->filepath, filepath, sizeof(job->filepath));
|
||||
set_job_filepath(job, filepath);
|
||||
|
||||
job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode);
|
||||
job->params = *params;
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "testing/testing.h"
|
||||
#include "tests/blendfile_loading_base_test.h"
|
||||
|
||||
#include <pxr/usd/usd/prim.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLO_readfile.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "usd.h"
|
||||
#include "usd_tests_common.h"
|
||||
|
||||
namespace blender::io::usd {
|
||||
|
||||
const StringRefNull usdz_export_test_filename = "usd/usdz_export_test.blend";
|
||||
char temp_dir[FILE_MAX];
|
||||
char temp_output_dir[FILE_MAX];
|
||||
char output_filename[FILE_MAX];
|
||||
|
||||
class UsdUsdzExportTest : public BlendfileLoadingBaseTest {
|
||||
protected:
|
||||
struct bContext *context = nullptr;
|
||||
|
||||
public:
|
||||
bool load_file_and_depsgraph(const StringRefNull &filepath,
|
||||
const eEvaluationMode eval_mode = DAG_EVAL_VIEWPORT)
|
||||
{
|
||||
if (!blendfile_load(filepath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
depsgraph_create(eval_mode);
|
||||
|
||||
context = CTX_create();
|
||||
CTX_data_main_set(context, bfile->main);
|
||||
CTX_data_scene_set(context, bfile->curscene);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void SetUp() override
|
||||
{
|
||||
BlendfileLoadingBaseTest::SetUp();
|
||||
std::string usd_plugin_path = register_usd_plugins_for_tests();
|
||||
if (usd_plugin_path.empty()) {
|
||||
FAIL();
|
||||
}
|
||||
|
||||
BKE_tempdir_init(nullptr);
|
||||
const char *temp_base_dir = BKE_tempdir_base();
|
||||
|
||||
BLI_path_join(temp_dir, FILE_MAX, temp_base_dir, "usdz_test_temp_dir");
|
||||
BLI_dir_create_recursive(temp_dir);
|
||||
|
||||
BLI_path_join(temp_output_dir, FILE_MAX, temp_base_dir, "usdz_test_output_dir");
|
||||
BLI_dir_create_recursive(temp_output_dir);
|
||||
|
||||
BLI_path_join(output_filename, FILE_MAX, temp_output_dir, "output_новый.usdz");
|
||||
}
|
||||
|
||||
virtual void TearDown() override
|
||||
{
|
||||
BlendfileLoadingBaseTest::TearDown();
|
||||
CTX_free(context);
|
||||
context = nullptr;
|
||||
|
||||
BLI_delete(temp_dir, true, true);
|
||||
BLI_delete(temp_output_dir, true, true);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(UsdUsdzExportTest, usdz_export)
|
||||
{
|
||||
if (!load_file_and_depsgraph(usdz_export_test_filename)) {
|
||||
ADD_FAILURE();
|
||||
return;
|
||||
}
|
||||
|
||||
/* File sanity check. */
|
||||
ASSERT_EQ(BLI_listbase_count(&bfile->main->objects), 4)
|
||||
<< "Blender scene should have 4 objects.";
|
||||
|
||||
char original_cwd_buff[FILE_MAX];
|
||||
char *original_cwd = BLI_current_working_dir(original_cwd_buff, sizeof(original_cwd_buff));
|
||||
/* Buffer is expected to be returned by #BLI_current_working_dir, although in theory other
|
||||
* returns are possible on some platforms, this is not handled by this code. */
|
||||
ASSERT_EQ(original_cwd, original_cwd_buff)
|
||||
<< "BLI_current_working_dir is not expected to return a different value than the given char "
|
||||
"buffer.";
|
||||
|
||||
USDExportParams params{};
|
||||
|
||||
bool result = USD_export(context, output_filename, ¶ms, false);
|
||||
ASSERT_TRUE(result) << "usd export to " << output_filename << " failed.";
|
||||
|
||||
pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(output_filename);
|
||||
ASSERT_TRUE(bool(stage)) << "unable to open stage for the exported usdz file.";
|
||||
|
||||
std::string prim_name = pxr::TfMakeValidIdentifier("Cube");
|
||||
pxr::UsdPrim test_prim = stage->GetPrimAtPath(pxr::SdfPath("/Cube/" + prim_name));
|
||||
EXPECT_TRUE(bool(test_prim)) << "Cube prim should exist in exported usdz file.";
|
||||
|
||||
prim_name = pxr::TfMakeValidIdentifier("Cylinder");
|
||||
test_prim = stage->GetPrimAtPath(pxr::SdfPath("/Cylinder/" + prim_name));
|
||||
EXPECT_TRUE(bool(test_prim)) << "Cylinder prim should exist in exported usdz file.";
|
||||
|
||||
prim_name = pxr::TfMakeValidIdentifier("Icosphere");
|
||||
test_prim = stage->GetPrimAtPath(pxr::SdfPath("/Icosphere/" + prim_name));
|
||||
EXPECT_TRUE(bool(test_prim)) << "Icosphere prim should exist in exported usdz file.";
|
||||
|
||||
prim_name = pxr::TfMakeValidIdentifier("Sphere");
|
||||
test_prim = stage->GetPrimAtPath(pxr::SdfPath("/Sphere/" + prim_name));
|
||||
EXPECT_TRUE(bool(test_prim)) << "Sphere prim should exist in exported usdz file.";
|
||||
|
||||
char final_cwd_buff[FILE_MAX];
|
||||
char *final_cwd = BLI_current_working_dir(final_cwd_buff, sizeof(final_cwd_buff));
|
||||
/* Buffer is expected to be returned by #BLI_current_working_dir, although in theory other
|
||||
* returns are possible on some platforms, this is not handled by this code. */
|
||||
ASSERT_EQ(final_cwd, final_cwd_buff) << "BLI_current_working_dir is not expected to return "
|
||||
"a different value than the given char buffer.";
|
||||
EXPECT_TRUE(STREQ(original_cwd, final_cwd))
|
||||
<< "Final CWD should be the same as the original one.";
|
||||
}
|
||||
|
||||
} // namespace blender::io::usd
|
Loading…
Reference in New Issue