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:
parent
946f12361c
commit
8cf7f446b7
|
@ -106,7 +106,7 @@ class FbxMaterialAccess
|
|||
|
||||
private:
|
||||
const FbxSurfaceMaterial *fbxMaterial;
|
||||
const std::map<const FbxTexture *, FbxString> &textureNames;
|
||||
const std::map<const FbxTexture *, FbxString> &textureLocations;
|
||||
|
||||
public:
|
||||
const FbxString name;
|
||||
|
@ -119,7 +119,7 @@ public:
|
|||
fbxMaterial(fbxMaterial),
|
||||
name(fbxMaterial->GetName()),
|
||||
shadingModel(fbxMaterial->ShadingModel),
|
||||
textureNames(textureNames),
|
||||
textureLocations(textureNames),
|
||||
props(extractTextures())
|
||||
{}
|
||||
|
||||
|
@ -180,7 +180,7 @@ public:
|
|||
|
||||
FbxDouble val(0);
|
||||
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
||||
if (tex != nullptr && textureNames.find(tex) == textureNames.end()) {
|
||||
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
||||
tex = nullptr;
|
||||
}
|
||||
if (tex == nullptr && prop.IsValid()) {
|
||||
|
@ -195,7 +195,7 @@ public:
|
|||
|
||||
FbxDouble3 val(1, 1, 1);
|
||||
FbxFileTexture *tex = prop.GetSrcObject<FbxFileTexture>();
|
||||
if (tex != nullptr && textureNames.find(tex) == textureNames.end()) {
|
||||
if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) {
|
||||
tex = nullptr;
|
||||
}
|
||||
if (tex == nullptr && prop.IsValid()) {
|
||||
|
@ -213,14 +213,14 @@ public:
|
|||
FbxDouble factorVal(1);
|
||||
|
||||
FbxFileTexture *colTex = colProp.GetSrcObject<FbxFileTexture>();
|
||||
if (colTex != nullptr && textureNames.find(colTex) == textureNames.end()) {
|
||||
if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) {
|
||||
colTex = nullptr;
|
||||
}
|
||||
if (colTex == nullptr && colProp.IsValid()) {
|
||||
colorVal = colProp.Get<FbxDouble3>();
|
||||
}
|
||||
FbxFileTexture *facTex = facProp.GetSrcObject<FbxFileTexture>();
|
||||
if (facTex != nullptr && textureNames.find(facTex) == textureNames.end()) {
|
||||
if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) {
|
||||
facTex = nullptr;
|
||||
}
|
||||
if (facTex == nullptr && facProp.IsValid()) {
|
||||
|
@ -240,7 +240,7 @@ class FbxMaterialsAccess
|
|||
{
|
||||
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),
|
||||
mesh(nullptr),
|
||||
indices(nullptr)
|
||||
|
@ -272,7 +272,7 @@ public:
|
|||
if (summary == nullptr) {
|
||||
summary = summaries[materialNum] = std::make_shared<FbxMaterialAccess>(
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
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> uvLayer1(pMesh->GetElementUV(1), pMesh->GetElementUVCount());
|
||||
const FbxSkinningAccess skinning(pMesh, pScene, pNode);
|
||||
const FbxMaterialsAccess materials(pMesh, textureNames);
|
||||
const FbxMaterialsAccess materials(pMesh, textureLocations);
|
||||
|
||||
if (verboseOutput) {
|
||||
fmt::printf(
|
||||
|
@ -562,9 +562,9 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
|||
|
||||
const auto maybeAddTexture = [&](FbxFileTexture *tex, RawTextureUsage usage) {
|
||||
if (tex != nullptr) {
|
||||
// dig out the inferred filename from the textureNames map
|
||||
const char *inferredPath = textureNames.find(tex)->second;
|
||||
textures[usage] = raw.AddTexture(tex->GetName(), inferredPath, usage);
|
||||
// dig out the inferred filename from the textureLocations map
|
||||
FbxString inferredPath = textureLocations.find(tex)->second;
|
||||
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()) {
|
||||
return;
|
||||
|
@ -723,7 +724,7 @@ static void ReadNodeAttributes(RawModel &raw, FbxScene *pScene, FbxNode *pNode,
|
|||
case FbxNodeAttribute::eNurbsSurface:
|
||||
case FbxNodeAttribute::eTrimNurbsSurface:
|
||||
case FbxNodeAttribute::ePatch: {
|
||||
ReadMesh(raw, pScene, pNode, textureNames);
|
||||
ReadMesh(raw, pScene, pNode, textureLocations);
|
||||
break;
|
||||
}
|
||||
case FbxNodeAttribute::eCamera: {
|
||||
|
@ -752,7 +753,7 @@ static void ReadNodeAttributes(RawModel &raw, FbxScene *pScene, FbxNode *pNode,
|
|||
}
|
||||
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
for (const auto &file : directoryFileList) {
|
||||
const std::string listedFileBase = Gltf::StringUtils::GetFileBaseString(file.c_str());
|
||||
|
||||
// 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 std::string(directory) + file;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the original file with extension
|
||||
return fbxFileName;
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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.
|
||||
*/
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
std::vector<std::string> fileList;
|
||||
FileUtils::ListFolderFiles(fileList, searchFolder, extensions);
|
||||
std::vector<std::string> fileList = FileUtils::ListFolderFiles(searchFolder.c_str(), extensions);
|
||||
|
||||
// Try to match the FBX texture names with the actual files on disk.
|
||||
for (int i = 0; i < pScene->GetTextureCount(); i++) {
|
||||
const FbxTexture *pTexture = pScene->GetTexture(i);
|
||||
const FbxFileTexture *pFileTexture = FbxCast<FbxFileTexture>(pTexture);
|
||||
const FbxFileTexture *pFileTexture = FbxCast<FbxFileTexture>(pScene->GetTexture(i));
|
||||
if (pFileTexture == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const FbxString name = GetInferredFileName(pFileTexture->GetFileName(), searchFolder, fileList).c_str();
|
||||
textureNames.emplace(pTexture, name);
|
||||
const std::string inferredName = GetInferredFileName(pFileTexture->GetFileName(), searchFolder, fileList);
|
||||
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;
|
||||
}
|
||||
|
||||
std::map<const FbxTexture *, FbxString> textureNames;
|
||||
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureNames);
|
||||
std::map<const FbxTexture *, FbxString> textureLocations;
|
||||
FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations);
|
||||
|
||||
// Use Y up for glTF
|
||||
FbxAxisSystem::MayaYUp.ConvertScene(pScene);
|
||||
|
@ -1054,7 +1053,7 @@ bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExte
|
|||
}
|
||||
|
||||
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), "", "");
|
||||
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureNames);
|
||||
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
|
||||
ReadAnimations(raw, pScene);
|
||||
|
||||
pScene->Destroy();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "FBX2glTF.h"
|
||||
#include "utils/String_Utils.h"
|
||||
#include "utils/Image_Utils.h"
|
||||
#include "RawModel.h"
|
||||
#include "Raw2Gltf.h"
|
||||
|
||||
|
@ -362,25 +363,25 @@ ModelData *Raw2Gltf(
|
|||
for (int textureIndex = 0; textureIndex < raw.GetTextureCount(); textureIndex++) {
|
||||
const RawTexture &texture = raw.GetTexture(textureIndex);
|
||||
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;
|
||||
if (options.outputBinary) {
|
||||
auto bufferView = gltf->AddBufferViewForFile(buffer, texFilename);
|
||||
auto bufferView = gltf->AddBufferViewForFile(buffer, texture.fileLocation);
|
||||
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 {
|
||||
// TODO: don't add .ktx here; try to work out a reasonable relative path.
|
||||
source = new ImageData(textureName, textureName + ".ktx");
|
||||
source = new ImageData(relativeFilename, relativeFilename);
|
||||
}
|
||||
if (!source) {
|
||||
// fallback is tiny transparent gif
|
||||
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)));
|
||||
assert(texDat.ix == textureIndex);
|
||||
}
|
||||
|
|
|
@ -84,9 +84,9 @@ int RawModel::AddTriangle(const int v0, const int v1, const int v2, const int ma
|
|||
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;
|
||||
}
|
||||
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;
|
||||
texture.name = name;
|
||||
|
@ -106,6 +106,7 @@ int RawModel::AddTexture(const char *name, const char *fileName, const RawTextur
|
|||
texture.occlusion = (properties.occlusion == IMAGE_TRANSPARENT) ?
|
||||
RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE;
|
||||
texture.fileName = fileName;
|
||||
texture.fileLocation = fileLocation;
|
||||
textures.emplace_back(texture);
|
||||
return (int) textures.size() - 1;
|
||||
}
|
||||
|
@ -313,7 +314,7 @@ void RawModel::Condense()
|
|||
for (int j = 0; j < RAW_TEXTURE_USAGE_MAX; j++) {
|
||||
if (material.textures[j] >= 0) {
|
||||
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;
|
||||
material.textures[j] = textureIndex;
|
||||
}
|
||||
|
|
|
@ -122,13 +122,14 @@ enum RawTextureOcclusion
|
|||
|
||||
struct RawTexture
|
||||
{
|
||||
std::string name;
|
||||
std::string name; // logical name in FBX file
|
||||
int width;
|
||||
int height;
|
||||
int mipLevels;
|
||||
RawTextureUsage usage;
|
||||
RawTextureOcclusion occlusion;
|
||||
std::string fileName;
|
||||
std::string fileName; // original filename in FBX file
|
||||
std::string fileLocation; // inferred path in local filesystem, or ""
|
||||
};
|
||||
|
||||
enum RawMaterialType
|
||||
|
@ -233,7 +234,7 @@ public:
|
|||
void AddVertexAttribute(const RawVertexAttribute attrib);
|
||||
int AddVertex(const RawVertex &vertex);
|
||||
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 char *name, const char *shadingModel, RawMaterialType materialType,
|
||||
|
|
|
@ -31,8 +31,9 @@ ImageData::ImageData(std::string name, const BufferViewData &bufferView, std::st
|
|||
|
||||
json ImageData::serialize() const
|
||||
{
|
||||
if (mimeType.empty()) {
|
||||
if (bufferView < 0) {
|
||||
return {
|
||||
{ "name", name },
|
||||
{ "uri", uri }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC.
|
|||
if (verboseOutput) {
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -54,17 +54,17 @@ namespace FileUtils {
|
|||
return std::string(cwd);
|
||||
}
|
||||
|
||||
bool FolderExists(const char *folderPath)
|
||||
bool FolderExists(const std::string &folderPath)
|
||||
{
|
||||
#if defined( __unix__ ) || defined( __APPLE__ )
|
||||
DIR *dir = opendir(folderPath);
|
||||
DIR *dir = opendir(folderPath.c_str());
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
const DWORD ftyp = GetFileAttributesA( folderPath );
|
||||
const DWORD ftyp = GetFileAttributesA( folderPath.c_str() );
|
||||
if ( ftyp == INVALID_FILE_ATTRIBUTES )
|
||||
{
|
||||
return false; // bad path
|
||||
|
@ -97,13 +97,12 @@ namespace FileUtils {
|
|||
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__ )
|
||||
DIR *dir = opendir(folder);
|
||||
if (dir == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (dir != nullptr) {
|
||||
for (;;) {
|
||||
struct dirent *dp = readdir(dir);
|
||||
if (dp == nullptr) {
|
||||
|
@ -125,6 +124,7 @@ namespace FileUtils {
|
|||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
#else
|
||||
std::string pathStr = folder;
|
||||
pathStr += "*";
|
||||
|
@ -148,6 +148,7 @@ namespace FileUtils {
|
|||
FindClose( hFind );
|
||||
}
|
||||
#endif
|
||||
return fileList;
|
||||
}
|
||||
|
||||
bool CreatePath(const char *path)
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
namespace FileUtils {
|
||||
std::string GetCurrentFolder();
|
||||
|
||||
bool FolderExists(const char *folderPath);
|
||||
bool FolderExists(const std::string &folderPath);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -26,4 +26,20 @@ struct ImageProperties
|
|||
|
||||
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__
|
||||
|
|
|
@ -73,6 +73,16 @@ namespace Gltf // TODO replace
|
|||
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)
|
||||
{
|
||||
return strncasecmp(s1.c_str(), s2.c_str(), MAX_PATH_LENGTH);
|
||||
|
|
Loading…
Reference in New Issue