Fix T49112: Alembic unicode path issues on Windows.

Now we pass streams to Alembic instead of passing the filename string.
That way we can open the stream ourselves with the proper unicode
encoding.

Note that this only applies to Ogawa archive, as HDF5 does not support
streams.

Differential Revision: https://developer.blender.org/D2160
This commit is contained in:
Kévin Dietrich 2016-08-25 05:19:41 +02:00
parent bfc5a94292
commit 3c533be77e
Notes: blender-bot 2023-02-14 07:40:55 +01:00
Referenced by issue #49112, The Alembic don't support Chinese path
4 changed files with 160 additions and 53 deletions

View File

@ -33,6 +33,7 @@ set(INC
../makesrna
../windowmanager
../../../intern/guardedalloc
../../../intern/utfconv
)
set(INC_SYS

View File

@ -30,6 +30,10 @@
#include <Alembic/AbcCoreOgawa/All.h>
#ifdef WIN32
# include "utfconv.h"
#endif
#include "abc_camera.h"
#include "abc_curves.h"
#include "abc_hair.h"
@ -67,6 +71,54 @@ extern "C" {
using Alembic::Abc::TimeSamplingPtr;
using Alembic::Abc::OBox3dProperty;
/* ************************************************************************** */
/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to
* have a version supporting streams. */
static Alembic::Abc::OArchive create_archive(std::ostream *ostream,
const std::string &filename,
const std::string &scene_name,
const Alembic::Abc::Argument &arg0,
const Alembic::Abc::Argument &arg1,
bool ogawa)
{
Alembic::Abc::MetaData md = GetMetaData(arg0, arg1);
md.set(Alembic::Abc::kApplicationNameKey, "Blender");
md.set(Alembic::Abc::kUserDescriptionKey, scene_name);
time_t raw_time;
time(&raw_time);
char buffer[128];
#if defined _WIN32 || defined _WIN64
ctime_s(buffer, 128, &raw_time);
#else
ctime_r(&raw_time, buffer);
#endif
const std::size_t buffer_len = strlen(buffer);
if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
buffer[buffer_len - 1] = '\0';
}
md.set(Alembic::Abc::kDateWrittenKey, buffer);
Alembic::Abc::ErrorHandler::Policy policy = GetErrorHandlerPolicyFromArgs(arg0, arg1);
#ifdef WITH_ALEMBIC_HDF5
if (!ogawa) {
return Alembic::Abc::OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, md, policy);
}
#else
static_cast<void>(filename);
static_cast<void>(ogawa);
#endif
Alembic::AbcCoreOgawa::WriteArchive archive_writer;
return Alembic::Abc::OArchive(archive_writer(ostream, md), Alembic::Abc::kWrapExisting, policy);
}
/* ************************************************************************** */
ExportSettings::ExportSettings()
@ -247,26 +299,25 @@ void AbcExporter::operator()(Main *bmain, float &progress, bool &was_canceled)
Alembic::Abc::Argument arg(md);
#ifdef WITH_ALEMBIC_HDF5
if (!m_settings.export_ogawa) {
m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreHDF5::WriteArchive(),
m_filename,
"Blender",
scene_name,
Alembic::Abc::ErrorHandler::kThrowPolicy,
arg);
}
else
/* Use stream to support unicode character paths on Windows. */
if (m_settings.export_ogawa) {
#ifdef WIN32
UTF16_ENCODE(m_filename);
std::wstring wstr(m_filename_16);
m_out_file.open(wstr.c_str(), std::ios::out | std::ios::binary);
UTF16_UN_ENCODE(m_filename);
#else
m_out_file.open(m_filename, std::ios::out | std::ios::binary);
#endif
{
m_archive = Alembic::Abc::CreateArchiveWithInfo(Alembic::AbcCoreOgawa::WriteArchive(),
m_filename,
"Blender",
scene_name,
Alembic::Abc::ErrorHandler::kThrowPolicy,
arg);
}
m_archive = create_archive(&m_out_file,
m_filename,
scene_name,
Alembic::Abc::ErrorHandler::kThrowPolicy,
arg,
m_settings.export_ogawa);
/* Create time samplings for transforms and shapes. */
TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_step_xform);

View File

@ -24,6 +24,7 @@
#define __ABC_EXPORTER_H__
#include <Alembic/Abc/All.h>
#include <fstream>
#include <map>
#include <set>
#include <vector>
@ -75,6 +76,7 @@ class AbcExporter {
const char *m_filename;
std::ofstream m_out_file;
Alembic::Abc::OArchive m_archive;
unsigned int m_trans_sampling_index, m_shape_sampling_index;

View File

@ -29,6 +29,12 @@
#include <Alembic/AbcCoreOgawa/All.h>
#include <Alembic/AbcMaterial/IMaterial.h>
#include <fstream>
#ifdef WIN32
# include "utfconv.h"
#endif
#include "abc_camera.h"
#include "abc_curves.h"
#include "abc_hair.h"
@ -109,49 +115,97 @@ using Alembic::AbcGeom::V3fArraySamplePtr;
using Alembic::AbcMaterial::IMaterial;
struct AbcArchiveHandle {
int unused;
};
ABC_INLINE IArchive *archive_from_handle(AbcArchiveHandle *handle)
static IArchive open_archive(const std::string &filename,
const std::vector<std::istream *> &input_streams,
bool &is_hdf5)
{
return reinterpret_cast<IArchive *>(handle);
}
ABC_INLINE AbcArchiveHandle *handle_from_archive(IArchive *archive)
{
return reinterpret_cast<AbcArchiveHandle *>(archive);
}
static IArchive *open_archive(const std::string &filename)
{
Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
IArchive *archive;
try {
archive = new IArchive(Alembic::AbcCoreOgawa::ReadArchive(),
filename.c_str(), ErrorHandler::kThrowPolicy,
cache_ptr);
is_hdf5 = false;
Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams);
return IArchive(archive_reader(filename),
kWrapExisting,
ErrorHandler::kThrowPolicy);
}
catch (const Exception &e) {
std::cerr << e.what() << '\n';
#ifdef WITH_ALEMBIC_HDF5
try {
archive = new IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
filename.c_str(), ErrorHandler::kThrowPolicy,
cache_ptr);
is_hdf5 = true;
Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr;
return IArchive(Alembic::AbcCoreHDF5::ReadArchive(),
filename.c_str(), ErrorHandler::kThrowPolicy,
cache_ptr);
}
catch (const Exception &) {
std::cerr << e.what() << '\n';
return NULL;
return IArchive();
}
#else
return NULL;
return IArchive();
#endif
}
return archive;
return IArchive();
}
/* Wrapper around an archive to be able to use streams so that unicode paths
* work on Windows (T49112), and to make sure the input stream remains valid as
* long as the archive is open. */
class ArchiveWrapper {
IArchive m_archive;
std::ifstream m_infile;
std::vector<std::istream *> m_streams;
public:
explicit ArchiveWrapper(const char *filename)
{
#ifdef WIN32
UTF16_ENCODE(filename);
std::wstring wstr(filename_16);
m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary);
UTF16_UN_ENCODE(filename);
#else
m_infile.open(filename, std::ios::in | std::ios::binary);
#endif
m_streams.push_back(&m_infile);
bool is_hdf5;
m_archive = open_archive(filename, m_streams, is_hdf5);
/* We can't open an HDF5 file from a stream, so close it. */
if (is_hdf5) {
m_infile.close();
m_streams.clear();
}
}
bool valid() const
{
return m_archive.valid();
}
IObject getTop()
{
return m_archive.getTop();
}
};
struct AbcArchiveHandle {
int unused;
};
ABC_INLINE ArchiveWrapper *archive_from_handle(AbcArchiveHandle *handle)
{
return reinterpret_cast<ArchiveWrapper *>(handle);
}
ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveWrapper *archive)
{
return reinterpret_cast<AbcArchiveHandle *>(archive);
}
//#define USE_NURBS
@ -247,9 +301,10 @@ static void gather_objects_paths(const IObject &object, ListBase *object_paths)
AbcArchiveHandle *ABC_create_handle(const char *filename, ListBase *object_paths)
{
IArchive *archive = open_archive(filename);
ArchiveWrapper *archive = new ArchiveWrapper(filename);
if (!archive) {
if (!archive->valid()) {
delete archive;
return NULL;
}
@ -582,12 +637,10 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa
data->do_update = do_update;
data->progress = progress;
IArchive *archive = open_archive(data->filename);
ArchiveWrapper *archive = new ArchiveWrapper(data->filename);
if (!archive || !archive->valid()) {
if (archive) {
delete archive;
}
if (!archive->valid()) {
delete archive;
data->error_code = ABC_ARCHIVE_FAIL;
return;
}
@ -812,7 +865,7 @@ void ABC_import(bContext *C, const char *filepath, float scale, bool is_sequence
void ABC_get_transform(AbcArchiveHandle *handle, Object *ob, const char *object_path, float r_mat[4][4], float time, float scale)
{
IArchive *archive = archive_from_handle(handle);
ArchiveWrapper *archive = archive_from_handle(handle);
if (!archive || !archive->valid()) {
return;
@ -1088,7 +1141,7 @@ DerivedMesh *ABC_read_mesh(AbcArchiveHandle *handle,
const char **err_str,
int read_flag)
{
IArchive *archive = archive_from_handle(handle);
ArchiveWrapper *archive = archive_from_handle(handle);
if (!archive || !archive->valid()) {
*err_str = "Invalid archive!";