Further improvemens to texture resolution. (#16)

* Further improvemens to texture resolution.

- Move towards std::string over char * and FbxString where convenient,
- Make a clear distinction between textures whose image files have been
  located and those who haven't; warn early in the latter case.
- Extend RawTexture so we always know logical name in FBX, original file
  name in FBX, and inferred location in local filesystem.
- In non-binary mode, simply output the inferred local file basename as
  the URI; this will be the correct relative path as long as the texture
  files are located next to the .gltf and .bin files.

Primary remaining urge for a follow-up PR:

- We should be copying texture image files into the .gltf output folder,
  but before that we should switch to an off-the-shelf cross-platform
  file manipulation library like https://github.com/cginternals/cppfs.
  When we make that transition, all this texture resolution code will
  undergo another refactoring.
This commit is contained in:
Pär Winzell 2017-10-20 09:42:39 -07:00 committed by GitHub
parent 946f12361c
commit 8cf7f446b7
10 changed files with 125 additions and 95 deletions

View File

@ -106,7 +106,7 @@ class FbxMaterialAccess
private: private:
const FbxSurfaceMaterial *fbxMaterial; const FbxSurfaceMaterial *fbxMaterial;
const std::map<const FbxTexture *, FbxString> &textureNames; const std::map<const FbxTexture *, FbxString> &textureLocations;
public: public:
const FbxString name; const FbxString name;
@ -119,7 +119,7 @@ public:
fbxMaterial(fbxMaterial), fbxMaterial(fbxMaterial),
name(fbxMaterial->GetName()), name(fbxMaterial->GetName()),
shadingModel(fbxMaterial->ShadingModel), shadingModel(fbxMaterial->ShadingModel),
textureNames(textureNames), textureLocations(textureNames),
props(extractTextures()) props(extractTextures())
{} {}
@ -180,7 +180,7 @@ public:
FbxDouble val(0); FbxDouble val(0);
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>(); FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
if (tex != nullptr && textureNames.find(tex) == textureNames.end()) { if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
tex = nullptr; tex = nullptr;
} }
if (tex == nullptr && prop.IsValid()) { if (tex == nullptr && prop.IsValid()) {
@ -195,7 +195,7 @@ public:
FbxDouble3 val(1, 1, 1); FbxDouble3 val(1, 1, 1);
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>(); FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
if (tex != nullptr && textureNames.find(tex) == textureNames.end()) { if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
tex = nullptr; tex = nullptr;
} }
if (tex == nullptr && prop.IsValid()) { if (tex == nullptr && prop.IsValid()) {
@ -213,14 +213,14 @@ public:
FbxDouble factorVal(1); FbxDouble factorVal(1);
FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>(); FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>();
if (colTex != nullptr && textureNames.find(colTex) == textureNames.end()) { if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) {
colTex = nullptr; colTex = nullptr;
} }
if (colTex == nullptr && colProp.IsValid()) { if (colTex == nullptr && colProp.IsValid()) {
colorVal = colProp.Get<FbxDouble3>(); colorVal = colProp.Get<FbxDouble3>();
} }
FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>(); FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>();
if (facTex != nullptr && textureNames.find(facTex) == textureNames.end()) { if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) {
facTex = nullptr; facTex = nullptr;
} }
if (facTex == nullptr && facProp.IsValid()) { if (facTex == nullptr && facProp.IsValid()) {
@ -240,7 +240,7 @@ class FbxMaterialsAccess
{ {
public: public:
FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<const FbxTexture *, FbxString> &textureNames) : FbxMaterialsAccess(const FbxMesh *pMesh, const std::map<const FbxTexture *, FbxString> &textureLocations) :
mappingMode(FbxGeometryElement::eNone), mappingMode(FbxGeometryElement::eNone),
mesh(nullptr), mesh(nullptr),
indices(nullptr) indices(nullptr)
@ -272,7 +272,7 @@ public:
if (summary == nullptr) { if (summary == nullptr) {
summary = summaries[materialNum] = std::make_shared<FbxMaterialAccess>( summary = summaries[materialNum] = std::make_shared<FbxMaterialAccess>(
mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum), mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum),
textureNames); textureLocations);
} }
} }
} }
@ -473,7 +473,7 @@ GetMaterialType(const RawModel &raw, const int textures[RAW_TEXTURE_USAGE_MAX],
return skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE; return skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE;
} }
static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std::map<const FbxTexture *, FbxString> &textureNames) static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std::map<const FbxTexture *, FbxString> &textureLocations)
{ {
FbxGeometryConverter meshConverter(pScene->GetFbxManager()); FbxGeometryConverter meshConverter(pScene->GetFbxManager());
meshConverter.Triangulate(pNode->GetNodeAttribute(), true); meshConverter.Triangulate(pNode->GetNodeAttribute(), true);
@ -490,7 +490,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
const FbxLayerElementAccess<FbxVector2> uvLayer0(pMesh->GetElementUV(0), pMesh->GetElementUVCount()); const FbxLayerElementAccess<FbxVector2> uvLayer0(pMesh->GetElementUV(0), pMesh->GetElementUVCount());
const FbxLayerElementAccess<FbxVector2> uvLayer1(pMesh->GetElementUV(1), pMesh->GetElementUVCount()); const FbxLayerElementAccess<FbxVector2> uvLayer1(pMesh->GetElementUV(1), pMesh->GetElementUVCount());
const FbxSkinningAccess skinning(pMesh, pScene, pNode); const FbxSkinningAccess skinning(pMesh, pScene, pNode);
const FbxMaterialsAccess materials(pMesh, textureNames); const FbxMaterialsAccess materials(pMesh, textureLocations);
if (verboseOutput) { if (verboseOutput) {
fmt::printf( fmt::printf(
@ -562,9 +562,9 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
const auto maybeAddTexture = [&](FbxFileTexture *tex, RawTextureUsage usage) { const auto maybeAddTexture = [&](FbxFileTexture *tex, RawTextureUsage usage) {
if (tex != nullptr) { if (tex != nullptr) {
// dig out the inferred filename from the textureNames map // dig out the inferred filename from the textureLocations map
const char *inferredPath = textureNames.find(tex)->second; FbxString inferredPath = textureLocations.find(tex)->second;
textures[usage] = raw.AddTexture(tex->GetName(), inferredPath, usage); textures[usage] = raw.AddTexture(tex->GetName(), tex->GetFileName(), inferredPath.Buffer(), usage);
} }
}; };
@ -708,7 +708,8 @@ static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode)
} }
} }
static void ReadNodeAttributes(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std::map<const FbxTexture *, FbxString> &textureNames) static void ReadNodeAttributes(
RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std::map<const FbxTexture *, FbxString> &textureLocations)
{ {
if (!pNode->GetVisibility()) { if (!pNode->GetVisibility()) {
return; return;
@ -723,7 +724,7 @@ static void ReadNodeAttributes(RawModel &raw, FbxScene *pScene, FbxNode *pNode,
case FbxNodeAttribute::eNurbsSurface: case FbxNodeAttribute::eNurbsSurface:
case FbxNodeAttribute::eTrimNurbsSurface: case FbxNodeAttribute::eTrimNurbsSurface:
case FbxNodeAttribute::ePatch: { case FbxNodeAttribute::ePatch: {
ReadMesh(raw, pScene, pNode, textureNames); ReadMesh(raw, pScene, pNode, textureLocations);
break; break;
} }
case FbxNodeAttribute::eCamera: { case FbxNodeAttribute::eCamera: {
@ -752,7 +753,7 @@ static void ReadNodeAttributes(RawModel &raw, FbxScene *pScene, FbxNode *pNode,
} }
for (int child = 0; child < pNode->GetChildCount(); child++) { for (int child = 0; child < pNode->GetChildCount(); child++) {
ReadNodeAttributes(raw, pScene, pNode->GetChild(child), textureNames); ReadNodeAttributes(raw, pScene, pNode->GetChild(child), textureLocations);
} }
} }
@ -944,7 +945,7 @@ static void ReadAnimations(RawModel &raw, FbxScene *pScene)
} }
} }
static std::string GetInferredFileName(const char *fbxFileName, const char *directory, const std::vector<std::string> &directoryFileList) static std::string GetInferredFileName(const std::string &fbxFileName, const std::string &directory, const std::vector<std::string> &directoryFileList)
{ {
// Get the file name with file extension. // Get the file name with file extension.
const std::string fileName = Gltf::StringUtils::GetFileNameString(Gltf::StringUtils::GetCleanPathString(fbxFileName)); const std::string fileName = Gltf::StringUtils::GetFileNameString(Gltf::StringUtils::GetCleanPathString(fbxFileName));
@ -956,25 +957,19 @@ static std::string GetInferredFileName(const char *fbxFileName, const char *dire
} }
} }
// Some FBX textures end with "_c.dds" while the source texture is a ".tga".
const bool isDDS = fileName.rfind("_c.dds") != std::string::npos;
// Get the file name without file extension. // Get the file name without file extension.
const std::string fileBase = isDDS ? fileName.substr(0, fileName.length() - 6) : Gltf::StringUtils::GetFileBaseString(fileName); const std::string fileBase = Gltf::StringUtils::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) {
const std::string listedFileBase = Gltf::StringUtils::GetFileBaseString(file.c_str());
// If the two extension-less base names match. // If the two extension-less base names match.
if (Gltf::StringUtils::CompareNoCase(fileBase, listedFileBase) == 0) { if (Gltf::StringUtils::CompareNoCase(fileBase, Gltf::StringUtils::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;
} }
} }
// Return the original file with extension return "";
return fbxFileName;
} }
/* /*
@ -987,30 +982,34 @@ static std::string GetInferredFileName(const char *fbxFileName, const char *dire
it to a list of existing texture files in the same directory as the FBX file. it to a list of existing texture files in the same directory as the FBX file.
*/ */
static void static void
FindFbxTextures(FbxScene *pScene, const char *fbxFileName, const char *extensions, std::map<const FbxTexture *, FbxString> &textureNames) FindFbxTextures(
FbxScene *pScene, const char *fbxFileName, const char *extensions, std::map<const FbxTexture *, FbxString> &textureLocations)
{ {
// Get the folder the FBX file is in. // Get the folder the FBX file is in.
const FbxString folder = Gltf::StringUtils::GetFolderString(fbxFileName).c_str(); const std::string folder = Gltf::StringUtils::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 FbxString fbmFolderName = folder + Gltf::StringUtils::GetFileBaseString(fbxFileName).c_str() + ".fbm/"; const std::string fbmFolderName = folder + Gltf::StringUtils::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 FbxString searchFolder = FileUtils::FolderExists(fbmFolderName) ? fbmFolderName : folder; const std::string searchFolder = FileUtils::FolderExists(fbmFolderName) ? fbmFolderName : folder;
// Get a list with all the texture files from either the folder with embedded textures or the same folder as the FBX file. // Get a list with all the texture files from either the folder with embedded textures or the same folder as the FBX file.
std::vector<std::string> fileList; std::vector<std::string> fileList = FileUtils::ListFolderFiles(searchFolder.c_str(), extensions);
FileUtils::ListFolderFiles(fileList, searchFolder, extensions);
// Try to match the FBX texture names with the actual files on disk. // Try to match the FBX texture names with the actual files on disk.
for (int i = 0; i < pScene->GetTextureCount(); i++) { for (int i = 0; i < pScene->GetTextureCount(); i++) {
const FbxTexture *pTexture = pScene->GetTexture(i); const FbxFileTexture *pFileTexture = FbxCast<FbxFileTexture>(pScene->GetTexture(i));
const FbxFileTexture *pFileTexture = FbxCast<FbxFileTexture>(pTexture);
if (pFileTexture == nullptr) { if (pFileTexture == nullptr) {
continue; continue;
} }
const FbxString name = GetInferredFileName(pFileTexture->GetFileName(), searchFolder, fileList).c_str(); const std::string inferredName = GetInferredFileName(pFileTexture->GetFileName(), searchFolder, fileList);
textureNames.emplace(pTexture, name); if (inferredName.empty()) {
fmt::printf("Warning: could not find a local image file for texture: %s.\n"
"Original filename: %s\n", pFileTexture->GetName(), pFileTexture->GetFileName());
}
// always extend the mapping, even for files we didn't find
textureLocations.emplace(pFileTexture, inferredName.c_str());
} }
} }
@ -1041,8 +1040,8 @@ bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExte
return false; return false;
} }
std::map<const FbxTexture *, FbxString> textureNames; std::map<const FbxTexture *, FbxString> textureLocations;
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureNames); FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations);
// Use Y up for glTF // Use Y up for glTF
FbxAxisSystem::MayaYUp.ConvertScene(pScene); FbxAxisSystem::MayaYUp.ConvertScene(pScene);
@ -1054,7 +1053,7 @@ bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExte
} }
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), "", ""); ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), "", "");
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureNames); ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
ReadAnimations(raw, pScene); ReadAnimations(raw, pScene);
pScene->Destroy(); pScene->Destroy();

View File

@ -14,6 +14,7 @@
#include "FBX2glTF.h" #include "FBX2glTF.h"
#include "utils/String_Utils.h" #include "utils/String_Utils.h"
#include "utils/Image_Utils.h"
#include "RawModel.h" #include "RawModel.h"
#include "Raw2Gltf.h" #include "Raw2Gltf.h"
@ -362,25 +363,25 @@ ModelData *Raw2Gltf(
for (int textureIndex = 0; textureIndex < raw.GetTextureCount(); textureIndex++) { for (int textureIndex = 0; textureIndex < raw.GetTextureCount(); textureIndex++) {
const RawTexture &texture = raw.GetTexture(textureIndex); const RawTexture &texture = raw.GetTexture(textureIndex);
const std::string textureName = Gltf::StringUtils::GetFileBaseString(texture.name); const std::string textureName = Gltf::StringUtils::GetFileBaseString(texture.name);
const std::string texFilename = texture.fileName; const std::string relativeFilename = Gltf::StringUtils::GetFileNameString(texture.fileLocation);
ImageData *source = nullptr; ImageData *source = nullptr;
if (options.outputBinary) { if (options.outputBinary) {
auto bufferView = gltf->AddBufferViewForFile(buffer, texFilename); auto bufferView = gltf->AddBufferViewForFile(buffer, texture.fileLocation);
if (bufferView) { if (bufferView) {
source = new ImageData(textureName, *bufferView, "image/png"); std::string suffix = Gltf::StringUtils::GetFileSuffixString(texture.fileLocation);
source = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix));
} }
} else { } else {
// TODO: don't add .ktx here; try to work out a reasonable relative path. source = new ImageData(relativeFilename, relativeFilename);
source = new ImageData(textureName, textureName + ".ktx");
} }
if (!source) { if (!source) {
// fallback is tiny transparent gif // fallback is tiny transparent gif
source = new ImageData(textureName, "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="); source = new ImageData(textureName, "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=");
} }
TextureData &texDat = *gltf->textures.hold( const TextureData &texDat = *gltf->textures.hold(
new TextureData(textureName, defaultSampler, *gltf->images.hold(source))); new TextureData(textureName, defaultSampler, *gltf->images.hold(source)));
assert(texDat.ix == textureIndex); assert(texDat.ix == textureIndex);
} }

View File

@ -84,9 +84,9 @@ int RawModel::AddTriangle(const int v0, const int v1, const int v2, const int ma
return (int) triangles.size() - 1; return (int) triangles.size() - 1;
} }
int RawModel::AddTexture(const char *name, const char *fileName, const RawTextureUsage usage) int RawModel::AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage)
{ {
if (name[0] == '\0') { if (name.empty()) {
return -1; return -1;
} }
for (size_t i = 0; i < textures.size(); i++) { for (size_t i = 0; i < textures.size(); i++) {
@ -95,7 +95,7 @@ int RawModel::AddTexture(const char *name, const char *fileName, const RawTextur
} }
} }
const ImageProperties properties = GetImageProperties(fileName); const ImageProperties properties = GetImageProperties(!fileLocation.empty() ? fileLocation.c_str() : fileName.c_str());
RawTexture texture; RawTexture texture;
texture.name = name; texture.name = name;
@ -106,6 +106,7 @@ int RawModel::AddTexture(const char *name, const char *fileName, const RawTextur
texture.occlusion = (properties.occlusion == IMAGE_TRANSPARENT) ? texture.occlusion = (properties.occlusion == IMAGE_TRANSPARENT) ?
RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE; RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE;
texture.fileName = fileName; texture.fileName = fileName;
texture.fileLocation = fileLocation;
textures.emplace_back(texture); textures.emplace_back(texture);
return (int) textures.size() - 1; return (int) textures.size() - 1;
} }
@ -313,7 +314,7 @@ void RawModel::Condense()
for (int j = 0; j < RAW_TEXTURE_USAGE_MAX; j++) { for (int j = 0; j < RAW_TEXTURE_USAGE_MAX; j++) {
if (material.textures[j] >= 0) { if (material.textures[j] >= 0) {
const RawTexture &texture = oldTextures[material.textures[j]]; const RawTexture &texture = oldTextures[material.textures[j]];
const int textureIndex = AddTexture(texture.name.c_str(), texture.fileName.c_str(), texture.usage); const int textureIndex = AddTexture(texture.name, texture.fileName, texture.fileLocation, texture.usage);
textures[textureIndex] = texture; textures[textureIndex] = texture;
material.textures[j] = textureIndex; material.textures[j] = textureIndex;
} }

View File

@ -122,13 +122,14 @@ enum RawTextureOcclusion
struct RawTexture struct RawTexture
{ {
std::string name; std::string name; // logical name in FBX file
int width; int width;
int height; int height;
int mipLevels; int mipLevels;
RawTextureUsage usage; RawTextureUsage usage;
RawTextureOcclusion occlusion; RawTextureOcclusion occlusion;
std::string fileName; std::string fileName; // original filename in FBX file
std::string fileLocation; // inferred path in local filesystem, or ""
}; };
enum RawMaterialType enum RawMaterialType
@ -233,7 +234,7 @@ public:
void AddVertexAttribute(const RawVertexAttribute attrib); void AddVertexAttribute(const RawVertexAttribute attrib);
int AddVertex(const RawVertex &vertex); int AddVertex(const RawVertex &vertex);
int AddTriangle(const int v0, const int v1, const int v2, const int materialIndex, const int surfaceIndex); int AddTriangle(const int v0, const int v1, const int v2, const int materialIndex, const int surfaceIndex);
int AddTexture(const char *name, const char *fileName, const RawTextureUsage usage); int AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage);
int AddMaterial(const RawMaterial &material); int AddMaterial(const RawMaterial &material);
int AddMaterial( int AddMaterial(
const char *name, const char *shadingModel, RawMaterialType materialType, const char *name, const char *shadingModel, RawMaterialType materialType,

View File

@ -31,8 +31,9 @@ ImageData::ImageData(std::string name, const BufferViewData &bufferView, std::st
json ImageData::serialize() const json ImageData::serialize() const
{ {
if (mimeType.empty()) { if (bufferView < 0) {
return { return {
{ "name", name },
{ "uri", uri } { "uri", uri }
}; };
} }

View File

@ -182,7 +182,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
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(), "tga;bmp;png;jpg")) { if (!LoadFBXFile(raw, inputPath.c_str(), "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

@ -54,17 +54,17 @@ namespace FileUtils {
return std::string(cwd); return std::string(cwd);
} }
bool FolderExists(const char *folderPath) bool FolderExists(const std::string &folderPath)
{ {
#if defined( __unix__ ) || defined( __APPLE__ ) #if defined( __unix__ ) || defined( __APPLE__ )
DIR *dir = opendir(folderPath); DIR *dir = opendir(folderPath.c_str());
if (dir) { if (dir) {
closedir(dir); closedir(dir);
return true; return true;
} }
return false; return false;
#else #else
const DWORD ftyp = GetFileAttributesA( folderPath ); const DWORD ftyp = GetFileAttributesA( folderPath.c_str() );
if ( ftyp == INVALID_FILE_ATTRIBUTES ) if ( ftyp == INVALID_FILE_ATTRIBUTES )
{ {
return false; // bad path return false; // bad path
@ -97,13 +97,12 @@ namespace FileUtils {
return false; return false;
} }
void ListFolderFiles(std::vector<std::string> &fileList, const char *folder, const char *matchExtensions) std::vector<std::string> ListFolderFiles(const char *folder, const char *matchExtensions)
{ {
std::vector<std::string> fileList;
#if defined( __unix__ ) || defined( __APPLE__ ) #if defined( __unix__ ) || defined( __APPLE__ )
DIR *dir = opendir(folder); DIR *dir = opendir(folder);
if (dir == nullptr) { if (dir != nullptr) {
return;
}
for (;;) { for (;;) {
struct dirent *dp = readdir(dir); struct dirent *dp = readdir(dir);
if (dp == nullptr) { if (dp == nullptr) {
@ -125,6 +124,7 @@ namespace FileUtils {
} }
closedir(dir); closedir(dir);
}
#else #else
std::string pathStr = folder; std::string pathStr = folder;
pathStr += "*"; pathStr += "*";
@ -148,6 +148,7 @@ namespace FileUtils {
FindClose( hFind ); FindClose( hFind );
} }
#endif #endif
return fileList;
} }
bool CreatePath(const char *path) bool CreatePath(const char *path)

View File

@ -13,10 +13,10 @@
namespace FileUtils { namespace FileUtils {
std::string GetCurrentFolder(); std::string GetCurrentFolder();
bool FolderExists(const char *folderPath); bool FolderExists(const std::string &folderPath);
bool MatchExtension(const char *fileExtension, const char *matchExtensions); bool MatchExtension(const char *fileExtension, const char *matchExtensions);
void ListFolderFiles(std::vector<std::string> &fileList, const char *folder, const char *matchExtensions); std::vector<std::string> ListFolderFiles(const char *folder, const char *matchExtensions);
bool CreatePath(const char *path); bool CreatePath(const char *path);
} }

View File

@ -26,4 +26,20 @@ struct ImageProperties
ImageProperties GetImageProperties(char const *filePath); ImageProperties GetImageProperties(char const *filePath);
/**
* Very simple method for mapping filename suffix to mime type. The glTF 2.0 spec only accepts values
* "image/jpeg" and "image/png" so we don't need to get too fancy.
*/
inline std::string suffixToMimeType(std::string suffix) {
std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower);
if (suffix == "jpg" || suffix == "jpeg") {
return "image/jpeg";
}
if (suffix == "png") {
return "image/png";
}
return "image/unknown";
}
#endif // !__IMAGE_UTILS_H__ #endif // !__IMAGE_UTILS_H__

View File

@ -73,6 +73,16 @@ namespace Gltf // TODO replace
return fileName.substr(0, fileName.rfind('.')).c_str(); return fileName.substr(0, fileName.rfind('.')).c_str();
} }
inline const std::string GetFileSuffixString(const std::string &path)
{
const std::string fileName = GetFileNameString(path);
unsigned long pos = fileName.rfind('.');
if (pos == std::string::npos) {
return "";
}
return fileName.substr(++pos);
}
inline int CompareNoCase(const std::string &s1, const std::string &s2) inline 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(), MAX_PATH_LENGTH);