Move to C++17 and std::filesystem.

With this, we are able to get rid of all the increasingly broken file
system utility code, and trust std::filesystem to handle all the cross-
platform complexity.

Unfortunately std::filesystem support remains a little elusive; it is
well supported in Visual Studio (especially 2019), but not by default
in Mac OS X, and even in GCC 8.0 it requires an explicit '-l c++fs'.

This also silences some of the more egregious compiler warnings, mostly
by being explicit about where we cast to the uint32 types glTF prefers.
This commit is contained in:
Par Winzell 2019-04-01 17:05:16 -07:00 committed by Pär Winzell
parent 769454e964
commit 95063ba9f1
20 changed files with 200 additions and 341 deletions

View File

@ -7,7 +7,7 @@ if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
"Hint: mkdir -p build; cmake -H. -Bbuild; make -Cbuild\n") "Hint: mkdir -p build; cmake -H. -Bbuild; make -Cbuild\n")
endif () endif ()
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 17)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
include(ExternalProject) include(ExternalProject)
@ -35,6 +35,13 @@ find_package(Iconv QUIET)
# create a compilation database for e.g. clang-tidy # create a compilation database for e.g. clang-tidy
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if (WIN32)
add_compile_definitions(
_CRT_SECURE_NO_WARNINGS
_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
)
endif()
if (WIN32) if (WIN32)
# this will suffice for now; don't really care about 32-bit # this will suffice for now; don't really care about 32-bit
set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR}) set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR})
@ -190,7 +197,6 @@ set(LIB_SOURCE_FILES
src/utils/File_Utils.hpp src/utils/File_Utils.hpp
src/utils/Image_Utils.cpp src/utils/Image_Utils.cpp
src/utils/Image_Utils.hpp src/utils/Image_Utils.hpp
src/utils/String_Utils.cpp
src/utils/String_Utils.hpp src/utils/String_Utils.hpp
third_party/CLI11/CLI11.hpp third_party/CLI11/CLI11.hpp
) )
@ -237,6 +243,12 @@ if (WIN32)
optimized ${ZLIB_LIBRARIES} optimized ${ZLIB_LIBRARIES}
debug ${ZLIB_LIBRARIES_DEBUG} debug ${ZLIB_LIBRARIES_DEBUG}
) )
# quiet warnings related to fopen, sscanf
target_compile_definitions(libFBX2glTF PRIVATE
_CRT_SECURE_NO_WARNINGS
_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
)
else() else()
target_link_libraries(libFBX2glTF target_link_libraries(libFBX2glTF
${LIBXML2_LIBRARIES} ${LIBXML2_LIBRARIES}

View File

@ -13,13 +13,6 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#if defined(__unix__) || defined(__APPLE__)
#include <sys/stat.h>
#define _stricmp strcasecmp
#endif
#include <CLI11.hpp> #include <CLI11.hpp>
#include "FBX2glTF.h" #include "FBX2glTF.h"
@ -271,10 +264,7 @@ int main(int argc, char* argv[]) {
if (outputPath.empty()) { if (outputPath.empty()) {
// if -o is not given, default to the basename of the .fbx // if -o is not given, default to the basename of the .fbx
outputPath = fmt::format( outputPath = "./" + FileUtils::GetFileBaseString(inputPath);
".{}{}",
(const char)StringUtils::GetPathSeparator(),
StringUtils::GetFileBaseString(inputPath));
} }
// the output folder in .gltf mode, not used for .glb // the output folder in .gltf mode, not used for .glb
std::string outputFolder; std::string outputFolder;
@ -282,14 +272,17 @@ int main(int argc, char* argv[]) {
// the path of the actual .glb or .gltf file // the path of the actual .glb or .gltf file
std::string modelPath; std::string modelPath;
if (gltfOptions.outputBinary) { if (gltfOptions.outputBinary) {
// in binary mode, we write precisely where we're asked const auto& suffix = FileUtils::GetFileSuffix(outputPath);
modelPath = outputPath + ".glb"; // add .glb to output path, unless it already ends in exactly that
if (suffix.has_value() && suffix.value() == "glb") {
modelPath = outputPath;
} else {
modelPath = outputPath + ".glb";
}
} else { } else {
// in gltf mode, we create a folder and write into that // in gltf mode, we create a folder and write into that
outputFolder = outputFolder = fmt::format("{}_out/", outputPath.c_str());
fmt::format("{}_out{}", outputPath.c_str(), (const char)StringUtils::GetPathSeparator()); modelPath = outputFolder + FileUtils::GetFileNameString(outputPath) + ".gltf";
modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf";
} }
if (!FileUtils::CreatePath(modelPath.c_str())) { if (!FileUtils::CreatePath(modelPath.c_str())) {
fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str()); fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str());
@ -302,7 +295,7 @@ int main(int argc, char* argv[]) {
if (verboseOutput) { if (verboseOutput) {
fmt::printf("Loading FBX File: %s\n", inputPath); fmt::printf("Loading FBX File: %s\n", inputPath);
} }
if (!LoadFBXFile(raw, inputPath.c_str(), "png;jpg;jpeg")) { if (!LoadFBXFile(raw, inputPath, {"png", "jpg", "jpeg"})) {
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath); fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
return 1; return 1;
} }

View File

@ -40,6 +40,15 @@ using json = nlohmann::basic_json<workaround_fifo_map>;
extern bool verboseOutput; extern bool verboseOutput;
/**
* Centralises all the laborious downcasting from your OS' 64-bit
* index variables down to the uint32s that glTF is built out of.
*/
inline uint32_t to_uint32(size_t n) {
assert(n < UINT_MAX);
return static_cast<uint32_t>(n);
}
/** /**
* The variuos situations in which the user may wish for us to (re-)compute normals for our vertices. * The variuos situations in which the user may wish for us to (re-)compute normals for our vertices.
*/ */

View File

@ -925,7 +925,7 @@ static std::string GetInferredFileName(
} }
// Get the file name with file extension. // Get the file name with file extension.
const std::string fileName = const std::string fileName =
StringUtils::GetFileNameString(StringUtils::GetCleanPathString(fbxFileName)); FileUtils::GetFileNameString(FileUtils::GetCanonicalPath(fbxFileName));
// Try to find a match with extension. // Try to find a match with extension.
for (const auto& file : directoryFileList) { for (const auto& file : directoryFileList) {
@ -935,12 +935,12 @@ static std::string GetInferredFileName(
} }
// Get the file name without file extension. // Get the file name without file extension.
const std::string fileBase = StringUtils::GetFileBaseString(fileName); const std::string fileBase = FileUtils::GetFileBaseString(fileName);
// Try to find a match without file extension. // Try to find a match without file extension.
for (const auto& file : directoryFileList) { for (const auto& file : directoryFileList) {
// If the two extension-less base names match. // If the two extension-less base names match.
if (StringUtils::CompareNoCase(fileBase, StringUtils::GetFileBaseString(file)) == 0) { if (StringUtils::CompareNoCase(fileBase, FileUtils::GetFileBaseString(file)) == 0) {
// Return the name with extension of the file in the directory. // Return the name with extension of the file in the directory.
return std::string(directory) + file; return std::string(directory) + file;
} }
@ -960,14 +960,14 @@ static std::string GetInferredFileName(
*/ */
static void FindFbxTextures( static void FindFbxTextures(
FbxScene* pScene, FbxScene* pScene,
const char* fbxFileName, const std::string fbxFileName,
const char* extensions, const std::set<std::string>& extensions,
std::map<const FbxTexture*, FbxString>& textureLocations) { std::map<const FbxTexture*, FbxString>& textureLocations) {
// Get the folder the FBX file is in. // Get the folder the FBX file is in.
const std::string folder = StringUtils::GetFolderString(fbxFileName); const std::string folder = FileUtils::GetFolderString(fbxFileName);
// Check if there is a filename.fbm folder to which embedded textures were extracted. // Check if there is a filename.fbm folder to which embedded textures were extracted.
const std::string fbmFolderName = folder + StringUtils::GetFileBaseString(fbxFileName) + ".fbm/"; const std::string fbmFolderName = folder + FileUtils::GetFileBaseString(fbxFileName) + ".fbm/";
// Search either in the folder with embedded textures or in the same folder as the FBX file. // Search either in the folder with embedded textures or in the same folder as the FBX file.
const std::string searchFolder = FileUtils::FolderExists(fbmFolderName) ? fbmFolderName : folder; const std::string searchFolder = FileUtils::FolderExists(fbmFolderName) ? fbmFolderName : folder;
@ -996,14 +996,17 @@ static void FindFbxTextures(
} }
} }
bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions) { bool LoadFBXFile(
RawModel& raw,
const std::string fbxFileName,
const std::set<std::string>& textureExtensions) {
FbxManager* pManager = FbxManager::Create(); FbxManager* pManager = FbxManager::Create();
FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT); FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT);
pManager->SetIOSettings(pIoSettings); pManager->SetIOSettings(pIoSettings);
FbxImporter* pImporter = FbxImporter::Create(pManager, ""); FbxImporter* pImporter = FbxImporter::Create(pManager, "");
if (!pImporter->Initialize(fbxFileName, -1, pManager->GetIOSettings())) { if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) {
if (verboseOutput) { if (verboseOutput) {
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString()); fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
} }

View File

@ -11,6 +11,9 @@
#include "raw/RawModel.hpp" #include "raw/RawModel.hpp"
bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions); bool LoadFBXFile(
RawModel& raw,
const std::string fbxFileName,
const std::set<std::string>& textureExtensions);
json TranscribeProperty(FbxProperty& prop); json TranscribeProperty(FbxProperty& prop);

View File

@ -95,7 +95,7 @@ class FbxBlendShapesAccess {
} }
FbxAnimCurve* GetAnimation(size_t channelIx, size_t animIx) const { FbxAnimCurve* GetAnimation(size_t channelIx, size_t animIx) const {
return channels.at(channelIx).ExtractAnimation(animIx); return channels.at(channelIx).ExtractAnimation(to_uint32(animIx));
} }
private: private:

View File

@ -37,7 +37,7 @@ class FbxSkinningAccess {
return jointNodes[jointIndex]; return jointNodes[jointIndex];
} }
const long GetJointId(const int jointIndex) const { const uint64_t GetJointId(const int jointIndex) const {
return jointIds[jointIndex]; return jointIds[jointIndex];
} }
@ -49,7 +49,7 @@ class FbxSkinningAccess {
return jointInverseGlobalTransforms[jointIndex]; return jointInverseGlobalTransforms[jointIndex];
} }
const long GetRootNode() const { const uint64_t GetRootNode() const {
assert(rootIndex != -1); assert(rootIndex != -1);
return jointIds[rootIndex]; return jointIds[rootIndex];
} }
@ -70,7 +70,7 @@ class FbxSkinningAccess {
private: private:
int rootIndex; int rootIndex;
std::vector<long> jointIds; std::vector<uint64_t> jointIds;
std::vector<FbxNode*> jointNodes; std::vector<FbxNode*> jointNodes;
std::vector<FbxMatrix> jointSkinningTransforms; std::vector<FbxMatrix> jointSkinningTransforms;
std::vector<FbxMatrix> jointInverseGlobalTransforms; std::vector<FbxMatrix> jointInverseGlobalTransforms;

View File

@ -12,7 +12,7 @@
std::shared_ptr<BufferViewData> GltfModel::GetAlignedBufferView( std::shared_ptr<BufferViewData> GltfModel::GetAlignedBufferView(
BufferData& buffer, BufferData& buffer,
const BufferViewData::GL_ArrayType target) { const BufferViewData::GL_ArrayType target) {
unsigned long bufferSize = this->binary->size(); uint32_t bufferSize = to_uint32(this->binary->size());
if ((bufferSize % 4) > 0) { if ((bufferSize % 4) > 0) {
bufferSize += (4 - (bufferSize % 4)); bufferSize += (4 - (bufferSize % 4));
this->binary->resize(bufferSize); this->binary->resize(bufferSize);
@ -27,7 +27,7 @@ GltfModel::AddRawBufferView(BufferData& buffer, const char* source, uint32_t byt
bufferView->byteLength = bytes; bufferView->byteLength = bytes;
// make space for the new bytes (possibly moving the underlying data) // make space for the new bytes (possibly moving the underlying data)
unsigned long bufferSize = this->binary->size(); uint32_t bufferSize = to_uint32(this->binary->size());
this->binary->resize(bufferSize + bytes); this->binary->resize(bufferSize + bytes);
// and copy them into place // and copy them into place
@ -52,7 +52,7 @@ std::shared_ptr<BufferViewData> GltfModel::AddBufferViewForFile(
std::vector<char> fileBuffer(size); std::vector<char> fileBuffer(size);
if (file.read(fileBuffer.data(), size)) { if (file.read(fileBuffer.data(), size)) {
result = AddRawBufferView(buffer, fileBuffer.data(), size); result = AddRawBufferView(buffer, fileBuffer.data(), to_uint32(size));
} else { } else {
fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename); fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename);
} }

View File

@ -44,7 +44,7 @@ template <typename T>
class Holder { class Holder {
public: public:
std::shared_ptr<T> hold(T* ptr) { std::shared_ptr<T> hold(T* ptr) {
ptr->ix = ptrs.size(); ptr->ix = to_uint32(ptrs.size());
ptrs.emplace_back(ptr); ptrs.emplace_back(ptr);
return ptrs.back(); return ptrs.back();
} }
@ -114,7 +114,7 @@ class GltfModel {
primitive.AddDracoAttrib(attrDef, attribArr); primitive.AddDracoAttrib(attrDef, attribArr);
accessor = accessors.hold(new AccessorData(attrDef.glType)); accessor = accessors.hold(new AccessorData(attrDef.glType));
accessor->count = attribArr.size(); accessor->count = to_uint32(attribArr.size());
} else { } else {
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));

View File

@ -256,29 +256,28 @@ ModelData* Raw2Gltf(
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) { if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
/** /**
* PBR FBX Material -> PBR Met/Rough glTF. * PBR FBX Material -> PBR Met/Rough glTF.
*
* METALLIC and ROUGHNESS textures are packed in G and B channels of a rough/met texture.
* Other values translate directly.
*/ */
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get(); RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
// diffuse and emissive are noncontroversial
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
diffuseFactor = props->diffuseFactor;
emissiveFactor = props->emissiveFactor;
emissiveIntensity = props->emissiveIntensity;
// we always send the metallic/roughness factors onto the glTF generator
metallic = props->metallic;
roughness = props->roughness;
// determine if we need to generate a combined map // determine if we need to generate a combined map
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0; bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0; bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0; bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap) bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
: (hasRoughnessMap && hasMetallicMap); : (hasRoughnessMap && hasMetallicMap);
if (atLeastTwoMaps) { if (!atLeastTwoMaps) {
// if there's at least two of metallic/roughness/occlusion, it makes sense to // this handles the case of 0 or 1 maps supplied
// merge them: occlusion into the red channel, metallic into blue channel, and aoMetRoughTex = hasMetallicMap
// roughness into the green. ? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
: (hasRoughnessMap
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
: (hasOcclusionMap ? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION) : nullptr));
} else {
// otherwise merge occlusion into the red channel, metallic into blue channel, and
// roughness into the green, of a new combinatory texture
aoMetRoughTex = textureBuilder.combine( aoMetRoughTex = textureBuilder.combine(
{ {
material.textures[RAW_TEXTURE_USAGE_OCCLUSION], material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
@ -289,27 +288,24 @@ ModelData* Raw2Gltf(
[&](const std::vector<const TextureBuilder::pixel*> pixels) [&](const std::vector<const TextureBuilder::pixel*> pixels)
-> TextureBuilder::pixel { -> TextureBuilder::pixel {
const float occlusion = (*pixels[0])[0]; const float occlusion = (*pixels[0])[0];
const float metallic = (*pixels[1])[0]; const float metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic);
const float roughness = (*pixels[2])[0]; const float roughness =
(*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness);
return {{occlusion, return {{occlusion,
props->invertRoughnessMap ? 1.0f - roughness : roughness, props->invertRoughnessMap ? 1.0f - roughness : roughness,
metallic, metallic,
1}}; 1}};
}, },
false); false);
if (hasOcclusionMap) {
// will only be true if there were actual non-trivial pixels
occlusionTexture = aoMetRoughTex.get();
}
} else {
// this handles the case of 0 or 1 maps supplied
if (hasMetallicMap) {
aoMetRoughTex = simpleTex(RAW_TEXTURE_USAGE_METALLIC);
} else if (hasRoughnessMap) {
aoMetRoughTex = simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS);
}
// else only occlusion map is possible: that check is handled further below
} }
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
diffuseFactor = props->diffuseFactor;
metallic = props->metallic;
roughness = props->roughness;
emissiveFactor = props->emissiveFactor;
emissiveIntensity = props->emissiveIntensity;
// this will set occlusionTexture to null, if no actual occlusion map exists
occlusionTexture = aoMetRoughTex.get();
} else { } else {
/** /**
* Traditional FBX Material -> PBR Met/Rough glTF. * Traditional FBX Material -> PBR Met/Rough glTF.
@ -393,7 +389,6 @@ ModelData* Raw2Gltf(
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial()); khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
} }
// after all the special cases have had a go, check if we need to look up occlusion map
if (!occlusionTexture) { if (!occlusionTexture) {
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get(); occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
} }
@ -447,11 +442,11 @@ ModelData* Raw2Gltf(
std::shared_ptr<PrimitiveData> primitive; std::shared_ptr<PrimitiveData> primitive;
if (options.draco.enabled) { if (options.draco.enabled) {
int triangleCount = surfaceModel.GetTriangleCount(); size_t triangleCount = surfaceModel.GetTriangleCount();
// initialize Draco mesh with vertex index information // initialize Draco mesh with vertex index information
auto dracoMesh(std::make_shared<draco::Mesh>()); auto dracoMesh(std::make_shared<draco::Mesh>());
dracoMesh->SetNumFaces(static_cast<size_t>(triangleCount)); dracoMesh->SetNumFaces(triangleCount);
dracoMesh->set_num_points(surfaceModel.GetVertexCount()); dracoMesh->set_num_points(surfaceModel.GetVertexCount());
for (uint32_t ii = 0; ii < triangleCount; ii++) { for (uint32_t ii = 0; ii < triangleCount; ii++) {
@ -464,7 +459,7 @@ ModelData* Raw2Gltf(
AccessorData& indexes = AccessorData& indexes =
*gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT)); *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT));
indexes.count = 3 * triangleCount; indexes.count = to_uint32(3 * triangleCount);
primitive.reset(new PrimitiveData(indexes, mData, dracoMesh)); primitive.reset(new PrimitiveData(indexes, mData, dracoMesh));
} else { } else {
const AccessorData& indexes = *gltf->AddAccessorWithView( const AccessorData& indexes = *gltf->AddAccessorWithView(
@ -499,11 +494,13 @@ ModelData* Raw2Gltf(
GLT_VEC3F, GLT_VEC3F,
draco::GeometryAttribute::NORMAL, draco::GeometryAttribute::NORMAL,
draco::DT_FLOAT32); draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL); const auto _ =
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) {
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F); const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT); const auto _ = gltf->AddAttributeToPrimitive<Vec4f>(
buffer, surfaceModel, *primitive, ATTR_TANGENT);
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) {
const AttributeDefinition<Vec4f> ATTR_COLOR( const AttributeDefinition<Vec4f> ATTR_COLOR(
@ -512,7 +509,8 @@ ModelData* Raw2Gltf(
GLT_VEC4F, GLT_VEC4F,
draco::GeometryAttribute::COLOR, draco::GeometryAttribute::COLOR,
draco::DT_FLOAT32); draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR); const auto _ =
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR);
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) {
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0( const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0(
@ -521,7 +519,8 @@ ModelData* Raw2Gltf(
GLT_VEC2F, GLT_VEC2F,
draco::GeometryAttribute::TEX_COORD, draco::GeometryAttribute::TEX_COORD,
draco::DT_FLOAT32); draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0); const auto _ = gltf->AddAttributeToPrimitive<Vec2f>(
buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0);
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) {
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1( const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1(
@ -530,7 +529,8 @@ ModelData* Raw2Gltf(
GLT_VEC2F, GLT_VEC2F,
draco::GeometryAttribute::TEX_COORD, draco::GeometryAttribute::TEX_COORD,
draco::DT_FLOAT32); draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); const auto _ = gltf->AddAttributeToPrimitive<Vec2f>(
buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
const AttributeDefinition<Vec4i> ATTR_JOINTS( const AttributeDefinition<Vec4i> ATTR_JOINTS(
@ -539,7 +539,8 @@ ModelData* Raw2Gltf(
GLT_VEC4I, GLT_VEC4I,
draco::GeometryAttribute::GENERIC, draco::GeometryAttribute::GENERIC,
draco::DT_UINT16); draco::DT_UINT16);
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS); const auto _ =
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
} }
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) { if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
const AttributeDefinition<Vec4f> ATTR_WEIGHTS( const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
@ -548,7 +549,8 @@ ModelData* Raw2Gltf(
GLT_VEC4F, GLT_VEC4F,
draco::GeometryAttribute::GENERIC, draco::GeometryAttribute::GENERIC,
draco::DT_FLOAT32); draco::DT_FLOAT32);
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); const auto _ =
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
} }
// each channel present in the mesh always ends up a target in the primitive // each channel present in the mesh always ends up a target in the primitive
@ -633,7 +635,7 @@ ModelData* Raw2Gltf(
draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer); draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer);
assert(status.code() == draco::Status::OK); assert(status.code() == draco::Status::OK);
auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), dracoBuffer.size()); auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), to_uint32(dracoBuffer.size()));
primitive->NoteDracoBuffer(*view); primitive->NoteDracoBuffer(*view);
} }
mesh->AddPrimitive(primitive); mesh->AddPrimitive(primitive);
@ -735,7 +737,7 @@ ModelData* Raw2Gltf(
type = LightData::Type::Spot; type = LightData::Type::Spot;
break; break;
} }
gltf->lights.hold(new LightData( const auto _ = gltf->lights.hold(new LightData(
light.name, light.name,
type, type,
light.color, light.color,
@ -842,13 +844,13 @@ ModelData* Raw2Gltf(
gltfOutStream.write(glb2BinaryHeader, 8); gltfOutStream.write(glb2BinaryHeader, 8);
// append binary buffer directly to .glb file // append binary buffer directly to .glb file
uint32_t binaryLength = gltf->binary->size(); size_t binaryLength = gltf->binary->size();
gltfOutStream.write((const char*)&(*gltf->binary)[0], binaryLength); gltfOutStream.write((const char*)&(*gltf->binary)[0], binaryLength);
while ((binaryLength % 4) != 0) { while ((binaryLength % 4) != 0) {
gltfOutStream.put('\0'); gltfOutStream.put('\0');
binaryLength++; binaryLength++;
} }
uint32_t totalLength = (uint32_t)gltfOutStream.tellp(); uint32_t totalLength = to_uint32(gltfOutStream.tellp());
// seek back to sub-header for json chunk // seek back to sub-header for json chunk
gltfOutStream.seekp(8); gltfOutStream.seekp(8);

View File

@ -120,7 +120,7 @@ const GLType GLT_QUATF = {CT_FLOAT, 4, "VEC4"};
* The base of any indexed glTF entity. * The base of any indexed glTF entity.
*/ */
struct Holdable { struct Holdable {
uint32_t ix; uint32_t ix = UINT_MAX;
virtual json serialize() const = 0; virtual json serialize() const = 0;
}; };

View File

@ -49,8 +49,7 @@ std::shared_ptr<TextureData> TextureBuilder::combine(
if (rawTexIx >= 0) { if (rawTexIx >= 0) {
const RawTexture& rawTex = raw.GetTexture(rawTexIx); const RawTexture& rawTex = raw.GetTexture(rawTexIx);
const std::string& fileLoc = rawTex.fileLocation; const std::string& fileLoc = rawTex.fileLocation;
const std::string& name = const std::string& name = FileUtils::GetFileBaseString(FileUtils::GetFileNameString(fileLoc));
StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc));
if (!fileLoc.empty()) { if (!fileLoc.empty()) {
info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0); info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0);
if (!info.pixels) { if (!info.pixels) {
@ -142,7 +141,7 @@ std::shared_ptr<TextureData> TextureBuilder::combine(
ImageData* image; ImageData* image;
if (options.outputBinary) { if (options.outputBinary) {
const auto bufferView = const auto bufferView =
gltf.AddRawBufferView(*gltf.defaultBuffer, imgBuffer.data(), imgBuffer.size()); gltf.AddRawBufferView(*gltf.defaultBuffer, imgBuffer.data(), to_uint32(imgBuffer.size()));
image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg"); image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg");
} else { } else {
const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg"); const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg");
@ -180,20 +179,30 @@ std::shared_ptr<TextureData> TextureBuilder::simple(int rawTexIndex, const std::
} }
const RawTexture& rawTexture = raw.GetTexture(rawTexIndex); const RawTexture& rawTexture = raw.GetTexture(rawTexIndex);
const std::string textureName = StringUtils::GetFileBaseString(rawTexture.name); const std::string textureName = FileUtils::GetFileBaseString(rawTexture.name);
const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation); const std::string relativeFilename = FileUtils::GetFileNameString(rawTexture.fileLocation);
ImageData* image = nullptr; ImageData* image = nullptr;
if (options.outputBinary) { if (options.outputBinary) {
auto bufferView = gltf.AddBufferViewForFile(*gltf.defaultBuffer, rawTexture.fileLocation); auto bufferView = gltf.AddBufferViewForFile(*gltf.defaultBuffer, rawTexture.fileLocation);
if (bufferView) { if (bufferView) {
std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); const auto& suffix = FileUtils::GetFileSuffix(rawTexture.fileLocation);
image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix)); std::string mimeType;
if (suffix) {
mimeType = ImageUtils::suffixToMimeType(suffix.value());
} else {
mimeType = "image/jpeg";
fmt::printf(
"Warning: Can't deduce mime type of texture '%s'; using %s.\n",
rawTexture.fileLocation,
mimeType);
}
image = new ImageData(relativeFilename, *bufferView, mimeType);
} }
} else if (!relativeFilename.empty()) { } else if (!relativeFilename.empty()) {
image = new ImageData(relativeFilename, relativeFilename); image = new ImageData(relativeFilename, relativeFilename);
std::string outputPath = outputFolder + StringUtils::NormalizePath(relativeFilename); std::string outputPath = FileUtils::GetCanonicalPath(outputFolder + "/" + relativeFilename);
if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true)) { if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true)) {
if (verboseOutput) { if (verboseOutput) {
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath); fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);

View File

@ -24,7 +24,7 @@ void AnimationData::AddNodeChannel(
const AccessorData& accessor, const AccessorData& accessor,
std::string path) { std::string path) {
assert(channels.size() == samplers.size()); assert(channels.size() == samplers.size());
uint32_t ix = channels.size(); uint32_t ix = to_uint32(channels.size());
channels.emplace_back(channel_t(ix, node, std::move(path))); channels.emplace_back(channel_t(ix, node, std::move(path)));
samplers.emplace_back(sampler_t(timeAccessor, accessor.ix)); samplers.emplace_back(sampler_t(timeAccessor, accessor.ix));
} }

View File

@ -59,13 +59,15 @@ void to_json(json& j, const PBRMetallicRoughness& d) {
if (d.baseColorFactor.LengthSquared() > 0) { if (d.baseColorFactor.LengthSquared() > 0) {
j["baseColorFactor"] = toStdVec(d.baseColorFactor); j["baseColorFactor"] = toStdVec(d.baseColorFactor);
} }
// we always copy metallic/roughness straight to the glTF:
// - if there's a texture, they're linear multiplier
// - if there's no texture, they're constants
j["metallicFactor"] = d.metallic;
j["roughnessFactor"] = d.roughness;
if (d.metRoughTexture != nullptr) { if (d.metRoughTexture != nullptr) {
j["metallicRoughnessTexture"] = *d.metRoughTexture; j["metallicRoughnessTexture"] = *d.metRoughTexture;
// if a texture is provided, throw away metallic/roughness values
j["roughnessFactor"] = 1.0f;
j["metallicFactor"] = 1.0f;
} else {
// without a texture, however, use metallic/roughness as constants
j["metallicFactor"] = d.metallic;
j["roughnessFactor"] = d.roughness;
} }
} }

View File

@ -49,7 +49,7 @@ struct PrimitiveData {
componentCount * draco::DataTypeLength(attribute.dracoComponentType), componentCount * draco::DataTypeLength(attribute.dracoComponentType),
0); 0);
const int dracoAttId = dracoMesh->AddAttribute(att, true, attribArr.size()); const int dracoAttId = dracoMesh->AddAttribute(att, true, to_uint32(attribArr.size()));
draco::PointAttribute* attPtr = dracoMesh->attribute(dracoAttId); draco::PointAttribute* attPtr = dracoMesh->attribute(dracoAttId);
std::vector<uint8_t> buf(sizeof(T)); std::vector<uint8_t> buf(sizeof(T));

View File

@ -632,7 +632,7 @@ void RawModel::CreateMaterialModels(
} }
} }
int RawModel::GetNodeById(const long nodeId) const { int RawModel::GetNodeById(const uint32_t nodeId) const {
for (size_t i = 0; i < nodes.size(); i++) { for (size_t i = 0; i < nodes.size(); i++) {
if (nodes[i].id == nodeId) { if (nodes[i].id == nodeId) {
return (int)i; return (int)i;
@ -641,7 +641,7 @@ int RawModel::GetNodeById(const long nodeId) const {
return -1; return -1;
} }
int RawModel::GetSurfaceById(const long surfaceId) const { int RawModel::GetSurfaceById(const uint32_t surfaceId) const {
for (size_t i = 0; i < surfaces.size(); i++) { for (size_t i = 0; i < surfaces.size(); i++) {
if (surfaces[i].id == surfaceId) { if (surfaces[i].id == surfaceId) {
return (int)i; return (int)i;

View File

@ -10,171 +10,41 @@
#include "File_Utils.hpp" #include "File_Utils.hpp"
#include <fstream> #include <fstream>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#if defined(__unix__) || defined(__APPLE__)
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define _getcwd getcwd
#define _mkdir(a) mkdir(a, 0777)
#elif defined(_WIN32)
#include <direct.h>
#include <process.h>
#else
#include <direct.h>
#include <process.h>
#endif
#include <sys/stat.h>
#include "FBX2glTF.h" #include "FBX2glTF.h"
#include "String_Utils.hpp" #include "String_Utils.hpp"
namespace FileUtils { namespace FileUtils {
std::string GetCurrentFolder() { std::vector<std::string> ListFolderFiles(
char cwd[StringUtils::MAX_PATH_LENGTH]; const std::string folder,
if (!_getcwd(cwd, sizeof(cwd))) { const std::set<std::string>& matchExtensions) {
return std::string();
}
cwd[sizeof(cwd) - 1] = '\0';
StringUtils::GetCleanPath(cwd, cwd, StringUtils::PATH_UNIX);
const size_t length = strlen(cwd);
if (cwd[length - 1] != '/' && length < StringUtils::MAX_PATH_LENGTH - 1) {
cwd[length + 0] = '/';
cwd[length + 1] = '\0';
}
return std::string(cwd);
}
bool FileExists(const std::string& filePath) {
std::ifstream stream(filePath);
return stream.good();
}
bool FolderExists(const std::string& folderPath) {
#if defined(__unix__) || defined(__APPLE__)
DIR* dir = opendir(folderPath.c_str());
if (dir) {
closedir(dir);
return true;
}
return false;
#else
const DWORD ftyp = GetFileAttributesA(folderPath.c_str());
if (ftyp == INVALID_FILE_ATTRIBUTES) {
return false; // bad path
}
return (ftyp & FILE_ATTRIBUTE_DIRECTORY) != 0;
#endif
}
bool MatchExtension(const char* fileExtension, const char* matchExtensions) {
if (matchExtensions[0] == '\0') {
return true;
}
if (fileExtension[0] == '.') {
fileExtension++;
}
for (const char* end = matchExtensions; end[0] != '\0';) {
for (; end[0] == ';'; end++) {
}
const char* ext = end;
for (; end[0] != ';' && end[0] != '\0'; end++) {
}
#if defined(__unix__) || defined(__APPLE__)
if (strncasecmp(fileExtension, ext, end - ext) == 0)
#else
if (_strnicmp(fileExtension, ext, end - ext) == 0)
#endif
{
return true;
}
}
return false;
}
std::vector<std::string> ListFolderFiles(const char* folder, const char* matchExtensions) {
std::vector<std::string> fileList; std::vector<std::string> fileList;
#if defined(__unix__) || defined(__APPLE__) for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(folder)) {
DIR* dir = opendir(strlen(folder) > 0 ? folder : "."); const std::filesystem::path& path = entry.path();
if (dir != nullptr) { if (matchExtensions.find(path.extension().string()) != matchExtensions.end()) {
for (;;) { fileList.push_back(path.string());
struct dirent* dp = readdir(dir);
if (dp == nullptr) {
break;
}
if (dp->d_type == DT_DIR) {
continue;
}
const char* fileName = dp->d_name;
const char* fileExt = strrchr(fileName, '.');
if (!fileExt || !MatchExtension(fileExt, matchExtensions)) {
continue;
}
fileList.emplace_back(fileName);
} }
closedir(dir);
} }
#else
std::string pathStr = folder;
pathStr += "*";
WIN32_FIND_DATA FindFileData;
HANDLE hFind = FindFirstFile(pathStr.c_str(), &FindFileData);
if (hFind != INVALID_HANDLE_VALUE) {
do {
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
std::string fileName = FindFileData.cFileName;
std::string::size_type extPos = fileName.rfind('.');
if (extPos != std::string::npos &&
MatchExtension(fileName.substr(extPos + 1).c_str(), matchExtensions)) {
fileList.push_back(fileName);
}
}
} while (FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
#endif
return fileList; return fileList;
} }
bool CreatePath(const char* path) { bool CreatePath(const std::string path) {
#if defined(__unix__) || defined(__APPLE__) const auto& parent = std::filesystem::path(path).parent_path();
StringUtils::PathSeparator separator = StringUtils::PATH_UNIX; if (parent.empty()) {
#else // this is either CWD or std::filesystem root; either way it exists
StringUtils::PathSeparator separator = StringUtils::PATH_WIN; return true;
#endif
std::string folder = StringUtils::GetFolderString(path);
std::string clean = StringUtils::GetCleanPathString(folder, separator);
std::string build = clean;
for (int i = 0; i < clean.length(); i++) {
if (clean[i] == separator && i > 0) {
build[i] = '\0';
if (i > 1 || build[1] != ':') {
if (_mkdir(build.c_str()) != 0 && errno != EEXIST) {
return false;
}
}
}
build[i] = clean[i];
} }
return true; if (std::filesystem::exists(parent)) {
return std::filesystem::is_directory(parent);
}
return std::filesystem::create_directory(parent);
} }
bool CopyFile(const std::string& srcFilename, const std::string& dstFilename, bool createPath) { bool CopyFile(const std::string& srcFilename, const std::string& dstFilename, bool createPath) {

View File

@ -9,6 +9,10 @@
#pragma once #pragma once
#include <filesystem>
#include <optional>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@ -19,13 +23,51 @@ std::string GetCurrentFolder();
bool FileExists(const std::string& folderPath); bool FileExists(const std::string& folderPath);
bool FolderExists(const std::string& folderPath); bool FolderExists(const std::string& folderPath);
bool MatchExtension(const char* fileExtension, const char* matchExtensions); std::vector<std::string> ListFolderFiles(
std::vector<std::string> ListFolderFiles(const char* folder, const char* matchExtensions); const std::string folder,
const std::set<std::string>& matchExtensions);
bool CreatePath(const char* path); bool CreatePath(std::string path);
bool CopyFile( bool CopyFile(
const std::string& srcFilename, const std::string& srcFilename,
const std::string& dstFilename, const std::string& dstFilename,
bool createPath = false); bool createPath = false);
inline std::string GetCurrentFolder() {
return std::filesystem::current_path().string() + "/";
}
inline bool FileExists(const std::string& filePath) {
return std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath);
}
inline bool FolderExists(const std::string& folderPath) {
return std::filesystem::exists(folderPath) && std::filesystem::is_directory(folderPath);
}
inline std::string GetFolderString(const std::string& path) {
return std::filesystem::path(path).parent_path().string();
}
inline std::string GetCanonicalPath(const std::string& path) {
return std::filesystem::canonical(path).string();
}
inline std::string GetFileNameString(const std::string& path) {
return std::filesystem::canonical(path).filename().string();
}
inline std::string GetFileBaseString(const std::string& path) {
return std::filesystem::canonical(path).stem().string();
}
inline std::optional<std::string> GetFileSuffix(const std::string& path) {
const auto& extension = std::filesystem::canonical(path).extension();
if (extension.empty()) {
return std::nullopt;
}
return extension.string().substr(1);
}
} // namespace FileUtils } // namespace FileUtils

View File

@ -9,72 +9,12 @@
#include "String_Utils.hpp" #include "String_Utils.hpp"
#include <algorithm>
namespace StringUtils { namespace StringUtils {
PathSeparator operator!(const PathSeparator& s) {
return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN;
}
PathSeparator GetPathSeparator() {
#if defined(__unix__) || defined(__APPLE__)
return PATH_UNIX;
#else
return PATH_WIN;
#endif
}
const std::string NormalizePath(const std::string& path) {
PathSeparator separator = GetPathSeparator();
char replace;
if (separator == PATH_WIN) {
replace = PATH_UNIX;
} else {
replace = PATH_WIN;
}
std::string normalizedPath = path;
for (size_t s = normalizedPath.find(replace, 0); s != std::string::npos;
s = normalizedPath.find(replace, s)) {
normalizedPath[s] = separator;
}
return normalizedPath;
}
const std::string GetFolderString(const std::string& path) {
size_t s = path.rfind(PATH_WIN);
s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX);
return path.substr(0, s + 1);
}
const std::string GetCleanPathString(const std::string& path, const PathSeparator separator) {
std::string cleanPath = path;
for (size_t s = cleanPath.find(!separator, 0); s != std::string::npos;
s = cleanPath.find(!separator, s)) {
cleanPath[s] = separator;
}
return cleanPath;
}
const std::string GetFileNameString(const std::string& path) {
size_t s = path.rfind(PATH_WIN);
s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX);
return path.substr(s + 1, std::string::npos);
}
const std::string GetFileBaseString(const std::string& path) {
const std::string fileName = GetFileNameString(path);
return fileName.substr(0, fileName.rfind('.')).c_str();
}
const std::string GetFileSuffixString(const std::string& path) {
const std::string fileName = GetFileNameString(path);
size_t pos = fileName.rfind('.');
if (pos == std::string::npos) {
return "";
}
return fileName.substr(++pos);
}
int CompareNoCase(const std::string& s1, const std::string& s2) { int CompareNoCase(const std::string& s1, const std::string& s2) {
return strncasecmp(s1.c_str(), s2.c_str(), MAX_PATH_LENGTH); return strncasecmp(s1.c_str(), s2.c_str(), std::max(s1.length(), s2.length()));
} }
} // namespace StringUtils } // namespace StringUtils

View File

@ -21,34 +21,8 @@
namespace StringUtils { namespace StringUtils {
static const unsigned int MAX_PATH_LENGTH = 1024; inline int CompareNoCase(const std::string& s1, const std::string& s2) {
return strncasecmp(s1.c_str(), s2.c_str(), std::max(s1.length(), s2.length()));
enum PathSeparator { PATH_WIN = '\\', PATH_UNIX = '/' };
PathSeparator operator!(const PathSeparator& s);
PathSeparator GetPathSeparator();
const std::string NormalizePath(const std::string& path);
const std::string GetCleanPathString(
const std::string& path,
const PathSeparator separator = PATH_WIN);
template <size_t size>
void GetCleanPath(char (&dest)[size], const char* path, const PathSeparator separator = PATH_WIN) {
size_t len = size - 1;
strncpy(dest, path, len);
char* destPtr = dest;
while ((destPtr = strchr(destPtr, !separator)) != nullptr) {
*destPtr = separator;
}
} }
const std::string GetFolderString(const std::string& path);
const std::string GetFileNameString(const std::string& path);
const std::string GetFileBaseString(const std::string& path);
const std::string GetFileSuffixString(const std::string& path);
int CompareNoCase(const std::string& s1, const std::string& s2);
} // namespace StringUtils } // namespace StringUtils