From a984f7bf37a3643e13223a5629966b0a673b4f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Winzell?= Date: Sun, 25 Feb 2018 17:19:19 -0800 Subject: [PATCH 1/2] Drop the Gltf:: namespace prefix. (#77) --- src/Fbx2Raw.cpp | 12 ++-- src/Raw2Gltf.cpp | 20 +++---- src/RawModel.cpp | 4 +- src/main.cpp | 4 +- src/utils/File_Utils.cpp | 14 ++--- src/utils/String_Utils.cpp | 14 ++--- src/utils/String_Utils.h | 118 ++++++++++++++++++------------------- 7 files changed, 90 insertions(+), 96 deletions(-) diff --git a/src/Fbx2Raw.cpp b/src/Fbx2Raw.cpp index edac809..3350fae 100644 --- a/src/Fbx2Raw.cpp +++ b/src/Fbx2Raw.cpp @@ -1319,22 +1319,22 @@ static void ReadAnimations(RawModel &raw, FbxScene *pScene) static std::string GetInferredFileName(const std::string &fbxFileName, const std::string &directory, const std::vector &directoryFileList) { // Get the file name with file extension. - const std::string fileName = Gltf::StringUtils::GetFileNameString(Gltf::StringUtils::GetCleanPathString(fbxFileName)); + const std::string fileName = StringUtils::GetFileNameString(StringUtils::GetCleanPathString(fbxFileName)); // Try to find a match with extension. for (const auto &file : directoryFileList) { - if (Gltf::StringUtils::CompareNoCase(fileName, file) == 0) { + if (StringUtils::CompareNoCase(fileName, file) == 0) { return std::string(directory) + file; } } // Get the file name without file extension. - const std::string fileBase = Gltf::StringUtils::GetFileBaseString(fileName); + const std::string fileBase = StringUtils::GetFileBaseString(fileName); // Try to find a match without file extension. for (const auto &file : directoryFileList) { // If the two extension-less base names match. - if (Gltf::StringUtils::CompareNoCase(fileBase, Gltf::StringUtils::GetFileBaseString(file)) == 0) { + if (StringUtils::CompareNoCase(fileBase, StringUtils::GetFileBaseString(file)) == 0) { // Return the name with extension of the file in the directory. return std::string(directory) + file; } @@ -1357,10 +1357,10 @@ FindFbxTextures( FbxScene *pScene, const char *fbxFileName, const char *extensions, std::map &textureLocations) { // Get the folder the FBX file is in. - const std::string folder = Gltf::StringUtils::GetFolderString(fbxFileName); + const std::string folder = StringUtils::GetFolderString(fbxFileName); // Check if there is a filename.fbm folder to which embedded textures were extracted. - const std::string fbmFolderName = folder + Gltf::StringUtils::GetFileBaseString(fbxFileName) + ".fbm/"; + const std::string fbmFolderName = folder + StringUtils::GetFileBaseString(fbxFileName) + ".fbm/"; // 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; diff --git a/src/Raw2Gltf.cpp b/src/Raw2Gltf.cpp index fd6d742..ecff0f2 100644 --- a/src/Raw2Gltf.cpp +++ b/src/Raw2Gltf.cpp @@ -448,39 +448,39 @@ ModelData *Raw2Gltf( }; int width = -1, height = -1; - std::string mergedName = tag; std::string mergedFilename = tag; std::vector texes { }; for (const int rawTexIx : rawTexIndices) { TexInfo info(rawTexIx); if (rawTexIx >= 0) { - const RawTexture &rawTex = raw.GetTexture(rawTexIx); + const RawTexture &rawTex = raw.GetTexture(rawTexIx); const std::string &fileLoc = rawTex.fileLocation; - const std::string &fileLocBase = Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc)); + const std::string &name = StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc)); if (!fileLoc.empty()) { info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0); if (!info.pixels) { fmt::printf("Warning: merge texture [%d](%s) could not be loaded.\n", rawTexIx, - Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc))); + name); } else { if (width < 0) { width = info.width; height = info.height; } else if (width != info.width || height != info.height) { fmt::printf("Warning: texture %s (%d, %d) can't be merged with previous texture(s) of dimension (%d, %d)\n", - Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc)), + name, info.width, info.height, width, height); // this is bad enough that we abort the whole merge return nullptr; } - mergedName += "_" + rawTex.fileName; - mergedFilename += "_" + fileLocBase; + mergedFilename += "_" + name; } } } texes.push_back(info); } + // at the moment, the best choice of filename is also the best choice of name + const std::string mergedName = mergedFilename; if (width < 0) { // no textures to merge; bail @@ -579,14 +579,14 @@ ModelData *Raw2Gltf( } const RawTexture &rawTexture = raw.GetTexture(rawTexIndex); - const std::string textureName = Gltf::StringUtils::GetFileBaseString(rawTexture.name); - const std::string relativeFilename = Gltf::StringUtils::GetFileNameString(rawTexture.fileLocation); + const std::string textureName = StringUtils::GetFileBaseString(rawTexture.name); + const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation); ImageData *image = nullptr; if (options.outputBinary) { auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation); if (bufferView) { - std::string suffix = Gltf::StringUtils::GetFileSuffixString(rawTexture.fileLocation); + std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); image = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix)); } diff --git a/src/RawModel.cpp b/src/RawModel.cpp index e8b708a..27a10fa 100644 --- a/src/RawModel.cpp +++ b/src/RawModel.cpp @@ -91,7 +91,7 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c return -1; } for (size_t i = 0; i < textures.size(); i++) { - if (Gltf::StringUtils::CompareNoCase(textures[i].name, name) == 0 && textures[i].usage == usage) { + if (StringUtils::CompareNoCase(textures[i].name, name) == 0 && textures[i].usage == usage) { return (int) i; } } @@ -159,7 +159,7 @@ int RawModel::AddMaterial( int RawModel::AddSurface(const RawSurface &surface) { for (size_t i = 0; i < surfaces.size(); i++) { - if (Gltf::StringUtils::CompareNoCase(surfaces[i].name, surface.name) == 0) { + if (StringUtils::CompareNoCase(surfaces[i].name, surface.name) == 0) { return (int) i; } } diff --git a/src/main.cpp b/src/main.cpp index ae86257..595b184 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -197,7 +197,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC. if (options.count("output") == 0) { // if -o is not given, default to the basename of the .fbx - outputPath = "./" + Gltf::StringUtils::GetFileBaseString(inputPath); + outputPath = "./" + StringUtils::GetFileBaseString(inputPath); } std::string outputFolder; // the output folder in .gltf mode, not used for .glb std::string modelPath; // the path of the actual .glb or .gltf file @@ -208,7 +208,7 @@ Copyright (c) 2016-2017 Oculus VR, LLC. } else { // in gltf mode, we create a folder and write into that outputFolder = outputPath + "_out/"; - modelPath = outputFolder + Gltf::StringUtils::GetFileNameString(outputPath) + ".gltf"; + modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf"; } if (!FileUtils::CreatePath(modelPath.c_str())) { fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str()); diff --git a/src/utils/File_Utils.cpp b/src/utils/File_Utils.cpp index 94c10e1..c3ea1d6 100644 --- a/src/utils/File_Utils.cpp +++ b/src/utils/File_Utils.cpp @@ -41,14 +41,14 @@ namespace FileUtils { std::string GetCurrentFolder() { - char cwd[Gltf::StringUtils::MAX_PATH_LENGTH]; + char cwd[StringUtils::MAX_PATH_LENGTH]; if (!_getcwd(cwd, sizeof(cwd))) { return std::string(); } cwd[sizeof(cwd) - 1] = '\0'; - Gltf::StringUtils::GetCleanPath(cwd, cwd, Gltf::StringUtils::PATH_UNIX); + StringUtils::GetCleanPath(cwd, cwd, StringUtils::PATH_UNIX); const size_t length = strlen(cwd); - if (cwd[length - 1] != '/' && length < Gltf::StringUtils::MAX_PATH_LENGTH - 1) { + if (cwd[length - 1] != '/' && length < StringUtils::MAX_PATH_LENGTH - 1) { cwd[length + 0] = '/'; cwd[length + 1] = '\0'; } @@ -155,12 +155,12 @@ namespace FileUtils { bool CreatePath(const char *path) { #if defined( __unix__ ) || defined( __APPLE__ ) - Gltf::StringUtils::PathSeparator separator = Gltf::StringUtils::PATH_UNIX; + StringUtils::PathSeparator separator = StringUtils::PATH_UNIX; #else - Gltf::StringUtils::PathSeparator separator = Gltf::StringUtils::PATH_WIN; + StringUtils::PathSeparator separator = StringUtils::PATH_WIN; #endif - std::string folder = Gltf::StringUtils::GetFolderString(path); - std::string clean = Gltf::StringUtils::GetCleanPathString(folder, separator); + 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) { diff --git a/src/utils/String_Utils.cpp b/src/utils/String_Utils.cpp index 873e9c3..4fb0712 100644 --- a/src/utils/String_Utils.cpp +++ b/src/utils/String_Utils.cpp @@ -9,13 +9,11 @@ #include "String_Utils.h" -namespace Gltf { - namespace StringUtils { - - PathSeparator operator!(const PathSeparator &s) - { - return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN; - } +namespace StringUtils { + PathSeparator operator!(const PathSeparator &s) + { + return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN; } -}// namespace Gltf + +} diff --git a/src/utils/String_Utils.h b/src/utils/String_Utils.h index 322add6..9f8c0ee 100644 --- a/src/utils/String_Utils.h +++ b/src/utils/String_Utils.h @@ -20,76 +20,72 @@ #define strcasecmp _stricmp #endif -namespace Gltf // TODO replace -{ - namespace StringUtils { +namespace StringUtils { - static const unsigned int MAX_PATH_LENGTH = 1024; + static const unsigned int MAX_PATH_LENGTH = 1024; - enum PathSeparator - { - PATH_WIN = '\\', - PATH_UNIX = '/' - }; + enum PathSeparator + { + PATH_WIN = '\\', + PATH_UNIX = '/' + }; - PathSeparator operator!(const PathSeparator &s); + PathSeparator operator!(const PathSeparator &s); - inline const std::string GetCleanPathString(const std::string &path, const PathSeparator separator = PATH_WIN) - { - 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; + inline const std::string GetCleanPathString(const std::string &path, const PathSeparator separator = PATH_WIN) + { + std::string cleanPath = path; + for (size_t s = cleanPath.find(!separator, 0); s != std::string::npos; s = cleanPath.find(!separator, s)) { + cleanPath[s] = separator; } - template - inline 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; - } + return cleanPath; + } + template + inline 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; } + } - inline 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); + inline 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); + } + + inline 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); + } + + inline const std::string GetFileBaseString(const std::string &path) + { + const std::string fileName = GetFileNameString(path); + 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 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); - } + inline int CompareNoCase(const std::string &s1, const std::string &s2) + { + return strncasecmp(s1.c_str(), s2.c_str(), MAX_PATH_LENGTH); + } - inline const std::string GetFileBaseString(const std::string &path) - { - const std::string fileName = GetFileNameString(path); - 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); - } - - } // StringUtils - -}// namespace Gltf +} // StringUtils #endif // _STRING_UTILS_H__ From a8f194f793c9eabc54b474d131f4b0e74e44d694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A4r=20Winzell?= Date: Sun, 25 Feb 2018 17:28:26 -0800 Subject: [PATCH 2/2] better texture transparency test (#78) Previous to this, a PNG that was on RGBA format would cause its corresponding texture to be flagged as transparent. This is very silly. We now iterate over the bytes, and if any is not 255, THEN there's alpha. --- src/Raw2Gltf.cpp | 2 - src/utils/Image_Utils.cpp | 141 ++++++++------------------------------ src/utils/Image_Utils.h | 7 +- 3 files changed, 33 insertions(+), 117 deletions(-) diff --git a/src/Raw2Gltf.cpp b/src/Raw2Gltf.cpp index ecff0f2..a440809 100644 --- a/src/Raw2Gltf.cpp +++ b/src/Raw2Gltf.cpp @@ -12,9 +12,7 @@ #include #include -#define STB_IMAGE_IMPLEMENTATION #include -#define STB_IMAGE_WRITE_IMPLEMENTATION #include #include "FBX2glTF.h" diff --git a/src/utils/Image_Utils.cpp b/src/utils/Image_Utils.cpp index 3966b71..9faf411 100644 --- a/src/utils/Image_Utils.cpp +++ b/src/utils/Image_Utils.cpp @@ -7,131 +7,50 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#include -#include #include -#include -#include -#include -#include "File_Utils.h" +#define STB_IMAGE_IMPLEMENTATION +#include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + #include "Image_Utils.h" -// https://www.w3.org/TR/PNG/#11IHDR -const int PNG_IHDR_CHUNK_START = 8; -const int PNG_HEADER_SIZE = PNG_IHDR_CHUNK_START + 8 + 13; - -enum PNG_ColorType -{ - Grayscale = 0, - RGB = 2, - Palette = 3, - GrayscaleAlpha = 4, - RGBA = 6 -}; - -static bool ImageIsPNG(char const *fileName, unsigned char const *buffer) -{ - if ((buffer[0] == 0x89 && buffer[1] == 0x50 && buffer[2] == 0x4E && buffer[3] == 0x47)) { - // first chunk must be an IHDR - return ( - buffer[PNG_IHDR_CHUNK_START + 4] == 'I' && - buffer[PNG_IHDR_CHUNK_START + 5] == 'H' && - buffer[PNG_IHDR_CHUNK_START + 6] == 'D' && - buffer[PNG_IHDR_CHUNK_START + 7] == 'R'); +static bool imageHasTransparentPixels(FILE *f) { + int width, height, channels; + // RGBA: we have to load the pixels to figure out if the image is fully opaque + uint8_t *pixels = stbi_load_from_file(f, &width, &height, &channels, 0); + if (pixels != nullptr) { + int pixelCount = width * height; + for (int ix = 0; ix < pixelCount; ix ++) { + // test fourth byte (alpha); 255 is 1.0 + if (pixels[4*ix + 3] != 255) { + return true; + } + } } return false; } -static ImageProperties PNGProperties(unsigned char const *buffer) -{ - // Extract (big-endian) properties from the PNG IHDR - ImageProperties properties; - properties.width = - (buffer[PNG_IHDR_CHUNK_START + 8] & 0xFF) << 24 | (buffer[PNG_IHDR_CHUNK_START + 9] & 0xFF) << 16 | - (buffer[PNG_IHDR_CHUNK_START + 10] & 0xFF) << 8 | (buffer[PNG_IHDR_CHUNK_START + 11] & 0xFF); - properties.height = - (buffer[PNG_IHDR_CHUNK_START + 12] & 0xFF) << 24 | (buffer[PNG_IHDR_CHUNK_START + 13] & 0xFF) << 16 | - (buffer[PNG_IHDR_CHUNK_START + 14] & 0xFF) << 8 | (buffer[PNG_IHDR_CHUNK_START + 15] & 0xFF); - properties.occlusion = (buffer[PNG_IHDR_CHUNK_START + 17] == RGBA) ? IMAGE_TRANSPARENT : IMAGE_OPAQUE; - - return properties; -} - -// header is broken into multiple structs because TGA headers are packed -struct TGA_HeaderStart_t -{ - char IDLength; - char ColorMapType; - char DataTypeCode; -}; - -struct TGA_HeaderColor_t -{ - short int ColorMapOrigin; - short int ColorMapLength; - char ColorMapDepth; -}; - -struct TGA_HeaderOrigin_t -{ - short int XOrigin; - short int YOrigin; - short Width; - short Height; - char BitsPerPixel; - char ImageDescriptor; -}; - -const int TGA_HEADER_SIZE = 8 + sizeof(TGA_HeaderOrigin_t); - -static bool ImageIsTGA(char const *fileName, unsigned char const *buffer) -{ - // TGA's have pretty ambiguous header so we simply check their file extension - size_t len = strlen(fileName); - if (len < 4) { - return false; - } -#if defined( __unix__ ) || defined( __APPLE__ ) - return strcasecmp(fileName + len - 4, ".tga") == 0; -#else - return _stricmp( fileName + len - 4, ".tga" ) == 0; -#endif -} - -static ImageProperties TGAProperties(unsigned char const *buffer) -{ - const TGA_HeaderOrigin_t *header = reinterpret_cast< const TGA_HeaderOrigin_t * >( &(buffer[8])); - - ImageProperties properties; - properties.width = header->Width; - properties.height = header->Height; - properties.occlusion = (header->BitsPerPixel == 32) ? IMAGE_TRANSPARENT : IMAGE_OPAQUE; - - return properties; -} - ImageProperties GetImageProperties(char const *filePath) { - ImageProperties defaultProperties; - defaultProperties.width = 1; - defaultProperties.height = 1; - defaultProperties.occlusion = IMAGE_OPAQUE; + ImageProperties result = { + 1, + 1, + IMAGE_OPAQUE, + }; FILE *f = fopen(filePath, "rb"); if (f == nullptr) { - return defaultProperties; + return result; } - // This assumes every image file is at least as large as the largest header. - const int maxHeaderSize = std::max(PNG_HEADER_SIZE, TGA_HEADER_SIZE); - unsigned char buffer[maxHeaderSize]; - if (fread(buffer, (size_t) maxHeaderSize, 1, f) == 1) { - if (ImageIsPNG(filePath, buffer)) { - return PNGProperties(buffer); - } else if (ImageIsTGA(filePath, buffer)) { - return TGAProperties(buffer); - } + int channels; + int success = stbi_info_from_file(f, &result.width, &result.height, &channels); + + if (success && channels == 4 && imageHasTransparentPixels(f)) { + result.occlusion = IMAGE_TRANSPARENT; } - return defaultProperties; + return result; } + diff --git a/src/utils/Image_Utils.h b/src/utils/Image_Utils.h index 48cd6fb..e6890d2 100644 --- a/src/utils/Image_Utils.h +++ b/src/utils/Image_Utils.h @@ -13,15 +13,14 @@ enum ImageOcclusion { IMAGE_OPAQUE, - IMAGE_PERFORATED, IMAGE_TRANSPARENT }; struct ImageProperties { - int width = 0; - int height = 0; - ImageOcclusion occlusion = IMAGE_OPAQUE; + int width; + int height; + ImageOcclusion occlusion; }; ImageProperties GetImageProperties(char const *filePath);