diff --git a/CMakeLists.txt b/CMakeLists.txt index b4e434b..c1cf0be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") "Hint: mkdir -p build; cmake -H. -Bbuild; make -Cbuild\n") endif () -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") include(ExternalProject) @@ -35,6 +35,13 @@ find_package(Iconv QUIET) # create a compilation database for e.g. clang-tidy 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) # this will suffice for now; don't really care about 32-bit set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR}) @@ -190,7 +197,6 @@ set(LIB_SOURCE_FILES src/utils/File_Utils.hpp src/utils/Image_Utils.cpp src/utils/Image_Utils.hpp - src/utils/String_Utils.cpp src/utils/String_Utils.hpp third_party/CLI11/CLI11.hpp ) @@ -237,6 +243,12 @@ if (WIN32) optimized ${ZLIB_LIBRARIES} 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() target_link_libraries(libFBX2glTF ${LIBXML2_LIBRARIES} diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index 270fcd7..a689b93 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -13,13 +13,6 @@ #include #include -#if defined(__unix__) || defined(__APPLE__) - -#include - -#define _stricmp strcasecmp -#endif - #include #include "FBX2glTF.h" @@ -271,10 +264,7 @@ int main(int argc, char* argv[]) { if (outputPath.empty()) { // if -o is not given, default to the basename of the .fbx - outputPath = fmt::format( - ".{}{}", - (const char)StringUtils::GetPathSeparator(), - StringUtils::GetFileBaseString(inputPath)); + outputPath = "./" + FileUtils::GetFileBaseString(inputPath); } // the output folder in .gltf mode, not used for .glb std::string outputFolder; @@ -282,14 +272,17 @@ int main(int argc, char* argv[]) { // the path of the actual .glb or .gltf file std::string modelPath; if (gltfOptions.outputBinary) { - // in binary mode, we write precisely where we're asked - modelPath = outputPath + ".glb"; - + const auto& suffix = FileUtils::GetFileSuffix(outputPath); + // 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 { // in gltf mode, we create a folder and write into that - outputFolder = - fmt::format("{}_out{}", outputPath.c_str(), (const char)StringUtils::GetPathSeparator()); - modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf"; + outputFolder = fmt::format("{}_out/", outputPath.c_str()); + modelPath = outputFolder + FileUtils::GetFileNameString(outputPath) + ".gltf"; } if (!FileUtils::CreatePath(modelPath.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) { 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); return 1; } diff --git a/src/FBX2glTF.h b/src/FBX2glTF.h index 5d3f07a..67a3225 100644 --- a/src/FBX2glTF.h +++ b/src/FBX2glTF.h @@ -40,6 +40,15 @@ using json = nlohmann::basic_json; 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(n); +} + /** * The variuos situations in which the user may wish for us to (re-)compute normals for our vertices. */ diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 9a11bdd..d99f2f7 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -925,7 +925,7 @@ static std::string GetInferredFileName( } // Get the file name with file extension. const std::string fileName = - StringUtils::GetFileNameString(StringUtils::GetCleanPathString(fbxFileName)); + FileUtils::GetFileNameString(FileUtils::GetCanonicalPath(fbxFileName)); // Try to find a match with extension. for (const auto& file : directoryFileList) { @@ -935,12 +935,12 @@ static std::string GetInferredFileName( } // 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. for (const auto& file : directoryFileList) { // 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 std::string(directory) + file; } @@ -960,14 +960,14 @@ static std::string GetInferredFileName( */ static void FindFbxTextures( FbxScene* pScene, - const char* fbxFileName, - const char* extensions, + const std::string fbxFileName, + const std::set& extensions, std::map& textureLocations) { // 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. - 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. 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& textureExtensions) { FbxManager* pManager = FbxManager::Create(); FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT); pManager->SetIOSettings(pIoSettings); FbxImporter* pImporter = FbxImporter::Create(pManager, ""); - if (!pImporter->Initialize(fbxFileName, -1, pManager->GetIOSettings())) { + if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) { if (verboseOutput) { fmt::printf("%s\n", pImporter->GetStatus().GetErrorString()); } diff --git a/src/fbx/Fbx2Raw.hpp b/src/fbx/Fbx2Raw.hpp index a5fd231..7933af1 100644 --- a/src/fbx/Fbx2Raw.hpp +++ b/src/fbx/Fbx2Raw.hpp @@ -11,6 +11,9 @@ #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& textureExtensions); json TranscribeProperty(FbxProperty& prop); \ No newline at end of file diff --git a/src/fbx/FbxBlendShapesAccess.hpp b/src/fbx/FbxBlendShapesAccess.hpp index fc99ec9..85fd32d 100644 --- a/src/fbx/FbxBlendShapesAccess.hpp +++ b/src/fbx/FbxBlendShapesAccess.hpp @@ -95,7 +95,7 @@ class FbxBlendShapesAccess { } FbxAnimCurve* GetAnimation(size_t channelIx, size_t animIx) const { - return channels.at(channelIx).ExtractAnimation(animIx); + return channels.at(channelIx).ExtractAnimation(to_uint32(animIx)); } private: diff --git a/src/fbx/FbxSkinningAccess.hpp b/src/fbx/FbxSkinningAccess.hpp index d2b42d8..d36c04f 100644 --- a/src/fbx/FbxSkinningAccess.hpp +++ b/src/fbx/FbxSkinningAccess.hpp @@ -37,7 +37,7 @@ class FbxSkinningAccess { return jointNodes[jointIndex]; } - const long GetJointId(const int jointIndex) const { + const uint64_t GetJointId(const int jointIndex) const { return jointIds[jointIndex]; } @@ -49,7 +49,7 @@ class FbxSkinningAccess { return jointInverseGlobalTransforms[jointIndex]; } - const long GetRootNode() const { + const uint64_t GetRootNode() const { assert(rootIndex != -1); return jointIds[rootIndex]; } @@ -70,7 +70,7 @@ class FbxSkinningAccess { private: int rootIndex; - std::vector jointIds; + std::vector jointIds; std::vector jointNodes; std::vector jointSkinningTransforms; std::vector jointInverseGlobalTransforms; diff --git a/src/gltf/GltfModel.cpp b/src/gltf/GltfModel.cpp index c3c6524..7bf53ba 100644 --- a/src/gltf/GltfModel.cpp +++ b/src/gltf/GltfModel.cpp @@ -12,7 +12,7 @@ std::shared_ptr GltfModel::GetAlignedBufferView( BufferData& buffer, const BufferViewData::GL_ArrayType target) { - unsigned long bufferSize = this->binary->size(); + uint32_t bufferSize = to_uint32(this->binary->size()); if ((bufferSize % 4) > 0) { bufferSize += (4 - (bufferSize % 4)); this->binary->resize(bufferSize); @@ -27,7 +27,7 @@ GltfModel::AddRawBufferView(BufferData& buffer, const char* source, uint32_t byt bufferView->byteLength = bytes; // 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); // and copy them into place @@ -52,7 +52,7 @@ std::shared_ptr GltfModel::AddBufferViewForFile( std::vector fileBuffer(size); if (file.read(fileBuffer.data(), size)) { - result = AddRawBufferView(buffer, fileBuffer.data(), size); + result = AddRawBufferView(buffer, fileBuffer.data(), to_uint32(size)); } else { fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename); } diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index f82b09f..7c7d52c 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -44,7 +44,7 @@ template class Holder { public: std::shared_ptr hold(T* ptr) { - ptr->ix = ptrs.size(); + ptr->ix = to_uint32(ptrs.size()); ptrs.emplace_back(ptr); return ptrs.back(); } @@ -114,7 +114,7 @@ class GltfModel { primitive.AddDracoAttrib(attrDef, attribArr); accessor = accessors.hold(new AccessorData(attrDef.glType)); - accessor->count = attribArr.size(); + accessor->count = to_uint32(attribArr.size()); } else { auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 84ec82b..e081311 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -256,29 +256,28 @@ ModelData* Raw2Gltf( if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) { /** * 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(); - // 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 bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0; bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0; bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0; bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap) : (hasRoughnessMap && hasMetallicMap); - if (atLeastTwoMaps) { - // if there's at least two of metallic/roughness/occlusion, it makes sense to - // merge them: occlusion into the red channel, metallic into blue channel, and - // roughness into the green. + if (!atLeastTwoMaps) { + // this handles the case of 0 or 1 maps supplied + aoMetRoughTex = hasMetallicMap + ? 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( { material.textures[RAW_TEXTURE_USAGE_OCCLUSION], @@ -289,27 +288,24 @@ ModelData* Raw2Gltf( [&](const std::vector pixels) -> TextureBuilder::pixel { const float occlusion = (*pixels[0])[0]; - const float metallic = (*pixels[1])[0]; - const float roughness = (*pixels[2])[0]; + const float metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic); + const float roughness = + (*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness); return {{occlusion, props->invertRoughnessMap ? 1.0f - roughness : roughness, metallic, 1}}; }, 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 { /** * Traditional FBX Material -> PBR Met/Rough glTF. @@ -393,7 +389,6 @@ ModelData* Raw2Gltf( khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial()); } - // after all the special cases have had a go, check if we need to look up occlusion map if (!occlusionTexture) { occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get(); } @@ -447,11 +442,11 @@ ModelData* Raw2Gltf( std::shared_ptr primitive; if (options.draco.enabled) { - int triangleCount = surfaceModel.GetTriangleCount(); + size_t triangleCount = surfaceModel.GetTriangleCount(); // initialize Draco mesh with vertex index information auto dracoMesh(std::make_shared()); - dracoMesh->SetNumFaces(static_cast(triangleCount)); + dracoMesh->SetNumFaces(triangleCount); dracoMesh->set_num_points(surfaceModel.GetVertexCount()); for (uint32_t ii = 0; ii < triangleCount; ii++) { @@ -464,7 +459,7 @@ ModelData* Raw2Gltf( AccessorData& indexes = *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)); } else { const AccessorData& indexes = *gltf->AddAccessorWithView( @@ -499,11 +494,13 @@ ModelData* Raw2Gltf( GLT_VEC3F, draco::GeometryAttribute::NORMAL, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_NORMAL); + const auto _ = + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_NORMAL); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) { const AttributeDefinition ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TANGENT); + const auto _ = gltf->AddAttributeToPrimitive( + buffer, surfaceModel, *primitive, ATTR_TANGENT); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { const AttributeDefinition ATTR_COLOR( @@ -512,7 +509,8 @@ ModelData* Raw2Gltf( GLT_VEC4F, draco::GeometryAttribute::COLOR, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_COLOR); + const auto _ = + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_COLOR); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { const AttributeDefinition ATTR_TEXCOORD_0( @@ -521,7 +519,8 @@ ModelData* Raw2Gltf( GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0); + const auto _ = gltf->AddAttributeToPrimitive( + buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { const AttributeDefinition ATTR_TEXCOORD_1( @@ -530,7 +529,8 @@ ModelData* Raw2Gltf( GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); + const auto _ = gltf->AddAttributeToPrimitive( + buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) { const AttributeDefinition ATTR_JOINTS( @@ -539,7 +539,8 @@ ModelData* Raw2Gltf( GLT_VEC4I, draco::GeometryAttribute::GENERIC, draco::DT_UINT16); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); + const auto _ = + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); } if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) { const AttributeDefinition ATTR_WEIGHTS( @@ -548,7 +549,8 @@ ModelData* Raw2Gltf( GLT_VEC4F, draco::GeometryAttribute::GENERIC, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); + const auto _ = + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); } // 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); 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); } mesh->AddPrimitive(primitive); @@ -735,7 +737,7 @@ ModelData* Raw2Gltf( type = LightData::Type::Spot; break; } - gltf->lights.hold(new LightData( + const auto _ = gltf->lights.hold(new LightData( light.name, type, light.color, @@ -842,13 +844,13 @@ ModelData* Raw2Gltf( gltfOutStream.write(glb2BinaryHeader, 8); // 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); while ((binaryLength % 4) != 0) { gltfOutStream.put('\0'); binaryLength++; } - uint32_t totalLength = (uint32_t)gltfOutStream.tellp(); + uint32_t totalLength = to_uint32(gltfOutStream.tellp()); // seek back to sub-header for json chunk gltfOutStream.seekp(8); diff --git a/src/gltf/Raw2Gltf.hpp b/src/gltf/Raw2Gltf.hpp index 0dbdc6e..cd44b41 100644 --- a/src/gltf/Raw2Gltf.hpp +++ b/src/gltf/Raw2Gltf.hpp @@ -120,7 +120,7 @@ const GLType GLT_QUATF = {CT_FLOAT, 4, "VEC4"}; * The base of any indexed glTF entity. */ struct Holdable { - uint32_t ix; + uint32_t ix = UINT_MAX; virtual json serialize() const = 0; }; diff --git a/src/gltf/TextureBuilder.cpp b/src/gltf/TextureBuilder.cpp index 4329c5f..2e8105e 100644 --- a/src/gltf/TextureBuilder.cpp +++ b/src/gltf/TextureBuilder.cpp @@ -49,8 +49,7 @@ std::shared_ptr TextureBuilder::combine( if (rawTexIx >= 0) { const RawTexture& rawTex = raw.GetTexture(rawTexIx); const std::string& fileLoc = rawTex.fileLocation; - const std::string& name = - StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc)); + const std::string& name = FileUtils::GetFileBaseString(FileUtils::GetFileNameString(fileLoc)); if (!fileLoc.empty()) { info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0); if (!info.pixels) { @@ -142,7 +141,7 @@ std::shared_ptr TextureBuilder::combine( ImageData* image; if (options.outputBinary) { 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"); } else { const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg"); @@ -180,20 +179,30 @@ std::shared_ptr TextureBuilder::simple(int rawTexIndex, const std:: } const RawTexture& rawTexture = raw.GetTexture(rawTexIndex); - const std::string textureName = StringUtils::GetFileBaseString(rawTexture.name); - const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation); + const std::string textureName = FileUtils::GetFileBaseString(rawTexture.name); + const std::string relativeFilename = FileUtils::GetFileNameString(rawTexture.fileLocation); ImageData* image = nullptr; if (options.outputBinary) { auto bufferView = gltf.AddBufferViewForFile(*gltf.defaultBuffer, rawTexture.fileLocation); if (bufferView) { - std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); - image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix)); + const auto& suffix = FileUtils::GetFileSuffix(rawTexture.fileLocation); + 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()) { 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 (verboseOutput) { fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath); diff --git a/src/gltf/properties/AnimationData.cpp b/src/gltf/properties/AnimationData.cpp index a1dd09b..a2fcced 100644 --- a/src/gltf/properties/AnimationData.cpp +++ b/src/gltf/properties/AnimationData.cpp @@ -24,7 +24,7 @@ void AnimationData::AddNodeChannel( const AccessorData& accessor, std::string path) { 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))); samplers.emplace_back(sampler_t(timeAccessor, accessor.ix)); } diff --git a/src/gltf/properties/MaterialData.cpp b/src/gltf/properties/MaterialData.cpp index 8ac6bfa..c1697b3 100644 --- a/src/gltf/properties/MaterialData.cpp +++ b/src/gltf/properties/MaterialData.cpp @@ -59,13 +59,15 @@ void to_json(json& j, const PBRMetallicRoughness& d) { if (d.baseColorFactor.LengthSquared() > 0) { 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) { 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; } } diff --git a/src/gltf/properties/PrimitiveData.hpp b/src/gltf/properties/PrimitiveData.hpp index 16bf7e9..3153229 100644 --- a/src/gltf/properties/PrimitiveData.hpp +++ b/src/gltf/properties/PrimitiveData.hpp @@ -49,7 +49,7 @@ struct PrimitiveData { componentCount * draco::DataTypeLength(attribute.dracoComponentType), 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); std::vector buf(sizeof(T)); diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index cafc076..9ddebf7 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -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++) { if (nodes[i].id == nodeId) { return (int)i; @@ -641,7 +641,7 @@ int RawModel::GetNodeById(const long nodeId) const { 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++) { if (surfaces[i].id == surfaceId) { return (int)i; diff --git a/src/utils/File_Utils.cpp b/src/utils/File_Utils.cpp index 4c044fe..aa3ffa8 100644 --- a/src/utils/File_Utils.cpp +++ b/src/utils/File_Utils.cpp @@ -10,171 +10,41 @@ #include "File_Utils.hpp" #include +#include #include #include #include #include -#if defined(__unix__) || defined(__APPLE__) - -#include -#include -#include -#include -#include - -#define _getcwd getcwd -#define _mkdir(a) mkdir(a, 0777) -#elif defined(_WIN32) -#include -#include -#else -#include -#include -#endif - -#include - #include "FBX2glTF.h" #include "String_Utils.hpp" namespace FileUtils { -std::string GetCurrentFolder() { - char cwd[StringUtils::MAX_PATH_LENGTH]; - if (!_getcwd(cwd, sizeof(cwd))) { - 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 ListFolderFiles(const char* folder, const char* matchExtensions) { +std::vector ListFolderFiles( + const std::string folder, + const std::set& matchExtensions) { std::vector fileList; -#if defined(__unix__) || defined(__APPLE__) - DIR* dir = opendir(strlen(folder) > 0 ? folder : "."); - if (dir != nullptr) { - for (;;) { - 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); + for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(folder)) { + const std::filesystem::path& path = entry.path(); + if (matchExtensions.find(path.extension().string()) != matchExtensions.end()) { + fileList.push_back(path.string()); } - - 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; } -bool CreatePath(const char* path) { -#if defined(__unix__) || defined(__APPLE__) - StringUtils::PathSeparator separator = StringUtils::PATH_UNIX; -#else - StringUtils::PathSeparator separator = StringUtils::PATH_WIN; -#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]; +bool CreatePath(const std::string path) { + const auto& parent = std::filesystem::path(path).parent_path(); + if (parent.empty()) { + // this is either CWD or std::filesystem root; either way it exists + return true; } - 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) { diff --git a/src/utils/File_Utils.hpp b/src/utils/File_Utils.hpp index 0f9a702..363115e 100644 --- a/src/utils/File_Utils.hpp +++ b/src/utils/File_Utils.hpp @@ -9,6 +9,10 @@ #pragma once +#include + +#include +#include #include #include @@ -19,13 +23,51 @@ std::string GetCurrentFolder(); bool FileExists(const std::string& folderPath); bool FolderExists(const std::string& folderPath); -bool MatchExtension(const char* fileExtension, const char* matchExtensions); -std::vector ListFolderFiles(const char* folder, const char* matchExtensions); +std::vector ListFolderFiles( + const std::string folder, + const std::set& matchExtensions); -bool CreatePath(const char* path); +bool CreatePath(std::string path); bool CopyFile( const std::string& srcFilename, const std::string& dstFilename, 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 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 diff --git a/src/utils/String_Utils.cpp b/src/utils/String_Utils.cpp index 8cf8063..7e0dd2f 100644 --- a/src/utils/String_Utils.cpp +++ b/src/utils/String_Utils.cpp @@ -9,72 +9,12 @@ #include "String_Utils.hpp" +#include + 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) { - 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 diff --git a/src/utils/String_Utils.hpp b/src/utils/String_Utils.hpp index f1a3a65..d7691d8 100644 --- a/src/utils/String_Utils.hpp +++ b/src/utils/String_Utils.hpp @@ -21,34 +21,8 @@ namespace StringUtils { -static const unsigned int MAX_PATH_LENGTH = 1024; - -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 -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 int CompareNoCase(const std::string& s1, const std::string& s2) { + return strncasecmp(s1.c_str(), s2.c_str(), std::max(s1.length(), s2.length())); } -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