Move to C++17 and std::filesystem.
With this, we are able to get rid of all the increasingly broken file system utility code, and trust std::filesystem to handle all the cross- platform complexity. Unfortunately std::filesystem support remains a little elusive; it is well supported in Visual Studio (especially 2019), but not by default in Mac OS X, and even in GCC 8.0 it requires an explicit '-l c++fs'. This also silences some of the more egregious compiler warnings, mostly by being explicit about where we cast to the uint32 types glTF prefers.
This commit is contained in:
parent
769454e964
commit
95063ba9f1
|
@ -7,7 +7,7 @@ if ("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}")
|
||||||
"Hint: mkdir -p build; cmake -H. -Bbuild; make -Cbuild\n")
|
"Hint: mkdir -p build; cmake -H. -Bbuild; make -Cbuild\n")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
include(ExternalProject)
|
include(ExternalProject)
|
||||||
|
@ -35,6 +35,13 @@ find_package(Iconv QUIET)
|
||||||
# create a compilation database for e.g. clang-tidy
|
# create a compilation database for e.g. clang-tidy
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
add_compile_definitions(
|
||||||
|
_CRT_SECURE_NO_WARNINGS
|
||||||
|
_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# this will suffice for now; don't really care about 32-bit
|
# this will suffice for now; don't really care about 32-bit
|
||||||
set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR})
|
set(LIBXML2_INCLUDE_DIRS ${FBXSDK_INCLUDE_DIR})
|
||||||
|
@ -190,7 +197,6 @@ set(LIB_SOURCE_FILES
|
||||||
src/utils/File_Utils.hpp
|
src/utils/File_Utils.hpp
|
||||||
src/utils/Image_Utils.cpp
|
src/utils/Image_Utils.cpp
|
||||||
src/utils/Image_Utils.hpp
|
src/utils/Image_Utils.hpp
|
||||||
src/utils/String_Utils.cpp
|
|
||||||
src/utils/String_Utils.hpp
|
src/utils/String_Utils.hpp
|
||||||
third_party/CLI11/CLI11.hpp
|
third_party/CLI11/CLI11.hpp
|
||||||
)
|
)
|
||||||
|
@ -237,6 +243,12 @@ if (WIN32)
|
||||||
optimized ${ZLIB_LIBRARIES}
|
optimized ${ZLIB_LIBRARIES}
|
||||||
debug ${ZLIB_LIBRARIES_DEBUG}
|
debug ${ZLIB_LIBRARIES_DEBUG}
|
||||||
)
|
)
|
||||||
|
# quiet warnings related to fopen, sscanf
|
||||||
|
target_compile_definitions(libFBX2glTF PRIVATE
|
||||||
|
_CRT_SECURE_NO_WARNINGS
|
||||||
|
_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
|
||||||
|
)
|
||||||
|
|
||||||
else()
|
else()
|
||||||
target_link_libraries(libFBX2glTF
|
target_link_libraries(libFBX2glTF
|
||||||
${LIBXML2_LIBRARIES}
|
${LIBXML2_LIBRARIES}
|
||||||
|
|
|
@ -13,13 +13,6 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#define _stricmp strcasecmp
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <CLI11.hpp>
|
#include <CLI11.hpp>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
|
@ -271,10 +264,7 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
if (outputPath.empty()) {
|
if (outputPath.empty()) {
|
||||||
// if -o is not given, default to the basename of the .fbx
|
// if -o is not given, default to the basename of the .fbx
|
||||||
outputPath = fmt::format(
|
outputPath = "./" + FileUtils::GetFileBaseString(inputPath);
|
||||||
".{}{}",
|
|
||||||
(const char)StringUtils::GetPathSeparator(),
|
|
||||||
StringUtils::GetFileBaseString(inputPath));
|
|
||||||
}
|
}
|
||||||
// the output folder in .gltf mode, not used for .glb
|
// the output folder in .gltf mode, not used for .glb
|
||||||
std::string outputFolder;
|
std::string outputFolder;
|
||||||
|
@ -282,14 +272,17 @@ int main(int argc, char* argv[]) {
|
||||||
// the path of the actual .glb or .gltf file
|
// the path of the actual .glb or .gltf file
|
||||||
std::string modelPath;
|
std::string modelPath;
|
||||||
if (gltfOptions.outputBinary) {
|
if (gltfOptions.outputBinary) {
|
||||||
// in binary mode, we write precisely where we're asked
|
const auto& suffix = FileUtils::GetFileSuffix(outputPath);
|
||||||
modelPath = outputPath + ".glb";
|
// add .glb to output path, unless it already ends in exactly that
|
||||||
|
if (suffix.has_value() && suffix.value() == "glb") {
|
||||||
|
modelPath = outputPath;
|
||||||
|
} else {
|
||||||
|
modelPath = outputPath + ".glb";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// in gltf mode, we create a folder and write into that
|
// in gltf mode, we create a folder and write into that
|
||||||
outputFolder =
|
outputFolder = fmt::format("{}_out/", outputPath.c_str());
|
||||||
fmt::format("{}_out{}", outputPath.c_str(), (const char)StringUtils::GetPathSeparator());
|
modelPath = outputFolder + FileUtils::GetFileNameString(outputPath) + ".gltf";
|
||||||
modelPath = outputFolder + StringUtils::GetFileNameString(outputPath) + ".gltf";
|
|
||||||
}
|
}
|
||||||
if (!FileUtils::CreatePath(modelPath.c_str())) {
|
if (!FileUtils::CreatePath(modelPath.c_str())) {
|
||||||
fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str());
|
fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str());
|
||||||
|
@ -302,7 +295,7 @@ int main(int argc, char* argv[]) {
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
fmt::printf("Loading FBX File: %s\n", inputPath);
|
fmt::printf("Loading FBX File: %s\n", inputPath);
|
||||||
}
|
}
|
||||||
if (!LoadFBXFile(raw, inputPath.c_str(), "png;jpg;jpeg")) {
|
if (!LoadFBXFile(raw, inputPath, {"png", "jpg", "jpeg"})) {
|
||||||
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
|
fmt::fprintf(stderr, "ERROR:: Failed to parse FBX: %s\n", inputPath);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,15 @@ using json = nlohmann::basic_json<workaround_fifo_map>;
|
||||||
|
|
||||||
extern bool verboseOutput;
|
extern bool verboseOutput;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Centralises all the laborious downcasting from your OS' 64-bit
|
||||||
|
* index variables down to the uint32s that glTF is built out of.
|
||||||
|
*/
|
||||||
|
inline uint32_t to_uint32(size_t n) {
|
||||||
|
assert(n < UINT_MAX);
|
||||||
|
return static_cast<uint32_t>(n);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The variuos situations in which the user may wish for us to (re-)compute normals for our vertices.
|
* The variuos situations in which the user may wish for us to (re-)compute normals for our vertices.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -925,7 +925,7 @@ static std::string GetInferredFileName(
|
||||||
}
|
}
|
||||||
// Get the file name with file extension.
|
// Get the file name with file extension.
|
||||||
const std::string fileName =
|
const std::string fileName =
|
||||||
StringUtils::GetFileNameString(StringUtils::GetCleanPathString(fbxFileName));
|
FileUtils::GetFileNameString(FileUtils::GetCanonicalPath(fbxFileName));
|
||||||
|
|
||||||
// Try to find a match with extension.
|
// Try to find a match with extension.
|
||||||
for (const auto& file : directoryFileList) {
|
for (const auto& file : directoryFileList) {
|
||||||
|
@ -935,12 +935,12 @@ static std::string GetInferredFileName(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the file name without file extension.
|
// Get the file name without file extension.
|
||||||
const std::string fileBase = StringUtils::GetFileBaseString(fileName);
|
const std::string fileBase = FileUtils::GetFileBaseString(fileName);
|
||||||
|
|
||||||
// Try to find a match without file extension.
|
// Try to find a match without file extension.
|
||||||
for (const auto& file : directoryFileList) {
|
for (const auto& file : directoryFileList) {
|
||||||
// If the two extension-less base names match.
|
// If the two extension-less base names match.
|
||||||
if (StringUtils::CompareNoCase(fileBase, StringUtils::GetFileBaseString(file)) == 0) {
|
if (StringUtils::CompareNoCase(fileBase, FileUtils::GetFileBaseString(file)) == 0) {
|
||||||
// Return the name with extension of the file in the directory.
|
// Return the name with extension of the file in the directory.
|
||||||
return std::string(directory) + file;
|
return std::string(directory) + file;
|
||||||
}
|
}
|
||||||
|
@ -960,14 +960,14 @@ static std::string GetInferredFileName(
|
||||||
*/
|
*/
|
||||||
static void FindFbxTextures(
|
static void FindFbxTextures(
|
||||||
FbxScene* pScene,
|
FbxScene* pScene,
|
||||||
const char* fbxFileName,
|
const std::string fbxFileName,
|
||||||
const char* extensions,
|
const std::set<std::string>& extensions,
|
||||||
std::map<const FbxTexture*, FbxString>& textureLocations) {
|
std::map<const FbxTexture*, FbxString>& textureLocations) {
|
||||||
// Get the folder the FBX file is in.
|
// Get the folder the FBX file is in.
|
||||||
const std::string folder = StringUtils::GetFolderString(fbxFileName);
|
const std::string folder = FileUtils::GetFolderString(fbxFileName);
|
||||||
|
|
||||||
// Check if there is a filename.fbm folder to which embedded textures were extracted.
|
// Check if there is a filename.fbm folder to which embedded textures were extracted.
|
||||||
const std::string fbmFolderName = folder + StringUtils::GetFileBaseString(fbxFileName) + ".fbm/";
|
const std::string fbmFolderName = folder + FileUtils::GetFileBaseString(fbxFileName) + ".fbm/";
|
||||||
|
|
||||||
// Search either in the folder with embedded textures or in the same folder as the FBX file.
|
// Search either in the folder with embedded textures or in the same folder as the FBX file.
|
||||||
const std::string searchFolder = FileUtils::FolderExists(fbmFolderName) ? fbmFolderName : folder;
|
const std::string searchFolder = FileUtils::FolderExists(fbmFolderName) ? fbmFolderName : folder;
|
||||||
|
@ -996,14 +996,17 @@ static void FindFbxTextures(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions) {
|
bool LoadFBXFile(
|
||||||
|
RawModel& raw,
|
||||||
|
const std::string fbxFileName,
|
||||||
|
const std::set<std::string>& textureExtensions) {
|
||||||
FbxManager* pManager = FbxManager::Create();
|
FbxManager* pManager = FbxManager::Create();
|
||||||
FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT);
|
FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT);
|
||||||
pManager->SetIOSettings(pIoSettings);
|
pManager->SetIOSettings(pIoSettings);
|
||||||
|
|
||||||
FbxImporter* pImporter = FbxImporter::Create(pManager, "");
|
FbxImporter* pImporter = FbxImporter::Create(pManager, "");
|
||||||
|
|
||||||
if (!pImporter->Initialize(fbxFileName, -1, pManager->GetIOSettings())) {
|
if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) {
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
|
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
|
|
||||||
#include "raw/RawModel.hpp"
|
#include "raw/RawModel.hpp"
|
||||||
|
|
||||||
bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions);
|
bool LoadFBXFile(
|
||||||
|
RawModel& raw,
|
||||||
|
const std::string fbxFileName,
|
||||||
|
const std::set<std::string>& textureExtensions);
|
||||||
|
|
||||||
json TranscribeProperty(FbxProperty& prop);
|
json TranscribeProperty(FbxProperty& prop);
|
|
@ -95,7 +95,7 @@ class FbxBlendShapesAccess {
|
||||||
}
|
}
|
||||||
|
|
||||||
FbxAnimCurve* GetAnimation(size_t channelIx, size_t animIx) const {
|
FbxAnimCurve* GetAnimation(size_t channelIx, size_t animIx) const {
|
||||||
return channels.at(channelIx).ExtractAnimation(animIx);
|
return channels.at(channelIx).ExtractAnimation(to_uint32(animIx));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -37,7 +37,7 @@ class FbxSkinningAccess {
|
||||||
return jointNodes[jointIndex];
|
return jointNodes[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const long GetJointId(const int jointIndex) const {
|
const uint64_t GetJointId(const int jointIndex) const {
|
||||||
return jointIds[jointIndex];
|
return jointIds[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ class FbxSkinningAccess {
|
||||||
return jointInverseGlobalTransforms[jointIndex];
|
return jointInverseGlobalTransforms[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const long GetRootNode() const {
|
const uint64_t GetRootNode() const {
|
||||||
assert(rootIndex != -1);
|
assert(rootIndex != -1);
|
||||||
return jointIds[rootIndex];
|
return jointIds[rootIndex];
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ class FbxSkinningAccess {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int rootIndex;
|
int rootIndex;
|
||||||
std::vector<long> jointIds;
|
std::vector<uint64_t> jointIds;
|
||||||
std::vector<FbxNode*> jointNodes;
|
std::vector<FbxNode*> jointNodes;
|
||||||
std::vector<FbxMatrix> jointSkinningTransforms;
|
std::vector<FbxMatrix> jointSkinningTransforms;
|
||||||
std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
std::shared_ptr<BufferViewData> GltfModel::GetAlignedBufferView(
|
std::shared_ptr<BufferViewData> GltfModel::GetAlignedBufferView(
|
||||||
BufferData& buffer,
|
BufferData& buffer,
|
||||||
const BufferViewData::GL_ArrayType target) {
|
const BufferViewData::GL_ArrayType target) {
|
||||||
unsigned long bufferSize = this->binary->size();
|
uint32_t bufferSize = to_uint32(this->binary->size());
|
||||||
if ((bufferSize % 4) > 0) {
|
if ((bufferSize % 4) > 0) {
|
||||||
bufferSize += (4 - (bufferSize % 4));
|
bufferSize += (4 - (bufferSize % 4));
|
||||||
this->binary->resize(bufferSize);
|
this->binary->resize(bufferSize);
|
||||||
|
@ -27,7 +27,7 @@ GltfModel::AddRawBufferView(BufferData& buffer, const char* source, uint32_t byt
|
||||||
bufferView->byteLength = bytes;
|
bufferView->byteLength = bytes;
|
||||||
|
|
||||||
// make space for the new bytes (possibly moving the underlying data)
|
// make space for the new bytes (possibly moving the underlying data)
|
||||||
unsigned long bufferSize = this->binary->size();
|
uint32_t bufferSize = to_uint32(this->binary->size());
|
||||||
this->binary->resize(bufferSize + bytes);
|
this->binary->resize(bufferSize + bytes);
|
||||||
|
|
||||||
// and copy them into place
|
// and copy them into place
|
||||||
|
@ -52,7 +52,7 @@ std::shared_ptr<BufferViewData> GltfModel::AddBufferViewForFile(
|
||||||
|
|
||||||
std::vector<char> fileBuffer(size);
|
std::vector<char> fileBuffer(size);
|
||||||
if (file.read(fileBuffer.data(), size)) {
|
if (file.read(fileBuffer.data(), size)) {
|
||||||
result = AddRawBufferView(buffer, fileBuffer.data(), size);
|
result = AddRawBufferView(buffer, fileBuffer.data(), to_uint32(size));
|
||||||
} else {
|
} else {
|
||||||
fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename);
|
fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ template <typename T>
|
||||||
class Holder {
|
class Holder {
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<T> hold(T* ptr) {
|
std::shared_ptr<T> hold(T* ptr) {
|
||||||
ptr->ix = ptrs.size();
|
ptr->ix = to_uint32(ptrs.size());
|
||||||
ptrs.emplace_back(ptr);
|
ptrs.emplace_back(ptr);
|
||||||
return ptrs.back();
|
return ptrs.back();
|
||||||
}
|
}
|
||||||
|
@ -114,7 +114,7 @@ class GltfModel {
|
||||||
primitive.AddDracoAttrib(attrDef, attribArr);
|
primitive.AddDracoAttrib(attrDef, attribArr);
|
||||||
|
|
||||||
accessor = accessors.hold(new AccessorData(attrDef.glType));
|
accessor = accessors.hold(new AccessorData(attrDef.glType));
|
||||||
accessor->count = attribArr.size();
|
accessor->count = to_uint32(attribArr.size());
|
||||||
} else {
|
} else {
|
||||||
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
|
auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER);
|
||||||
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));
|
accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string(""));
|
||||||
|
|
|
@ -256,29 +256,28 @@ ModelData* Raw2Gltf(
|
||||||
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
||||||
/**
|
/**
|
||||||
* PBR FBX Material -> PBR Met/Rough glTF.
|
* PBR FBX Material -> PBR Met/Rough glTF.
|
||||||
|
*
|
||||||
|
* METALLIC and ROUGHNESS textures are packed in G and B channels of a rough/met texture.
|
||||||
|
* Other values translate directly.
|
||||||
*/
|
*/
|
||||||
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
|
RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get();
|
||||||
|
|
||||||
// diffuse and emissive are noncontroversial
|
|
||||||
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
|
||||||
diffuseFactor = props->diffuseFactor;
|
|
||||||
emissiveFactor = props->emissiveFactor;
|
|
||||||
emissiveIntensity = props->emissiveIntensity;
|
|
||||||
|
|
||||||
// we always send the metallic/roughness factors onto the glTF generator
|
|
||||||
metallic = props->metallic;
|
|
||||||
roughness = props->roughness;
|
|
||||||
|
|
||||||
// determine if we need to generate a combined map
|
// determine if we need to generate a combined map
|
||||||
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
|
bool hasMetallicMap = material.textures[RAW_TEXTURE_USAGE_METALLIC] >= 0;
|
||||||
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
|
bool hasRoughnessMap = material.textures[RAW_TEXTURE_USAGE_ROUGHNESS] >= 0;
|
||||||
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
|
bool hasOcclusionMap = material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0;
|
||||||
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
|
bool atLeastTwoMaps = hasMetallicMap ? (hasRoughnessMap || hasOcclusionMap)
|
||||||
: (hasRoughnessMap && hasMetallicMap);
|
: (hasRoughnessMap && hasMetallicMap);
|
||||||
if (atLeastTwoMaps) {
|
if (!atLeastTwoMaps) {
|
||||||
// if there's at least two of metallic/roughness/occlusion, it makes sense to
|
// this handles the case of 0 or 1 maps supplied
|
||||||
// merge them: occlusion into the red channel, metallic into blue channel, and
|
aoMetRoughTex = hasMetallicMap
|
||||||
// roughness into the green.
|
? simpleTex(RAW_TEXTURE_USAGE_METALLIC)
|
||||||
|
: (hasRoughnessMap
|
||||||
|
? simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS)
|
||||||
|
: (hasOcclusionMap ? simpleTex(RAW_TEXTURE_USAGE_OCCLUSION) : nullptr));
|
||||||
|
} else {
|
||||||
|
// otherwise merge occlusion into the red channel, metallic into blue channel, and
|
||||||
|
// roughness into the green, of a new combinatory texture
|
||||||
aoMetRoughTex = textureBuilder.combine(
|
aoMetRoughTex = textureBuilder.combine(
|
||||||
{
|
{
|
||||||
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
material.textures[RAW_TEXTURE_USAGE_OCCLUSION],
|
||||||
|
@ -289,27 +288,24 @@ ModelData* Raw2Gltf(
|
||||||
[&](const std::vector<const TextureBuilder::pixel*> pixels)
|
[&](const std::vector<const TextureBuilder::pixel*> pixels)
|
||||||
-> TextureBuilder::pixel {
|
-> TextureBuilder::pixel {
|
||||||
const float occlusion = (*pixels[0])[0];
|
const float occlusion = (*pixels[0])[0];
|
||||||
const float metallic = (*pixels[1])[0];
|
const float metallic = (*pixels[1])[0] * (hasMetallicMap ? 1 : props->metallic);
|
||||||
const float roughness = (*pixels[2])[0];
|
const float roughness =
|
||||||
|
(*pixels[2])[0] * (hasRoughnessMap ? 1 : props->roughness);
|
||||||
return {{occlusion,
|
return {{occlusion,
|
||||||
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
props->invertRoughnessMap ? 1.0f - roughness : roughness,
|
||||||
metallic,
|
metallic,
|
||||||
1}};
|
1}};
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
if (hasOcclusionMap) {
|
|
||||||
// will only be true if there were actual non-trivial pixels
|
|
||||||
occlusionTexture = aoMetRoughTex.get();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// this handles the case of 0 or 1 maps supplied
|
|
||||||
if (hasMetallicMap) {
|
|
||||||
aoMetRoughTex = simpleTex(RAW_TEXTURE_USAGE_METALLIC);
|
|
||||||
} else if (hasRoughnessMap) {
|
|
||||||
aoMetRoughTex = simpleTex(RAW_TEXTURE_USAGE_ROUGHNESS);
|
|
||||||
}
|
|
||||||
// else only occlusion map is possible: that check is handled further below
|
|
||||||
}
|
}
|
||||||
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
metallic = props->metallic;
|
||||||
|
roughness = props->roughness;
|
||||||
|
emissiveFactor = props->emissiveFactor;
|
||||||
|
emissiveIntensity = props->emissiveIntensity;
|
||||||
|
// this will set occlusionTexture to null, if no actual occlusion map exists
|
||||||
|
occlusionTexture = aoMetRoughTex.get();
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* Traditional FBX Material -> PBR Met/Rough glTF.
|
* Traditional FBX Material -> PBR Met/Rough glTF.
|
||||||
|
@ -393,7 +389,6 @@ ModelData* Raw2Gltf(
|
||||||
|
|
||||||
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial());
|
||||||
}
|
}
|
||||||
// after all the special cases have had a go, check if we need to look up occlusion map
|
|
||||||
if (!occlusionTexture) {
|
if (!occlusionTexture) {
|
||||||
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get();
|
||||||
}
|
}
|
||||||
|
@ -447,11 +442,11 @@ ModelData* Raw2Gltf(
|
||||||
|
|
||||||
std::shared_ptr<PrimitiveData> primitive;
|
std::shared_ptr<PrimitiveData> primitive;
|
||||||
if (options.draco.enabled) {
|
if (options.draco.enabled) {
|
||||||
int triangleCount = surfaceModel.GetTriangleCount();
|
size_t triangleCount = surfaceModel.GetTriangleCount();
|
||||||
|
|
||||||
// initialize Draco mesh with vertex index information
|
// initialize Draco mesh with vertex index information
|
||||||
auto dracoMesh(std::make_shared<draco::Mesh>());
|
auto dracoMesh(std::make_shared<draco::Mesh>());
|
||||||
dracoMesh->SetNumFaces(static_cast<size_t>(triangleCount));
|
dracoMesh->SetNumFaces(triangleCount);
|
||||||
dracoMesh->set_num_points(surfaceModel.GetVertexCount());
|
dracoMesh->set_num_points(surfaceModel.GetVertexCount());
|
||||||
|
|
||||||
for (uint32_t ii = 0; ii < triangleCount; ii++) {
|
for (uint32_t ii = 0; ii < triangleCount; ii++) {
|
||||||
|
@ -464,7 +459,7 @@ ModelData* Raw2Gltf(
|
||||||
|
|
||||||
AccessorData& indexes =
|
AccessorData& indexes =
|
||||||
*gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT));
|
*gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT));
|
||||||
indexes.count = 3 * triangleCount;
|
indexes.count = to_uint32(3 * triangleCount);
|
||||||
primitive.reset(new PrimitiveData(indexes, mData, dracoMesh));
|
primitive.reset(new PrimitiveData(indexes, mData, dracoMesh));
|
||||||
} else {
|
} else {
|
||||||
const AccessorData& indexes = *gltf->AddAccessorWithView(
|
const AccessorData& indexes = *gltf->AddAccessorWithView(
|
||||||
|
@ -499,11 +494,13 @@ ModelData* Raw2Gltf(
|
||||||
GLT_VEC3F,
|
GLT_VEC3F,
|
||||||
draco::GeometryAttribute::NORMAL,
|
draco::GeometryAttribute::NORMAL,
|
||||||
draco::DT_FLOAT32);
|
draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
|
const auto _ =
|
||||||
|
gltf->AddAttributeToPrimitive<Vec3f>(buffer, surfaceModel, *primitive, ATTR_NORMAL);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_TANGENT) != 0) {
|
||||||
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
|
const AttributeDefinition<Vec4f> ATTR_TANGENT("TANGENT", &RawVertex::tangent, GLT_VEC4F);
|
||||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_TANGENT);
|
const auto _ = gltf->AddAttributeToPrimitive<Vec4f>(
|
||||||
|
buffer, surfaceModel, *primitive, ATTR_TANGENT);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) {
|
||||||
const AttributeDefinition<Vec4f> ATTR_COLOR(
|
const AttributeDefinition<Vec4f> ATTR_COLOR(
|
||||||
|
@ -512,7 +509,8 @@ ModelData* Raw2Gltf(
|
||||||
GLT_VEC4F,
|
GLT_VEC4F,
|
||||||
draco::GeometryAttribute::COLOR,
|
draco::GeometryAttribute::COLOR,
|
||||||
draco::DT_FLOAT32);
|
draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR);
|
const auto _ =
|
||||||
|
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_COLOR);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) {
|
||||||
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0(
|
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_0(
|
||||||
|
@ -521,7 +519,8 @@ ModelData* Raw2Gltf(
|
||||||
GLT_VEC2F,
|
GLT_VEC2F,
|
||||||
draco::GeometryAttribute::TEX_COORD,
|
draco::GeometryAttribute::TEX_COORD,
|
||||||
draco::DT_FLOAT32);
|
draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0);
|
const auto _ = gltf->AddAttributeToPrimitive<Vec2f>(
|
||||||
|
buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) {
|
||||||
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1(
|
const AttributeDefinition<Vec2f> ATTR_TEXCOORD_1(
|
||||||
|
@ -530,7 +529,8 @@ ModelData* Raw2Gltf(
|
||||||
GLT_VEC2F,
|
GLT_VEC2F,
|
||||||
draco::GeometryAttribute::TEX_COORD,
|
draco::GeometryAttribute::TEX_COORD,
|
||||||
draco::DT_FLOAT32);
|
draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec2f>(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
|
const auto _ = gltf->AddAttributeToPrimitive<Vec2f>(
|
||||||
|
buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) {
|
||||||
const AttributeDefinition<Vec4i> ATTR_JOINTS(
|
const AttributeDefinition<Vec4i> ATTR_JOINTS(
|
||||||
|
@ -539,7 +539,8 @@ ModelData* Raw2Gltf(
|
||||||
GLT_VEC4I,
|
GLT_VEC4I,
|
||||||
draco::GeometryAttribute::GENERIC,
|
draco::GeometryAttribute::GENERIC,
|
||||||
draco::DT_UINT16);
|
draco::DT_UINT16);
|
||||||
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
|
const auto _ =
|
||||||
|
gltf->AddAttributeToPrimitive<Vec4i>(buffer, surfaceModel, *primitive, ATTR_JOINTS);
|
||||||
}
|
}
|
||||||
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
|
if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) {
|
||||||
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
|
const AttributeDefinition<Vec4f> ATTR_WEIGHTS(
|
||||||
|
@ -548,7 +549,8 @@ ModelData* Raw2Gltf(
|
||||||
GLT_VEC4F,
|
GLT_VEC4F,
|
||||||
draco::GeometryAttribute::GENERIC,
|
draco::GeometryAttribute::GENERIC,
|
||||||
draco::DT_FLOAT32);
|
draco::DT_FLOAT32);
|
||||||
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
|
const auto _ =
|
||||||
|
gltf->AddAttributeToPrimitive<Vec4f>(buffer, surfaceModel, *primitive, ATTR_WEIGHTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// each channel present in the mesh always ends up a target in the primitive
|
// each channel present in the mesh always ends up a target in the primitive
|
||||||
|
@ -633,7 +635,7 @@ ModelData* Raw2Gltf(
|
||||||
draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer);
|
draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer);
|
||||||
assert(status.code() == draco::Status::OK);
|
assert(status.code() == draco::Status::OK);
|
||||||
|
|
||||||
auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), dracoBuffer.size());
|
auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), to_uint32(dracoBuffer.size()));
|
||||||
primitive->NoteDracoBuffer(*view);
|
primitive->NoteDracoBuffer(*view);
|
||||||
}
|
}
|
||||||
mesh->AddPrimitive(primitive);
|
mesh->AddPrimitive(primitive);
|
||||||
|
@ -735,7 +737,7 @@ ModelData* Raw2Gltf(
|
||||||
type = LightData::Type::Spot;
|
type = LightData::Type::Spot;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
gltf->lights.hold(new LightData(
|
const auto _ = gltf->lights.hold(new LightData(
|
||||||
light.name,
|
light.name,
|
||||||
type,
|
type,
|
||||||
light.color,
|
light.color,
|
||||||
|
@ -842,13 +844,13 @@ ModelData* Raw2Gltf(
|
||||||
gltfOutStream.write(glb2BinaryHeader, 8);
|
gltfOutStream.write(glb2BinaryHeader, 8);
|
||||||
|
|
||||||
// append binary buffer directly to .glb file
|
// append binary buffer directly to .glb file
|
||||||
uint32_t binaryLength = gltf->binary->size();
|
size_t binaryLength = gltf->binary->size();
|
||||||
gltfOutStream.write((const char*)&(*gltf->binary)[0], binaryLength);
|
gltfOutStream.write((const char*)&(*gltf->binary)[0], binaryLength);
|
||||||
while ((binaryLength % 4) != 0) {
|
while ((binaryLength % 4) != 0) {
|
||||||
gltfOutStream.put('\0');
|
gltfOutStream.put('\0');
|
||||||
binaryLength++;
|
binaryLength++;
|
||||||
}
|
}
|
||||||
uint32_t totalLength = (uint32_t)gltfOutStream.tellp();
|
uint32_t totalLength = to_uint32(gltfOutStream.tellp());
|
||||||
|
|
||||||
// seek back to sub-header for json chunk
|
// seek back to sub-header for json chunk
|
||||||
gltfOutStream.seekp(8);
|
gltfOutStream.seekp(8);
|
||||||
|
|
|
@ -120,7 +120,7 @@ const GLType GLT_QUATF = {CT_FLOAT, 4, "VEC4"};
|
||||||
* The base of any indexed glTF entity.
|
* The base of any indexed glTF entity.
|
||||||
*/
|
*/
|
||||||
struct Holdable {
|
struct Holdable {
|
||||||
uint32_t ix;
|
uint32_t ix = UINT_MAX;
|
||||||
|
|
||||||
virtual json serialize() const = 0;
|
virtual json serialize() const = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,8 +49,7 @@ std::shared_ptr<TextureData> TextureBuilder::combine(
|
||||||
if (rawTexIx >= 0) {
|
if (rawTexIx >= 0) {
|
||||||
const RawTexture& rawTex = raw.GetTexture(rawTexIx);
|
const RawTexture& rawTex = raw.GetTexture(rawTexIx);
|
||||||
const std::string& fileLoc = rawTex.fileLocation;
|
const std::string& fileLoc = rawTex.fileLocation;
|
||||||
const std::string& name =
|
const std::string& name = FileUtils::GetFileBaseString(FileUtils::GetFileNameString(fileLoc));
|
||||||
StringUtils::GetFileBaseString(StringUtils::GetFileNameString(fileLoc));
|
|
||||||
if (!fileLoc.empty()) {
|
if (!fileLoc.empty()) {
|
||||||
info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0);
|
info.pixels = stbi_load(fileLoc.c_str(), &info.width, &info.height, &info.channels, 0);
|
||||||
if (!info.pixels) {
|
if (!info.pixels) {
|
||||||
|
@ -142,7 +141,7 @@ std::shared_ptr<TextureData> TextureBuilder::combine(
|
||||||
ImageData* image;
|
ImageData* image;
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary) {
|
||||||
const auto bufferView =
|
const auto bufferView =
|
||||||
gltf.AddRawBufferView(*gltf.defaultBuffer, imgBuffer.data(), imgBuffer.size());
|
gltf.AddRawBufferView(*gltf.defaultBuffer, imgBuffer.data(), to_uint32(imgBuffer.size()));
|
||||||
image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg");
|
image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg");
|
||||||
} else {
|
} else {
|
||||||
const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg");
|
const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg");
|
||||||
|
@ -180,20 +179,30 @@ std::shared_ptr<TextureData> TextureBuilder::simple(int rawTexIndex, const std::
|
||||||
}
|
}
|
||||||
|
|
||||||
const RawTexture& rawTexture = raw.GetTexture(rawTexIndex);
|
const RawTexture& rawTexture = raw.GetTexture(rawTexIndex);
|
||||||
const std::string textureName = StringUtils::GetFileBaseString(rawTexture.name);
|
const std::string textureName = FileUtils::GetFileBaseString(rawTexture.name);
|
||||||
const std::string relativeFilename = StringUtils::GetFileNameString(rawTexture.fileLocation);
|
const std::string relativeFilename = FileUtils::GetFileNameString(rawTexture.fileLocation);
|
||||||
|
|
||||||
ImageData* image = nullptr;
|
ImageData* image = nullptr;
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary) {
|
||||||
auto bufferView = gltf.AddBufferViewForFile(*gltf.defaultBuffer, rawTexture.fileLocation);
|
auto bufferView = gltf.AddBufferViewForFile(*gltf.defaultBuffer, rawTexture.fileLocation);
|
||||||
if (bufferView) {
|
if (bufferView) {
|
||||||
std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation);
|
const auto& suffix = FileUtils::GetFileSuffix(rawTexture.fileLocation);
|
||||||
image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix));
|
std::string mimeType;
|
||||||
|
if (suffix) {
|
||||||
|
mimeType = ImageUtils::suffixToMimeType(suffix.value());
|
||||||
|
} else {
|
||||||
|
mimeType = "image/jpeg";
|
||||||
|
fmt::printf(
|
||||||
|
"Warning: Can't deduce mime type of texture '%s'; using %s.\n",
|
||||||
|
rawTexture.fileLocation,
|
||||||
|
mimeType);
|
||||||
|
}
|
||||||
|
image = new ImageData(relativeFilename, *bufferView, mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!relativeFilename.empty()) {
|
} else if (!relativeFilename.empty()) {
|
||||||
image = new ImageData(relativeFilename, relativeFilename);
|
image = new ImageData(relativeFilename, relativeFilename);
|
||||||
std::string outputPath = outputFolder + StringUtils::NormalizePath(relativeFilename);
|
std::string outputPath = FileUtils::GetCanonicalPath(outputFolder + "/" + relativeFilename);
|
||||||
if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true)) {
|
if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true)) {
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);
|
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);
|
||||||
|
|
|
@ -24,7 +24,7 @@ void AnimationData::AddNodeChannel(
|
||||||
const AccessorData& accessor,
|
const AccessorData& accessor,
|
||||||
std::string path) {
|
std::string path) {
|
||||||
assert(channels.size() == samplers.size());
|
assert(channels.size() == samplers.size());
|
||||||
uint32_t ix = channels.size();
|
uint32_t ix = to_uint32(channels.size());
|
||||||
channels.emplace_back(channel_t(ix, node, std::move(path)));
|
channels.emplace_back(channel_t(ix, node, std::move(path)));
|
||||||
samplers.emplace_back(sampler_t(timeAccessor, accessor.ix));
|
samplers.emplace_back(sampler_t(timeAccessor, accessor.ix));
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,13 +59,15 @@ void to_json(json& j, const PBRMetallicRoughness& d) {
|
||||||
if (d.baseColorFactor.LengthSquared() > 0) {
|
if (d.baseColorFactor.LengthSquared() > 0) {
|
||||||
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
||||||
}
|
}
|
||||||
// we always copy metallic/roughness straight to the glTF:
|
|
||||||
// - if there's a texture, they're linear multiplier
|
|
||||||
// - if there's no texture, they're constants
|
|
||||||
j["metallicFactor"] = d.metallic;
|
|
||||||
j["roughnessFactor"] = d.roughness;
|
|
||||||
if (d.metRoughTexture != nullptr) {
|
if (d.metRoughTexture != nullptr) {
|
||||||
j["metallicRoughnessTexture"] = *d.metRoughTexture;
|
j["metallicRoughnessTexture"] = *d.metRoughTexture;
|
||||||
|
// if a texture is provided, throw away metallic/roughness values
|
||||||
|
j["roughnessFactor"] = 1.0f;
|
||||||
|
j["metallicFactor"] = 1.0f;
|
||||||
|
} else {
|
||||||
|
// without a texture, however, use metallic/roughness as constants
|
||||||
|
j["metallicFactor"] = d.metallic;
|
||||||
|
j["roughnessFactor"] = d.roughness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ struct PrimitiveData {
|
||||||
componentCount * draco::DataTypeLength(attribute.dracoComponentType),
|
componentCount * draco::DataTypeLength(attribute.dracoComponentType),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
const int dracoAttId = dracoMesh->AddAttribute(att, true, attribArr.size());
|
const int dracoAttId = dracoMesh->AddAttribute(att, true, to_uint32(attribArr.size()));
|
||||||
draco::PointAttribute* attPtr = dracoMesh->attribute(dracoAttId);
|
draco::PointAttribute* attPtr = dracoMesh->attribute(dracoAttId);
|
||||||
|
|
||||||
std::vector<uint8_t> buf(sizeof(T));
|
std::vector<uint8_t> buf(sizeof(T));
|
||||||
|
|
|
@ -632,7 +632,7 @@ void RawModel::CreateMaterialModels(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::GetNodeById(const long nodeId) const {
|
int RawModel::GetNodeById(const uint32_t nodeId) const {
|
||||||
for (size_t i = 0; i < nodes.size(); i++) {
|
for (size_t i = 0; i < nodes.size(); i++) {
|
||||||
if (nodes[i].id == nodeId) {
|
if (nodes[i].id == nodeId) {
|
||||||
return (int)i;
|
return (int)i;
|
||||||
|
@ -641,7 +641,7 @@ int RawModel::GetNodeById(const long nodeId) const {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::GetSurfaceById(const long surfaceId) const {
|
int RawModel::GetSurfaceById(const uint32_t surfaceId) const {
|
||||||
for (size_t i = 0; i < surfaces.size(); i++) {
|
for (size_t i = 0; i < surfaces.size(); i++) {
|
||||||
if (surfaces[i].id == surfaceId) {
|
if (surfaces[i].id == surfaceId) {
|
||||||
return (int)i;
|
return (int)i;
|
||||||
|
|
|
@ -10,171 +10,41 @@
|
||||||
#include "File_Utils.hpp"
|
#include "File_Utils.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define _getcwd getcwd
|
|
||||||
#define _mkdir(a) mkdir(a, 0777)
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <direct.h>
|
|
||||||
#include <process.h>
|
|
||||||
#else
|
|
||||||
#include <direct.h>
|
|
||||||
#include <process.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
#include "String_Utils.hpp"
|
#include "String_Utils.hpp"
|
||||||
|
|
||||||
namespace FileUtils {
|
namespace FileUtils {
|
||||||
|
|
||||||
std::string GetCurrentFolder() {
|
std::vector<std::string> ListFolderFiles(
|
||||||
char cwd[StringUtils::MAX_PATH_LENGTH];
|
const std::string folder,
|
||||||
if (!_getcwd(cwd, sizeof(cwd))) {
|
const std::set<std::string>& matchExtensions) {
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
cwd[sizeof(cwd) - 1] = '\0';
|
|
||||||
StringUtils::GetCleanPath(cwd, cwd, StringUtils::PATH_UNIX);
|
|
||||||
const size_t length = strlen(cwd);
|
|
||||||
if (cwd[length - 1] != '/' && length < StringUtils::MAX_PATH_LENGTH - 1) {
|
|
||||||
cwd[length + 0] = '/';
|
|
||||||
cwd[length + 1] = '\0';
|
|
||||||
}
|
|
||||||
return std::string(cwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileExists(const std::string& filePath) {
|
|
||||||
std::ifstream stream(filePath);
|
|
||||||
return stream.good();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FolderExists(const std::string& folderPath) {
|
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
|
||||||
DIR* dir = opendir(folderPath.c_str());
|
|
||||||
if (dir) {
|
|
||||||
closedir(dir);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
const DWORD ftyp = GetFileAttributesA(folderPath.c_str());
|
|
||||||
if (ftyp == INVALID_FILE_ATTRIBUTES) {
|
|
||||||
return false; // bad path
|
|
||||||
}
|
|
||||||
return (ftyp & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MatchExtension(const char* fileExtension, const char* matchExtensions) {
|
|
||||||
if (matchExtensions[0] == '\0') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (fileExtension[0] == '.') {
|
|
||||||
fileExtension++;
|
|
||||||
}
|
|
||||||
for (const char* end = matchExtensions; end[0] != '\0';) {
|
|
||||||
for (; end[0] == ';'; end++) {
|
|
||||||
}
|
|
||||||
const char* ext = end;
|
|
||||||
for (; end[0] != ';' && end[0] != '\0'; end++) {
|
|
||||||
}
|
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
|
||||||
if (strncasecmp(fileExtension, ext, end - ext) == 0)
|
|
||||||
#else
|
|
||||||
if (_strnicmp(fileExtension, ext, end - ext) == 0)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> ListFolderFiles(const char* folder, const char* matchExtensions) {
|
|
||||||
std::vector<std::string> fileList;
|
std::vector<std::string> fileList;
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
for (const std::filesystem::directory_entry& entry : std::filesystem::directory_iterator(folder)) {
|
||||||
DIR* dir = opendir(strlen(folder) > 0 ? folder : ".");
|
const std::filesystem::path& path = entry.path();
|
||||||
if (dir != nullptr) {
|
if (matchExtensions.find(path.extension().string()) != matchExtensions.end()) {
|
||||||
for (;;) {
|
fileList.push_back(path.string());
|
||||||
struct dirent* dp = readdir(dir);
|
|
||||||
if (dp == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dp->d_type == DT_DIR) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* fileName = dp->d_name;
|
|
||||||
const char* fileExt = strrchr(fileName, '.');
|
|
||||||
|
|
||||||
if (!fileExt || !MatchExtension(fileExt, matchExtensions)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fileList.emplace_back(fileName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closedir(dir);
|
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
std::string pathStr = folder;
|
|
||||||
pathStr += "*";
|
|
||||||
|
|
||||||
WIN32_FIND_DATA FindFileData;
|
|
||||||
HANDLE hFind = FindFirstFile(pathStr.c_str(), &FindFileData);
|
|
||||||
if (hFind != INVALID_HANDLE_VALUE) {
|
|
||||||
do {
|
|
||||||
if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
|
||||||
std::string fileName = FindFileData.cFileName;
|
|
||||||
std::string::size_type extPos = fileName.rfind('.');
|
|
||||||
if (extPos != std::string::npos &&
|
|
||||||
MatchExtension(fileName.substr(extPos + 1).c_str(), matchExtensions)) {
|
|
||||||
fileList.push_back(fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (FindNextFile(hFind, &FindFileData));
|
|
||||||
|
|
||||||
FindClose(hFind);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return fileList;
|
return fileList;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CreatePath(const char* path) {
|
bool CreatePath(const std::string path) {
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
const auto& parent = std::filesystem::path(path).parent_path();
|
||||||
StringUtils::PathSeparator separator = StringUtils::PATH_UNIX;
|
if (parent.empty()) {
|
||||||
#else
|
// this is either CWD or std::filesystem root; either way it exists
|
||||||
StringUtils::PathSeparator separator = StringUtils::PATH_WIN;
|
return true;
|
||||||
#endif
|
|
||||||
std::string folder = StringUtils::GetFolderString(path);
|
|
||||||
std::string clean = StringUtils::GetCleanPathString(folder, separator);
|
|
||||||
std::string build = clean;
|
|
||||||
for (int i = 0; i < clean.length(); i++) {
|
|
||||||
if (clean[i] == separator && i > 0) {
|
|
||||||
build[i] = '\0';
|
|
||||||
if (i > 1 || build[1] != ':') {
|
|
||||||
if (_mkdir(build.c_str()) != 0 && errno != EEXIST) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
build[i] = clean[i];
|
|
||||||
}
|
}
|
||||||
return true;
|
if (std::filesystem::exists(parent)) {
|
||||||
|
return std::filesystem::is_directory(parent);
|
||||||
|
}
|
||||||
|
return std::filesystem::create_directory(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CopyFile(const std::string& srcFilename, const std::string& dstFilename, bool createPath) {
|
bool CopyFile(const std::string& srcFilename, const std::string& dstFilename, bool createPath) {
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -19,13 +23,51 @@ std::string GetCurrentFolder();
|
||||||
bool FileExists(const std::string& folderPath);
|
bool FileExists(const std::string& folderPath);
|
||||||
bool FolderExists(const std::string& folderPath);
|
bool FolderExists(const std::string& folderPath);
|
||||||
|
|
||||||
bool MatchExtension(const char* fileExtension, const char* matchExtensions);
|
std::vector<std::string> ListFolderFiles(
|
||||||
std::vector<std::string> ListFolderFiles(const char* folder, const char* matchExtensions);
|
const std::string folder,
|
||||||
|
const std::set<std::string>& matchExtensions);
|
||||||
|
|
||||||
bool CreatePath(const char* path);
|
bool CreatePath(std::string path);
|
||||||
|
|
||||||
bool CopyFile(
|
bool CopyFile(
|
||||||
const std::string& srcFilename,
|
const std::string& srcFilename,
|
||||||
const std::string& dstFilename,
|
const std::string& dstFilename,
|
||||||
bool createPath = false);
|
bool createPath = false);
|
||||||
|
|
||||||
|
inline std::string GetCurrentFolder() {
|
||||||
|
return std::filesystem::current_path().string() + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool FileExists(const std::string& filePath) {
|
||||||
|
return std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool FolderExists(const std::string& folderPath) {
|
||||||
|
return std::filesystem::exists(folderPath) && std::filesystem::is_directory(folderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetFolderString(const std::string& path) {
|
||||||
|
return std::filesystem::path(path).parent_path().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetCanonicalPath(const std::string& path) {
|
||||||
|
return std::filesystem::canonical(path).string();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetFileNameString(const std::string& path) {
|
||||||
|
return std::filesystem::canonical(path).filename().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string GetFileBaseString(const std::string& path) {
|
||||||
|
return std::filesystem::canonical(path).stem().string();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::optional<std::string> GetFileSuffix(const std::string& path) {
|
||||||
|
const auto& extension = std::filesystem::canonical(path).extension();
|
||||||
|
if (extension.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return extension.string().substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FileUtils
|
} // namespace FileUtils
|
||||||
|
|
|
@ -9,72 +9,12 @@
|
||||||
|
|
||||||
#include "String_Utils.hpp"
|
#include "String_Utils.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
PathSeparator operator!(const PathSeparator& s) {
|
|
||||||
return (s == PATH_WIN) ? PATH_UNIX : PATH_WIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
PathSeparator GetPathSeparator() {
|
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
|
||||||
return PATH_UNIX;
|
|
||||||
#else
|
|
||||||
return PATH_WIN;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
const std::string NormalizePath(const std::string& path) {
|
|
||||||
PathSeparator separator = GetPathSeparator();
|
|
||||||
char replace;
|
|
||||||
if (separator == PATH_WIN) {
|
|
||||||
replace = PATH_UNIX;
|
|
||||||
} else {
|
|
||||||
replace = PATH_WIN;
|
|
||||||
}
|
|
||||||
std::string normalizedPath = path;
|
|
||||||
for (size_t s = normalizedPath.find(replace, 0); s != std::string::npos;
|
|
||||||
s = normalizedPath.find(replace, s)) {
|
|
||||||
normalizedPath[s] = separator;
|
|
||||||
}
|
|
||||||
return normalizedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GetFolderString(const std::string& path) {
|
|
||||||
size_t s = path.rfind(PATH_WIN);
|
|
||||||
s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX);
|
|
||||||
return path.substr(0, s + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GetCleanPathString(const std::string& path, const PathSeparator separator) {
|
|
||||||
std::string cleanPath = path;
|
|
||||||
for (size_t s = cleanPath.find(!separator, 0); s != std::string::npos;
|
|
||||||
s = cleanPath.find(!separator, s)) {
|
|
||||||
cleanPath[s] = separator;
|
|
||||||
}
|
|
||||||
return cleanPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GetFileNameString(const std::string& path) {
|
|
||||||
size_t s = path.rfind(PATH_WIN);
|
|
||||||
s = (s != std::string::npos) ? s : path.rfind(PATH_UNIX);
|
|
||||||
return path.substr(s + 1, std::string::npos);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GetFileBaseString(const std::string& path) {
|
|
||||||
const std::string fileName = GetFileNameString(path);
|
|
||||||
return fileName.substr(0, fileName.rfind('.')).c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string GetFileSuffixString(const std::string& path) {
|
|
||||||
const std::string fileName = GetFileNameString(path);
|
|
||||||
size_t pos = fileName.rfind('.');
|
|
||||||
if (pos == std::string::npos) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return fileName.substr(++pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CompareNoCase(const std::string& s1, const std::string& s2) {
|
int CompareNoCase(const std::string& s1, const std::string& s2) {
|
||||||
return strncasecmp(s1.c_str(), s2.c_str(), MAX_PATH_LENGTH);
|
return strncasecmp(s1.c_str(), s2.c_str(), std::max(s1.length(), s2.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|
|
@ -21,34 +21,8 @@
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
static const unsigned int MAX_PATH_LENGTH = 1024;
|
inline int CompareNoCase(const std::string& s1, const std::string& s2) {
|
||||||
|
return strncasecmp(s1.c_str(), s2.c_str(), std::max(s1.length(), s2.length()));
|
||||||
enum PathSeparator { PATH_WIN = '\\', PATH_UNIX = '/' };
|
|
||||||
|
|
||||||
PathSeparator operator!(const PathSeparator& s);
|
|
||||||
|
|
||||||
PathSeparator GetPathSeparator();
|
|
||||||
const std::string NormalizePath(const std::string& path);
|
|
||||||
|
|
||||||
const std::string GetCleanPathString(
|
|
||||||
const std::string& path,
|
|
||||||
const PathSeparator separator = PATH_WIN);
|
|
||||||
|
|
||||||
template <size_t size>
|
|
||||||
void GetCleanPath(char (&dest)[size], const char* path, const PathSeparator separator = PATH_WIN) {
|
|
||||||
size_t len = size - 1;
|
|
||||||
strncpy(dest, path, len);
|
|
||||||
char* destPtr = dest;
|
|
||||||
while ((destPtr = strchr(destPtr, !separator)) != nullptr) {
|
|
||||||
*destPtr = separator;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string GetFolderString(const std::string& path);
|
|
||||||
const std::string GetFileNameString(const std::string& path);
|
|
||||||
const std::string GetFileBaseString(const std::string& path);
|
|
||||||
const std::string GetFileSuffixString(const std::string& path);
|
|
||||||
|
|
||||||
int CompareNoCase(const std::string& s1, const std::string& s2);
|
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|
Loading…
Reference in New Issue