Skip to content

Commit

Permalink
Release 1.0.0-alpha.38; Add --image-path-mode option (#71)
Browse files Browse the repository at this point in the history
* Compatible with FBX SDK 2022

* Add --image-path-mode option

* Revert "Release 1.0.0-alpha.37; Optimize for unknown path textures (#70)"

This reverts commit b10e587.

Co-authored-by: AFeiYA <[email protected]>
  • Loading branch information
shrinktofit and AFeiYA authored Aug 22, 2022
1 parent b10e587 commit a8ad7fc
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 81 deletions.
1 change: 0 additions & 1 deletion Cli/Cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ int main(int argc_, const char *argv_[]) {
MyWriter writer{cliOptions->inputFile, cliOptions->outFile};
cliOptions->convertOptions.useDataUriForBuffers = false;
cliOptions->convertOptions.writer = &writer;
cliOptions->convertOptions.pathMode = bee::ConvertOptions::PathMode::copy;

std::unique_ptr<bee::Logger> logger;
if (cliOptions->logFile) {
Expand Down
42 changes: 42 additions & 0 deletions Cli/ReadCliArgs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,21 @@ std::optional<CliArgs> readCliArgs(std::span<std::string_view> args_) {
"console",
cxxopts::value<std::string>());

options.add_options()(
"image-path-mode",
"Specify the mode used to specify the image path. Could "
"be one of the following:\n"
"- absolute - Output the absolute path to the path.\n"
"- relative - Output the relative path to the path.\n"
"- prefer-relative - If the image is under the the same or sub directory "
"of "
"glTF file, output as relative. Otherwise output as absolute.\n"
"- strip - Ingore all path information, only output the file name.\n"
"- embedded - Embedded the image into Data URI.\n"
"- copy - Copy the image to the output directory and reference it with a "
"relative path.\n",
cxxopts::value<std::string>());

options.parse_positional("input-file");

std::vector<std::string> argStrings(args_.size());
Expand Down Expand Up @@ -215,6 +230,33 @@ std::optional<CliArgs> readCliArgs(std::span<std::string_view> args_) {
cliParseResult["export-raw-materials"].as<bool>();
}

cliArgs.convertOptions.pathMode = bee::ConvertOptions::PathMode::copy;
if (cliParseResult.count("image-path-mode")) {
const auto pathModeString =
cliParseResult["image-path-mode"].as<std::string>();
if (pathModeString == "relative") {
cliArgs.convertOptions.pathMode =
bee::ConvertOptions::PathMode::relative;
} else if (pathModeString == "absolute") {
cliArgs.convertOptions.pathMode =
bee::ConvertOptions::PathMode::absolute;
} else if (pathModeString == "prefer-relative") {
cliArgs.convertOptions.pathMode =
bee::ConvertOptions::PathMode::prefer_relative;
} else if (pathModeString == "strip") {
cliArgs.convertOptions.pathMode = bee::ConvertOptions::PathMode::strip;
} else if (pathModeString == "embedded") {
cliArgs.convertOptions.pathMode =
bee::ConvertOptions::PathMode::embedded;
} else if (pathModeString == "copy") {
cliArgs.convertOptions.pathMode = bee::ConvertOptions::PathMode::copy;
} else {
std::cerr << "Bad --image-path-mode \"" << pathModeString << "\"\n";
std::cout << options.help() << std::endl;
return {};
}
}

if (cliParseResult.count("verbose")) {
cliArgs.convertOptions.verbose = cliParseResult["verbose"].as<bool>();
}
Expand Down
9 changes: 7 additions & 2 deletions Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,13 @@ message (STATUS "FBX SDK configuration: ${FbxSdkConfiguration}")
message (STATUS "CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}")
set (FbxSdkIncludeDirectories "${FbxSdkHome}/include")
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set (FbxSdkLibraries "${FbxSdkHome}/lib/vs2017/x64/${FbxSdkConfiguration}/libfbxsdk.lib")
set (FbxSdkDynLibraries "${FbxSdkHome}/lib/vs2017/x64/${FbxSdkConfiguration}/libfbxsdk.dll")
if (EXISTS "${FbxSdkHome}/lib/vs2019")
set (FbxSdkLibraries "${FbxSdkHome}/lib/vs2019/x64/${FbxSdkConfiguration}/libfbxsdk.lib")
set (FbxSdkDynLibraries "${FbxSdkHome}/lib/vs2019/x64/${FbxSdkConfiguration}/libfbxsdk.dll")
else ()
set (FbxSdkLibraries "${FbxSdkHome}/lib/vs2017/x64/${FbxSdkConfiguration}/libfbxsdk.lib")
set (FbxSdkDynLibraries "${FbxSdkHome}/lib/vs2017/x64/${FbxSdkConfiguration}/libfbxsdk.dll")
endif ()
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
set (FbxSdkLibraries "${FbxSdkHome}/lib/clang/${FbxSdkConfiguration}/libfbxsdk.a")
set (FbxSdkDynLibraries "${FbxSdkHome}/lib/clang/${FbxSdkConfiguration}/libfbxsdk.dylib")
Expand Down
57 changes: 54 additions & 3 deletions Core/Source/bee/Convert/SceneConverter.Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,47 @@ glm_fbx_mat4 compute_fbx_texture_transform(glm_fbx_vec3 pivot_center_,
(scalePivotTransform * scale * invScalePivotTransform) *
invPivotTransform;
};

std::u8string encode_url_component(std::u8string_view input) {
// http://www.zedwood.com/article/cpp-urlencode-function
static const char8_t lookup[] = u8"0123456789abcdef";
std::basic_ostringstream<char8_t> ss;
std::regex r("[!'\\(\\)*-.0-9A-Za-z_~]");
for (const auto &c : input) {
if ((u8'0' <= c && c <= u8'9') || // 0-9
(u8'a' <= c && c <= u8'z') || // a-z
(u8'A' <= c && c <= u8'Z') || // A-Z
(c == '-' || c == '_' || c == '.' || c == '~')) {
ss << c;
} else {
ss << '%';
ss << lookup[(c & 0xF0) >> 4];
ss << lookup[(c & 0x0F)];
}
}
return ss.str();
}

std::u8string path_to_file_url(const bee::filesystem::path &path_) {
const auto normalized = path_.lexically_normal();
if (normalized.empty()) {
return {};
}
std::basic_ostringstream<char8_t> ss;
ss << u8"file://";
auto p = normalized.begin();
if (p != normalized.end() && normalized.has_root_name()) {
ss << u8"/" << p->u8string();
++p;
}
if (p != normalized.end() && normalized.has_root_directory()) {
++p;
}
for (; p != normalized.end(); ++p) {
ss << u8"/" << encode_url_component(p->u8string());
}
return ss.str();
}
} // namespace

namespace bee {
Expand Down Expand Up @@ -380,12 +421,19 @@ SceneConverter::_processPath(const bee::filesystem::path &path_) {

const auto toRelative = [getOutDirNormalized](const fs::path &to_) {
const auto outDir = getOutDirNormalized();
return to_.lexically_relative(outDir).generic_u8string();
const auto rel = to_.lexically_relative(outDir).generic_u8string();
if (!rel.empty()) {
return rel;
} else { // Could not be referenced using relative path
return path_to_file_url(to_); // Fallback to absolute
}
};

const auto toAbsUrl = [&]() { return path_to_file_url(normalizedPath); };

switch (_options.pathMode) {
case ConvertOptions::PathMode::absolute: {
return normalizedPath.u8string();
return toAbsUrl();
}
case ConvertOptions::PathMode::relative: {
return toRelative(normalizedPath);
Expand Down Expand Up @@ -433,6 +481,9 @@ SceneConverter::_processPath(const bee::filesystem::path &path_) {
const auto relativePath =
normalizedPath.lexically_relative(getOutDirNormalized());
auto relativePathStr = relativePath.u8string();
if (relativePathStr.empty()) {
return toAbsUrl();
}
const auto dotdot = std::string_view{".."};
const auto startsWithDotDot = [](const fs::path &path_) {
for (const auto part : path_) {
Expand All @@ -446,7 +497,7 @@ SceneConverter::_processPath(const bee::filesystem::path &path_) {
if (relativePath.is_relative() && !startsWithDotDot(relativePath)) {
return relativePathStr;
} else {
return normalizedPath.u8string();
return toAbsUrl();
}
break;
}
Expand Down
92 changes: 19 additions & 73 deletions Core/Source/bee/Convert/SceneConverter.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#include "./fbxsdk/String.h"
#include <bee/Convert/ConvertError.h>
#include <bee/Convert/SceneConverter.h>
Expand All @@ -20,7 +21,7 @@ class UnsupportedInheritTypeError
};

UnsupportedInheritTypeError(Type type_, std::string_view node_)
: NodeError(node_), _type(type_) {
: _type(type_), NodeError(node_) {
}

Type type() const {
Expand All @@ -47,10 +48,9 @@ SceneConverter::SceneConverter(fbxsdk::FbxManager &fbx_manager_,
const ConvertOptions &options_,
std::u8string_view fbx_file_name_,
GLTFBuilder &glTF_builder_)
: _glTFBuilder(glTF_builder_), _fbxManager(fbx_manager_),
_fbxGeometryConverter(&fbx_manager_), _fbxScene(fbx_scene_),
_options(options_),
_fbxFileName(fbx_file_name_) {
: _glTFBuilder(glTF_builder_), _fbxManager(fbx_manager_),
_fbxScene(fbx_scene_), _options(options_), _fbxFileName(fbx_file_name_),
_fbxGeometryConverter(&fbx_manager_) {
const auto &globalSettings = fbx_scene_.GetGlobalSettings();
if (!options_.animationBakeRate) {
_animationTimeMode = globalSettings.GetTimeMode();
Expand All @@ -61,7 +61,7 @@ SceneConverter::SceneConverter(fbxsdk::FbxManager &fbx_manager_,

const auto frameRate = fbxsdk::FbxTime::GetFrameRate(_animationTimeMode);
_log(Logger::Level::verbose, fmt::format("Frame rate: {}", frameRate));
_fixUnknownPathTextures();

auto &documentExtras = glTF_builder_.document().extensionsAndExtras;
documentExtras["extras"]["FBX-glTF-conv"]["animationFrameRate"] = frameRate;
}
Expand All @@ -76,76 +76,23 @@ void SceneConverter::convert() {
_convertAnimation(_fbxScene);
}


void to_json(Json &j_, Logger::Level level_) {
j_ = level_;
void to_json(Json &j_, bee::Logger::Level level_) {
j_ = static_cast<std::underlying_type_t<decltype(level_)>>(level_);
}

void SceneConverter::_log(Logger::Level level_,
void SceneConverter::_log(bee::Logger::Level level_,
std::u8string_view message_) {
if (_options.logger) {
(*_options.logger)(level_, message_);
}
}

void SceneConverter::_log(Logger::Level level_, Json &&message_) {
void SceneConverter::_log(bee::Logger::Level level_, Json &&message_) {
if (_options.logger) {
(*_options.logger)(level_, std::move(message_));
}
}

void SceneConverter::_fixUnknownPathTextures() {
// Fetch all textures in scene.
fbxsdk::FbxArray<fbxsdk::FbxTexture *> textures;
_fbxScene.FillTextureArray(textures);

// Partition all textures so that:
// [
// known-path-file-textures
// ...
// unknown-path-file-textures
// ...
// non-file-textures
// ]
const auto pTextures = textures.GetArray();
const auto pTextureEnd = pTextures + textures.GetCount();
const auto pFileTexturesEnd =
std::partition(pTextures, pTextureEnd, [](fbxsdk::FbxTexture *tex_) {
return fbxsdk::FbxCast<fbxsdk::FbxFileTexture>(tex_);
});
const auto pUnknownPathTextureBegin =
std::partition(pTextures, pFileTexturesEnd, [](fbxsdk::FbxTexture *tex_) {
const auto fileTexture = fbxsdk::FbxCast<fbxsdk::FbxFileTexture>(tex_);
return std::strlen(fileTexture->GetFileName()) != 0;
});

const auto pKnownPathTextureBegin = pTextures;
const auto pKnownPathTextureEnd = pUnknownPathTextureBegin;
const auto pUnknownPathTextureEnd = pFileTexturesEnd;

// For each unknown-path file texture, find if there's a also-named known-path
// file texture. If the one exists, copy the path.
for (auto pKnownPathTexture = pUnknownPathTextureBegin;
pKnownPathTexture != pUnknownPathTextureEnd; ++pKnownPathTexture) {
const auto knownPathTexture = *pKnownPathTexture;
const auto sameNameButKnownPath = std::find_if(
pKnownPathTextureBegin, pKnownPathTextureEnd,
[knownPathTexture](fbxsdk::FbxTexture *tex_) {
return 0 ==
std::strcmp(
fbxsdk::FbxCast<fbxsdk::FbxFileTexture>(tex_)->GetName(),
fbxsdk::FbxCast<fbxsdk::FbxFileTexture>(knownPathTexture)
->GetName());
});
if (sameNameButKnownPath != pKnownPathTextureEnd) {
const auto a = fbxsdk::FbxCast<fbxsdk::FbxFileTexture>(knownPathTexture);
const auto b =
fbxsdk::FbxCast<fbxsdk::FbxFileTexture>(*sameNameButKnownPath);
a->SetFileName(b->GetFileName());
a->SetRelativeFileName(b->GetRelativeFileName());
}
}
}
fbxsdk::FbxGeometryConverter &SceneConverter::_getGeometryConverter() {
return _fbxGeometryConverter;
}
Expand All @@ -156,8 +103,8 @@ void SceneConverter::_prepareScene() {

// Convert system unit
if (const auto fbxFileSystemUnit =
_fbxScene.GetGlobalSettings().GetSystemUnit();
fbxFileSystemUnit != fbxsdk::FbxSystemUnit::m) {
_fbxScene.GetGlobalSettings().GetSystemUnit();
fbxFileSystemUnit != fbxsdk::FbxSystemUnit::m) {
// FBX SDK's `convertScene` is not perfectly.
// The problem occurs when bind a FBX model under bones in another FBX
// model. See: https://github.com/facebookincubator/FBX2glTF/issues/29
Expand Down Expand Up @@ -226,7 +173,7 @@ std::string SceneConverter::_convertName(const char *fbx_name_) {
return fbx_name_;
}

filesystem::path
bee::filesystem::path
SceneConverter::_convertFileName(const char *fbx_file_name_) {
std::u8string u8name{reinterpret_cast<const char8_t *>(fbx_file_name_)};
// Some FBX files contain non-UTF8 encoded file names and will cause
Expand All @@ -249,7 +196,7 @@ SceneConverter::_convertScene(fbxsdk::FbxScene &fbx_scene_) {
auto &extensionsAndExtras =
_glTFBuilder.get(&fx::gltf::Document::extensionsAndExtras);
auto &sceneInfo = extensionsAndExtras["extras"]["FBX-glTF-conv"]
["fbxFileHeaderInfo"]["sceneInfo"];
["fbxFileHeaderInfo"]["sceneInfo"];

sceneInfo["url"] = fbx_string_to_utf8_checked(fbxSceneInfo.Url.Get());

Expand Down Expand Up @@ -320,17 +267,17 @@ void SceneConverter::_convertNode(fbxsdk::FbxNode &fbx_node_) {
}

if (auto fbxLocalTransform = fbx_node_.EvaluateLocalTransform();
!fbxLocalTransform.IsIdentity()) {
!fbxLocalTransform.IsIdentity()) {
if (const auto fbxT = fbxLocalTransform.GetT(); !fbxT.IsZero(3)) {
FbxVec3Spreader::spread(_applyUnitScaleFactorV3(fbxT),
glTFNode.translation.data());
}
if (const auto fbxR = fbxLocalTransform.GetQ();
fbxR.Compare(fbxsdk::FbxQuaternion{})) {
fbxR.Compare(fbxsdk::FbxQuaternion{})) {
FbxQuatSpreader::spread(fbxR, glTFNode.rotation.data());
}
if (const auto fbxS = fbxLocalTransform.GetS();
fbxS[0] != 1. || fbxS[1] != 1. || fbxS[2] != 1.) {
fbxS[0] != 1. || fbxS[1] != 1. || fbxS[2] != 1.) {
FbxVec3Spreader::spread(fbxS, glTFNode.scale.data());
}
}
Expand Down Expand Up @@ -395,8 +342,7 @@ void SceneConverter::_convertNode(fbxsdk::FbxNode &fbx_node_) {
{"a", limbColor.mBlue},
{"a", limbColor.mAlpha}};
}
}
break;
} break;
default:
// TODO:
// if (_options.verbose) {
Expand Down Expand Up @@ -425,4 +371,4 @@ void SceneConverter::_convertNode(fbxsdk::FbxNode &fbx_node_) {
std::string SceneConverter::_getName(fbxsdk::FbxNode &fbx_node_) {
return fbx_node_.GetName();
}
} // namespace bee
} // namespace bee
2 changes: 0 additions & 2 deletions Core/Source/bee/Convert/SceneConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,6 @@ class SceneConverter {

void _log(bee::Logger::Level level_, Json &&message_);

void _fixUnknownPathTextures();

fbxsdk::FbxGeometryConverter &_getGeometryConverter();

void _prepareScene();
Expand Down

0 comments on commit a8ad7fc

Please sign in to comment.