diff --git a/.clang-format b/.clang-format index 456643f..98b90db 100644 --- a/.clang-format +++ b/.clang-format @@ -35,7 +35,7 @@ BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false -ColumnLimit: 80 +ColumnLimit: 100 CommentPragmas: '^ IWYU pragma:' ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 diff --git a/src/FBX2glTF.cpp b/src/FBX2glTF.cpp index c4ba952..6189ca7 100644 --- a/src/FBX2glTF.cpp +++ b/src/FBX2glTF.cpp @@ -35,8 +35,7 @@ int main(int argc, char* argv[]) { CLI::App app{ fmt::sprintf( - "FBX2glTF %s: Generate a glTF 2.0 representation of an FBX model.", - FBX2GLTF_VERSION), + "FBX2glTF %s: Generate a glTF 2.0 representation of an FBX model.", FBX2GLTF_VERSION), "FBX2glTF"}; app.add_flag( @@ -45,32 +44,22 @@ int main(int argc, char* argv[]) { "Include blend shape tangents, if reported present by the FBX SDK."); app.add_flag_function("-V,--version", [&](size_t count) { - fmt::printf( - "FBX2glTF version %s\nCopyright (c) 2016-2018 Oculus VR, LLC.\n", - FBX2GLTF_VERSION); + fmt::printf("FBX2glTF version %s\nCopyright (c) 2016-2018 Oculus VR, LLC.\n", FBX2GLTF_VERSION); exit(0); }); std::string inputPath; - app.add_option("FBX Model", inputPath, "The FBX model to convert.") - ->check(CLI::ExistingFile); - app.add_option("-i,--input", inputPath, "The FBX model to convert.") - ->check(CLI::ExistingFile); + app.add_option("FBX Model", inputPath, "The FBX model to convert.")->check(CLI::ExistingFile); + app.add_option("-i,--input", inputPath, "The FBX model to convert.")->check(CLI::ExistingFile); std::string outputPath; - app.add_option( - "-o,--output", - outputPath, - "Where to generate the output, without suffix."); + app.add_option("-o,--output", outputPath, "Where to generate the output, without suffix."); app.add_flag( "-e,--embed", gltfOptions.embedResources, "Inline buffers as data:// URIs within generated non-binary glTF."); - app.add_flag( - "-b,--binary", - gltfOptions.outputBinary, - "Output a single binary format .glb file."); + app.add_flag("-b,--binary", gltfOptions.outputBinary, "Output a single binary format .glb file."); app.add_option( "--long-indices", @@ -119,8 +108,7 @@ int main(int argc, char* argv[]) { "--flip-u", [&](size_t count) { if (count > 0) { - texturesTransforms.emplace_back( - [](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); }); + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(1.0f - uv[0], uv[1]); }); if (verboseOutput) { fmt::printf("Flipping texture coordinates in the 'U' dimension.\n"); } @@ -128,23 +116,20 @@ int main(int argc, char* argv[]) { }, "Flip all U texture coordinates."); - app.add_flag("--no-flip-u", "Don't flip U texture coordinates.") - ->excludes("--flip-u"); + app.add_flag("--no-flip-u", "Don't flip U texture coordinates.")->excludes("--flip-u"); app.add_flag_function( "--no-flip-v", [&](size_t count) { if (count > 0) { - texturesTransforms.emplace_back( - [](Vec2f uv) { return Vec2f(uv[0], 1.0f - uv[1]); }); + texturesTransforms.emplace_back([](Vec2f uv) { return Vec2f(uv[0], 1.0f - uv[1]); }); if (verboseOutput) { fmt::printf("NOT flipping texture coordinates in the 'V' dimension.\n"); } } }, "Flip all V texture coordinates."); - app.add_flag("--flip-v", "Don't flip U texture coordinates.") - ->excludes("--no-flip-v"); + app.add_flag("--flip-v", "Don't flip U texture coordinates.")->excludes("--no-flip-v"); app.add_flag( "--pbr-metallic-rougnness", @@ -181,8 +166,8 @@ int main(int argc, char* argv[]) { app.add_option( "-k,--keep-attribute", [&](std::vector attributes) -> bool { - gltfOptions.keepAttribs = RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | - RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + gltfOptions.keepAttribs = + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; for (std::string attribute : attributes) { if (attribute == "position") { gltfOptions.keepAttribs |= RAW_VERTEX_ATTRIBUTE_POSITION; @@ -212,9 +197,7 @@ int main(int argc, char* argv[]) { ->type_name("(position|normal|tangent|binormial|color|uv0|uv1|auto)"); app.add_flag( - "-d,--draco", - gltfOptions.draco.enabled, - "Apply Draco mesh compression to geometries.") + "-d,--draco", gltfOptions.draco.enabled, "Apply Draco mesh compression to geometries.") ->group("Draco"); app.add_option( @@ -301,16 +284,12 @@ int main(int argc, char* argv[]) { } 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(), (const char)StringUtils::GetPathSeparator()); + 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()); + fmt::fprintf(stderr, "ERROR: Failed to create folder: %s'\n", outputFolder.c_str()); return 1; } @@ -334,14 +313,9 @@ int main(int argc, char* argv[]) { std::ofstream outStream; // note: auto-flushes in destructor const auto streamStart = outStream.tellp(); - outStream.open( - modelPath, - std::ios::trunc | std::ios::ate | std::ios::out | std::ios::binary); + outStream.open(modelPath, std::ios::trunc | std::ios::ate | std::ios::out | std::ios::binary); if (outStream.fail()) { - fmt::fprintf( - stderr, - "ERROR:: Couldn't open file for writing: %s\n", - modelPath.c_str()); + fmt::fprintf(stderr, "ERROR:: Couldn't open file for writing: %s\n", modelPath.c_str()); return 1; } data_render_model = Raw2Gltf(outStream, outputFolder, raw, gltfOptions); @@ -371,8 +345,7 @@ int main(int argc, char* argv[]) { const std::string binaryPath = outputFolder + extBufferFilename; FILE* fp = fopen(binaryPath.c_str(), "wb"); if (fp == nullptr) { - fmt::fprintf( - stderr, "ERROR:: Couldn't open file '%s' for writing.\n", binaryPath); + fmt::fprintf(stderr, "ERROR:: Couldn't open file '%s' for writing.\n", binaryPath); return 1; } @@ -381,16 +354,12 @@ int main(int argc, char* argv[]) { unsigned long binarySize = data_render_model->binary->size(); if (fwrite(binaryData, binarySize, 1, fp) != 1) { fmt::fprintf( - stderr, - "ERROR: Failed to write %lu bytes to file '%s'.\n", - binarySize, - binaryPath); + stderr, "ERROR: Failed to write %lu bytes to file '%s'.\n", binarySize, binaryPath); fclose(fp); return 1; } fclose(fp); - fmt::printf( - "Wrote %lu bytes of binary data to %s.\n", binarySize, binaryPath); + fmt::printf("Wrote %lu bytes of binary data to %s.\n", binarySize, binaryPath); } delete data_render_model; diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 03125b5..719bce0 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -10,22 +10,22 @@ #include "Fbx2Raw.hpp" #include -#include -#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include +#include +#include #include "FBX2glTF.h" +#include "raw/RawModel.hpp" #include "utils/File_Utils.hpp" #include "utils/String_Utils.hpp" -#include "raw/RawModel.hpp" #include "FbxBlendShapesAccess.hpp" #include "FbxLayerElementAccess.hpp" @@ -34,373 +34,451 @@ float scaleFactor; -static bool TriangleTexturePolarity(const Vec2f &uv0, const Vec2f &uv1, const Vec2f &uv2) -{ - const Vec2f d0 = uv1 - uv0; - const Vec2f d1 = uv2 - uv0; +static bool TriangleTexturePolarity(const Vec2f& uv0, const Vec2f& uv1, const Vec2f& uv2) { + const Vec2f d0 = uv1 - uv0; + const Vec2f d1 = uv2 - uv0; - return (d0[0] * d1[1] - d0[1] * d1[0] < 0.0f); + return (d0[0] * d1[1] - d0[1] * d1[0] < 0.0f); } -static RawMaterialType -GetMaterialType(const RawModel &raw, const int textures[RAW_TEXTURE_USAGE_MAX], const bool vertexTransparency, const bool skinned) -{ - // DIFFUSE and ALBEDO are different enough to represent distinctly, but they both help determine transparency. - int diffuseTexture = textures[RAW_TEXTURE_USAGE_DIFFUSE]; - if (diffuseTexture < 0) { - diffuseTexture = textures[RAW_TEXTURE_USAGE_ALBEDO]; - } - // determine material type based on texture occlusion. - if (diffuseTexture >= 0) { - return (raw.GetTexture(diffuseTexture).occlusion == RAW_TEXTURE_OCCLUSION_OPAQUE) - ? (skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE) - : (skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT); - } +static RawMaterialType GetMaterialType( + const RawModel& raw, + const int textures[RAW_TEXTURE_USAGE_MAX], + const bool vertexTransparency, + const bool skinned) { + // DIFFUSE and ALBEDO are different enough to represent distinctly, but they both help determine + // transparency. + int diffuseTexture = textures[RAW_TEXTURE_USAGE_DIFFUSE]; + if (diffuseTexture < 0) { + diffuseTexture = textures[RAW_TEXTURE_USAGE_ALBEDO]; + } + // determine material type based on texture occlusion. + if (diffuseTexture >= 0) { + return (raw.GetTexture(diffuseTexture).occlusion == RAW_TEXTURE_OCCLUSION_OPAQUE) + ? (skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE) + : (skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT); + } - // else if there is any vertex transparency, treat whole mesh as transparent - if (vertexTransparency) { - return skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT; - } + // else if there is any vertex transparency, treat whole mesh as transparent + if (vertexTransparency) { + return skinned ? RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT : RAW_MATERIAL_TYPE_TRANSPARENT; + } - - // Default to simply opaque. - return skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE; + // Default to simply opaque. + return skinned ? RAW_MATERIAL_TYPE_SKINNED_OPAQUE : RAW_MATERIAL_TYPE_OPAQUE; } -static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std::map &textureLocations) -{ - FbxGeometryConverter meshConverter(pScene->GetFbxManager()); - meshConverter.Triangulate(pNode->GetNodeAttribute(), true); - FbxMesh *pMesh = pNode->GetMesh(); +static void ReadMesh( + RawModel& raw, + FbxScene* pScene, + FbxNode* pNode, + const std::map& textureLocations) { + FbxGeometryConverter meshConverter(pScene->GetFbxManager()); + meshConverter.Triangulate(pNode->GetNodeAttribute(), true); + FbxMesh* pMesh = pNode->GetMesh(); - // Obtains the surface Id - const long surfaceId = pMesh->GetUniqueID(); + // Obtains the surface Id + const long surfaceId = pMesh->GetUniqueID(); - // Associate the node to this surface - int nodeId = raw.GetNodeById(pNode->GetUniqueID()); - if (nodeId >= 0) { - RawNode &node = raw.GetNode(nodeId); - node.surfaceId = surfaceId; + // Associate the node to this surface + int nodeId = raw.GetNodeById(pNode->GetUniqueID()); + if (nodeId >= 0) { + RawNode& node = raw.GetNode(nodeId); + node.surfaceId = surfaceId; + } + + if (raw.GetSurfaceById(surfaceId) >= 0) { + // This surface is already loaded + return; + } + + const char* meshName = (pNode->GetName()[0] != '\0') ? pNode->GetName() : pMesh->GetName(); + const int rawSurfaceIndex = raw.AddSurface(meshName, surfaceId); + + const FbxVector4* controlPoints = pMesh->GetControlPoints(); + const FbxLayerElementAccess normalLayer( + pMesh->GetElementNormal(), pMesh->GetElementNormalCount()); + const FbxLayerElementAccess binormalLayer( + pMesh->GetElementBinormal(), pMesh->GetElementBinormalCount()); + const FbxLayerElementAccess tangentLayer( + pMesh->GetElementTangent(), pMesh->GetElementTangentCount()); + const FbxLayerElementAccess colorLayer( + pMesh->GetElementVertexColor(), pMesh->GetElementVertexColorCount()); + const FbxLayerElementAccess uvLayer0( + pMesh->GetElementUV(0), pMesh->GetElementUVCount()); + const FbxLayerElementAccess uvLayer1( + pMesh->GetElementUV(1), pMesh->GetElementUVCount()); + const FbxSkinningAccess skinning(pMesh, pScene, pNode); + const FbxMaterialsAccess materials(pMesh, textureLocations); + const FbxBlendShapesAccess blendShapes(pMesh); + + if (verboseOutput) { + fmt::printf( + "mesh %d: %s (skinned: %s)\n", + rawSurfaceIndex, + meshName, + skinning.IsSkinned() ? raw.GetNode(raw.GetNodeById(skinning.GetRootNode())).name.c_str() + : "NO"); + } + + // The FbxNode geometric transformation describes how a FbxNodeAttribute is offset from + // the FbxNode's local frame of reference. These geometric transforms are applied to the + // FbxNodeAttribute after the FbxNode's local transforms are computed, and are not + // inherited across the node hierarchy. + // Apply the geometric transform to the mesh geometry (vertices, normal etc.) because + // glTF does not have an equivalent to the geometric transform. + const FbxVector4 meshTranslation = pNode->GetGeometricTranslation(FbxNode::eSourcePivot); + const FbxVector4 meshRotation = pNode->GetGeometricRotation(FbxNode::eSourcePivot); + const FbxVector4 meshScaling = pNode->GetGeometricScaling(FbxNode::eSourcePivot); + const FbxAMatrix meshTransform(meshTranslation, meshRotation, meshScaling); + const FbxMatrix transform = meshTransform; + + // Remove translation & scaling from transforms that will bi applied to normals, tangents & + // binormals + const FbxMatrix normalTransform(FbxVector4(), meshRotation, meshScaling); + const FbxMatrix inverseTransposeTransform = normalTransform.Inverse().Transpose(); + + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_POSITION); + if (normalLayer.LayerPresent()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_NORMAL); + } + if (tangentLayer.LayerPresent()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_TANGENT); + } + if (binormalLayer.LayerPresent()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_BINORMAL); + } + if (colorLayer.LayerPresent()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_COLOR); + } + if (uvLayer0.LayerPresent()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV0); + } + if (uvLayer1.LayerPresent()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV1); + } + if (skinning.IsSkinned()) { + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS); + raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES); + } + + RawSurface& rawSurface = raw.GetSurface(rawSurfaceIndex); + + Mat4f scaleMatrix = Mat4f::FromScaleVector(Vec3f(scaleFactor, scaleFactor, scaleFactor)); + Mat4f invScaleMatrix = scaleMatrix.Inverse(); + + rawSurface.skeletonRootId = + (skinning.IsSkinned()) ? skinning.GetRootNode() : pNode->GetUniqueID(); + for (int jointIndex = 0; jointIndex < skinning.GetNodeCount(); jointIndex++) { + const long jointId = skinning.GetJointId(jointIndex); + raw.GetNode(raw.GetNodeById(jointId)).isJoint = true; + + rawSurface.jointIds.emplace_back(jointId); + rawSurface.inverseBindMatrices.push_back( + invScaleMatrix * toMat4f(skinning.GetInverseBindMatrix(jointIndex)) * scaleMatrix); + rawSurface.jointGeometryMins.emplace_back(FLT_MAX, FLT_MAX, FLT_MAX); + rawSurface.jointGeometryMaxs.emplace_back(-FLT_MAX, -FLT_MAX, -FLT_MAX); + } + + rawSurface.blendChannels.clear(); + std::vector targetShapes; + for (size_t channelIx = 0; channelIx < blendShapes.GetChannelCount(); channelIx++) { + for (size_t targetIx = 0; targetIx < blendShapes.GetTargetShapeCount(channelIx); targetIx++) { + const FbxBlendShapesAccess::TargetShape& shape = + blendShapes.GetTargetShape(channelIx, targetIx); + targetShapes.push_back(&shape); + auto& blendChannel = blendShapes.GetBlendChannel(channelIx); + + rawSurface.blendChannels.push_back( + RawBlendChannel{static_cast(blendChannel.deformPercent), + shape.normals.LayerPresent(), + shape.tangents.LayerPresent(), + blendChannel.name}); } + } - if (raw.GetSurfaceById(surfaceId) >= 0) { - // This surface is already loaded - return; - } + int polygonVertexIndex = 0; + for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) { + FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3); + const std::shared_ptr fbxMaterial = materials.GetMaterial(polygonIndex); + const std::vector userProperties = materials.GetUserProperties(polygonIndex); - const char *meshName = (pNode->GetName()[0] != '\0') ? pNode->GetName() : pMesh->GetName(); - const int rawSurfaceIndex = raw.AddSurface(meshName, surfaceId); + int textures[RAW_TEXTURE_USAGE_MAX]; + std::fill_n(textures, (int)RAW_TEXTURE_USAGE_MAX, -1); - const FbxVector4 *controlPoints = pMesh->GetControlPoints(); - const FbxLayerElementAccess normalLayer(pMesh->GetElementNormal(), pMesh->GetElementNormalCount()); - const FbxLayerElementAccess binormalLayer(pMesh->GetElementBinormal(), pMesh->GetElementBinormalCount()); - const FbxLayerElementAccess tangentLayer(pMesh->GetElementTangent(), pMesh->GetElementTangentCount()); - const FbxLayerElementAccess colorLayer(pMesh->GetElementVertexColor(), pMesh->GetElementVertexColorCount()); - const FbxLayerElementAccess uvLayer0(pMesh->GetElementUV(0), pMesh->GetElementUVCount()); - const FbxLayerElementAccess uvLayer1(pMesh->GetElementUV(1), pMesh->GetElementUVCount()); - const FbxSkinningAccess skinning(pMesh, pScene, pNode); - const FbxMaterialsAccess materials(pMesh, textureLocations); - const FbxBlendShapesAccess blendShapes(pMesh); + std::shared_ptr rawMatProps; + FbxString materialName; - if (verboseOutput) { - fmt::printf( - "mesh %d: %s (skinned: %s)\n", rawSurfaceIndex, meshName, - skinning.IsSkinned() ? raw.GetNode(raw.GetNodeById(skinning.GetRootNode())).name.c_str() : "NO"); - } + if (fbxMaterial == nullptr) { + materialName = "DefaultMaterial"; + rawMatProps.reset(new RawTraditionalMatProps( + RAW_SHADING_MODEL_LAMBERT, + Vec3f(0, 0, 0), + Vec4f(.5, .5, .5, 1), + Vec3f(0, 0, 0), + Vec3f(0, 0, 0), + 0.5)); - // The FbxNode geometric transformation describes how a FbxNodeAttribute is offset from - // the FbxNode's local frame of reference. These geometric transforms are applied to the - // FbxNodeAttribute after the FbxNode's local transforms are computed, and are not - // inherited across the node hierarchy. - // Apply the geometric transform to the mesh geometry (vertices, normal etc.) because - // glTF does not have an equivalent to the geometric transform. - const FbxVector4 meshTranslation = pNode->GetGeometricTranslation(FbxNode::eSourcePivot); - const FbxVector4 meshRotation = pNode->GetGeometricRotation(FbxNode::eSourcePivot); - const FbxVector4 meshScaling = pNode->GetGeometricScaling(FbxNode::eSourcePivot); - const FbxAMatrix meshTransform(meshTranslation, meshRotation, meshScaling); - const FbxMatrix transform = meshTransform; + } else { + materialName = fbxMaterial->name; - // Remove translation & scaling from transforms that will bi applied to normals, tangents & binormals - const FbxMatrix normalTransform(FbxVector4(), meshRotation, meshScaling); - const FbxMatrix inverseTransposeTransform = normalTransform.Inverse().Transpose(); - - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_POSITION); - if (normalLayer.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_NORMAL); } - if (tangentLayer.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_TANGENT); } - if (binormalLayer.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_BINORMAL); } - if (colorLayer.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_COLOR); } - if (uvLayer0.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV0); } - if (uvLayer1.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_UV1); } - if (skinning.IsSkinned()) { - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS); - raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_JOINT_INDICES); - } - - RawSurface &rawSurface = raw.GetSurface(rawSurfaceIndex); - - Mat4f scaleMatrix = Mat4f::FromScaleVector(Vec3f(scaleFactor, scaleFactor, scaleFactor)); - Mat4f invScaleMatrix = scaleMatrix.Inverse(); - - rawSurface.skeletonRootId = (skinning.IsSkinned()) ? skinning.GetRootNode() : pNode->GetUniqueID(); - for (int jointIndex = 0; jointIndex < skinning.GetNodeCount(); jointIndex++) { - const long jointId = skinning.GetJointId(jointIndex); - raw.GetNode(raw.GetNodeById(jointId)).isJoint = true; - - rawSurface.jointIds.emplace_back(jointId); - rawSurface.inverseBindMatrices.push_back(invScaleMatrix * toMat4f(skinning.GetInverseBindMatrix(jointIndex)) * scaleMatrix); - rawSurface.jointGeometryMins.emplace_back(FLT_MAX, FLT_MAX, FLT_MAX); - rawSurface.jointGeometryMaxs.emplace_back(-FLT_MAX, -FLT_MAX, -FLT_MAX); - } - - rawSurface.blendChannels.clear(); - std::vector targetShapes; - for (size_t channelIx = 0; channelIx < blendShapes.GetChannelCount(); channelIx ++) { - for (size_t targetIx = 0; targetIx < blendShapes.GetTargetShapeCount(channelIx); targetIx ++) { - const FbxBlendShapesAccess::TargetShape &shape = blendShapes.GetTargetShape(channelIx, targetIx); - targetShapes.push_back(&shape); - auto &blendChannel = blendShapes.GetBlendChannel(channelIx); - - rawSurface.blendChannels.push_back(RawBlendChannel { - static_cast(blendChannel.deformPercent), - shape.normals.LayerPresent(), - shape.tangents.LayerPresent(), - blendChannel.name - }); + const auto maybeAddTexture = [&](const FbxFileTexture* tex, RawTextureUsage usage) { + if (tex != nullptr) { + // dig out the inferred filename from the textureLocations map + FbxString inferredPath = textureLocations.find(tex)->second; + textures[usage] = + raw.AddTexture(tex->GetName(), tex->GetFileName(), inferredPath.Buffer(), usage); } - } + }; - int polygonVertexIndex = 0; - for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) { - FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3); - const std::shared_ptr fbxMaterial = materials.GetMaterial(polygonIndex); - const std::vector userProperties = materials.GetUserProperties(polygonIndex); - - int textures[RAW_TEXTURE_USAGE_MAX]; - std::fill_n(textures, (int) RAW_TEXTURE_USAGE_MAX, -1); - - std::shared_ptr rawMatProps; - FbxString materialName; - - if (fbxMaterial == nullptr) { - materialName = "DefaultMaterial"; - rawMatProps.reset(new RawTraditionalMatProps(RAW_SHADING_MODEL_LAMBERT, - Vec3f(0, 0, 0), Vec4f(.5, .5, .5, 1), Vec3f(0, 0, 0), Vec3f(0, 0, 0), 0.5)); + std::shared_ptr matInfo; + if (fbxMaterial->shadingModel == FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH) { + FbxRoughMetMaterialInfo* fbxMatInfo = + static_cast(fbxMaterial.get()); + maybeAddTexture(fbxMatInfo->texColor, RAW_TEXTURE_USAGE_ALBEDO); + maybeAddTexture(fbxMatInfo->texNormal, RAW_TEXTURE_USAGE_NORMAL); + maybeAddTexture(fbxMatInfo->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE); + maybeAddTexture(fbxMatInfo->texRoughness, RAW_TEXTURE_USAGE_ROUGHNESS); + maybeAddTexture(fbxMatInfo->texMetallic, RAW_TEXTURE_USAGE_METALLIC); + maybeAddTexture(fbxMatInfo->texAmbientOcclusion, RAW_TEXTURE_USAGE_OCCLUSION); + rawMatProps.reset(new RawMetRoughMatProps( + RAW_SHADING_MODEL_PBR_MET_ROUGH, + toVec4f(fbxMatInfo->colBase), + toVec3f(fbxMatInfo->colEmissive), + fbxMatInfo->emissiveIntensity, + fbxMatInfo->metallic, + fbxMatInfo->roughness)); + } else { + FbxTraditionalMaterialInfo* fbxMatInfo = + static_cast(fbxMaterial.get()); + RawShadingModel shadingModel; + if (fbxMaterial->shadingModel == "Lambert") { + shadingModel = RAW_SHADING_MODEL_LAMBERT; + } else if (fbxMaterial->shadingModel == "Blinn") { + shadingModel = RAW_SHADING_MODEL_BLINN; + } else if (fbxMaterial->shadingModel == "Phong") { + shadingModel = RAW_SHADING_MODEL_PHONG; + } else if (fbxMaterial->shadingModel == "Constant") { + shadingModel = RAW_SHADING_MODEL_PHONG; } else { - materialName = fbxMaterial->name; - - const auto maybeAddTexture = [&](const FbxFileTexture *tex, RawTextureUsage usage) { - if (tex != nullptr) { - // dig out the inferred filename from the textureLocations map - FbxString inferredPath = textureLocations.find(tex)->second; - textures[usage] = raw.AddTexture(tex->GetName(), tex->GetFileName(), inferredPath.Buffer(), usage); - } - }; - - std::shared_ptr matInfo; - if (fbxMaterial->shadingModel == FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH) { - FbxRoughMetMaterialInfo *fbxMatInfo = static_cast(fbxMaterial.get()); - - maybeAddTexture(fbxMatInfo->texColor, RAW_TEXTURE_USAGE_ALBEDO); - maybeAddTexture(fbxMatInfo->texNormal, RAW_TEXTURE_USAGE_NORMAL); - maybeAddTexture(fbxMatInfo->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE); - maybeAddTexture(fbxMatInfo->texRoughness, RAW_TEXTURE_USAGE_ROUGHNESS); - maybeAddTexture(fbxMatInfo->texMetallic, RAW_TEXTURE_USAGE_METALLIC); - maybeAddTexture(fbxMatInfo->texAmbientOcclusion, RAW_TEXTURE_USAGE_OCCLUSION); - rawMatProps.reset(new RawMetRoughMatProps( - RAW_SHADING_MODEL_PBR_MET_ROUGH, toVec4f(fbxMatInfo->colBase), toVec3f(fbxMatInfo->colEmissive), - fbxMatInfo->emissiveIntensity, fbxMatInfo->metallic, fbxMatInfo->roughness)); - } else { - - FbxTraditionalMaterialInfo *fbxMatInfo = static_cast(fbxMaterial.get()); - RawShadingModel shadingModel; - if (fbxMaterial->shadingModel == "Lambert") { - shadingModel = RAW_SHADING_MODEL_LAMBERT; - } else if (fbxMaterial->shadingModel == "Blinn") { - shadingModel = RAW_SHADING_MODEL_BLINN; - } else if (fbxMaterial->shadingModel == "Phong") { - shadingModel = RAW_SHADING_MODEL_PHONG; - } else if (fbxMaterial->shadingModel == "Constant") { - shadingModel = RAW_SHADING_MODEL_PHONG; - } else { - shadingModel = RAW_SHADING_MODEL_UNKNOWN; - } - maybeAddTexture(fbxMatInfo->texDiffuse, RAW_TEXTURE_USAGE_DIFFUSE); - maybeAddTexture(fbxMatInfo->texNormal, RAW_TEXTURE_USAGE_NORMAL); - maybeAddTexture(fbxMatInfo->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE); - maybeAddTexture(fbxMatInfo->texShininess, RAW_TEXTURE_USAGE_SHININESS); - maybeAddTexture(fbxMatInfo->texAmbient, RAW_TEXTURE_USAGE_AMBIENT); - maybeAddTexture(fbxMatInfo->texSpecular, RAW_TEXTURE_USAGE_SPECULAR); - rawMatProps.reset(new RawTraditionalMatProps(shadingModel, - toVec3f(fbxMatInfo->colAmbient), toVec4f(fbxMatInfo->colDiffuse), toVec3f(fbxMatInfo->colEmissive), - toVec3f(fbxMatInfo->colSpecular), fbxMatInfo->shininess)); - } + shadingModel = RAW_SHADING_MODEL_UNKNOWN; } - - RawVertex rawVertices[3]; - bool vertexTransparency = false; - for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++, polygonVertexIndex++) { - const int controlPointIndex = pMesh->GetPolygonVertex(polygonIndex, vertexIndex); - - // Note that the default values here must be the same as the RawVertex default values! - const FbxVector4 fbxPosition = transform.MultNormalize(controlPoints[controlPointIndex]); - const FbxVector4 fbxNormal = normalLayer.GetElement( - polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), inverseTransposeTransform, true); - const FbxVector4 fbxTangent = tangentLayer.GetElement( - polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), inverseTransposeTransform, true); - const FbxVector4 fbxBinormal = binormalLayer.GetElement( - polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), inverseTransposeTransform, true); - const FbxColor fbxColor = colorLayer - .GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, FbxColor(0.0f, 0.0f, 0.0f, 0.0f)); - const FbxVector2 fbxUV0 = uvLayer0.GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector2(0.0f, 0.0f)); - const FbxVector2 fbxUV1 = uvLayer1.GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector2(0.0f, 0.0f)); - - RawVertex &vertex = rawVertices[vertexIndex]; - vertex.position[0] = (float) fbxPosition[0] * scaleFactor; - vertex.position[1] = (float) fbxPosition[1] * scaleFactor; - vertex.position[2] = (float) fbxPosition[2] * scaleFactor; - vertex.normal[0] = (float) fbxNormal[0]; - vertex.normal[1] = (float) fbxNormal[1]; - vertex.normal[2] = (float) fbxNormal[2]; - vertex.tangent[0] = (float) fbxTangent[0]; - vertex.tangent[1] = (float) fbxTangent[1]; - vertex.tangent[2] = (float) fbxTangent[2]; - vertex.tangent[3] = (float) fbxTangent[3]; - vertex.binormal[0] = (float) fbxBinormal[0]; - vertex.binormal[1] = (float) fbxBinormal[1]; - vertex.binormal[2] = (float) fbxBinormal[2]; - vertex.color[0] = (float) fbxColor.mRed; - vertex.color[1] = (float) fbxColor.mGreen; - vertex.color[2] = (float) fbxColor.mBlue; - vertex.color[3] = (float) fbxColor.mAlpha; - vertex.uv0[0] = (float) fbxUV0[0]; - vertex.uv0[1] = (float) fbxUV0[1]; - vertex.uv1[0] = (float) fbxUV1[0]; - vertex.uv1[1] = (float) fbxUV1[1]; - vertex.jointIndices = skinning.GetVertexIndices(controlPointIndex); - vertex.jointWeights = skinning.GetVertexWeights(controlPointIndex); - vertex.polarityUv0 = false; - - // flag this triangle as transparent if any of its corner vertices substantially deviates from fully opaque - vertexTransparency |= colorLayer.LayerPresent() && (fabs(fbxColor.mAlpha - 1.0) > 1e-3); - - rawSurface.bounds.AddPoint(vertex.position); - - if (!targetShapes.empty()) { - vertex.blendSurfaceIx = rawSurfaceIndex; - for (const auto *targetShape : targetShapes) { - RawBlendVertex blendVertex; - // the morph target data must be transformed just as with the vertex positions above - const FbxVector4 &shapePosition = transform.MultNormalize(targetShape->positions[controlPointIndex]); - blendVertex.position = toVec3f(shapePosition - fbxPosition) * scaleFactor; - if (targetShape->normals.LayerPresent()) { - const FbxVector4 &normal = targetShape->normals.GetElement( - polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), inverseTransposeTransform, true); - blendVertex.normal = toVec3f(normal - fbxNormal); - } - if (targetShape->tangents.LayerPresent()) { - const FbxVector4 &tangent = targetShape->tangents.GetElement( - polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), inverseTransposeTransform, true); - blendVertex.tangent = toVec4f(tangent - fbxTangent); - } - vertex.blends.push_back(blendVertex); - } - } else { - vertex.blendSurfaceIx = -1; - } - - if (skinning.IsSkinned()) { - const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = { - vertex.jointIndices[0], - vertex.jointIndices[1], - vertex.jointIndices[2], - vertex.jointIndices[3] - }; - const float jointWeights[FbxSkinningAccess::MAX_WEIGHTS] = { - vertex.jointWeights[0], - vertex.jointWeights[1], - vertex.jointWeights[2], - vertex.jointWeights[3] - }; - const FbxMatrix skinningMatrix = - skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] + - skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] + - skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] + - skinning.GetJointSkinningTransform(jointIndices[3]) * jointWeights[3]; - - const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition); - for (int i = 0; i < FbxSkinningAccess::MAX_WEIGHTS; i++) { - if (jointWeights[i] > 0.0f) { - const FbxVector4 localPosition = - skinning.GetJointInverseGlobalTransforms(jointIndices[i]).MultNormalize(globalPosition); - - Vec3f &mins = rawSurface.jointGeometryMins[jointIndices[i]]; - mins[0] = std::min(mins[0], (float) localPosition[0]); - mins[1] = std::min(mins[1], (float) localPosition[1]); - mins[2] = std::min(mins[2], (float) localPosition[2]); - - Vec3f &maxs = rawSurface.jointGeometryMaxs[jointIndices[i]]; - maxs[0] = std::max(maxs[0], (float) localPosition[0]); - maxs[1] = std::max(maxs[1], (float) localPosition[1]); - maxs[2] = std::max(maxs[2], (float) localPosition[2]); - } - } - } - } - - if (textures[RAW_TEXTURE_USAGE_NORMAL] != -1) { - // Distinguish vertices that are used by triangles with a different texture polarity to avoid degenerate tangent space smoothing. - const bool polarity = TriangleTexturePolarity(rawVertices[0].uv0, rawVertices[1].uv0, rawVertices[2].uv0); - rawVertices[0].polarityUv0 = polarity; - rawVertices[1].polarityUv0 = polarity; - rawVertices[2].polarityUv0 = polarity; - } - - int rawVertexIndices[3]; - for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++) { - rawVertexIndices[vertexIndex] = raw.AddVertex(rawVertices[vertexIndex]); - } - - const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned()); - const int rawMaterialIndex = raw.AddMaterial(materialName, materialType, textures, rawMatProps, userProperties); - - raw.AddTriangle(rawVertexIndices[0], rawVertexIndices[1], rawVertexIndices[2], rawMaterialIndex, rawSurfaceIndex); + maybeAddTexture(fbxMatInfo->texDiffuse, RAW_TEXTURE_USAGE_DIFFUSE); + maybeAddTexture(fbxMatInfo->texNormal, RAW_TEXTURE_USAGE_NORMAL); + maybeAddTexture(fbxMatInfo->texEmissive, RAW_TEXTURE_USAGE_EMISSIVE); + maybeAddTexture(fbxMatInfo->texShininess, RAW_TEXTURE_USAGE_SHININESS); + maybeAddTexture(fbxMatInfo->texAmbient, RAW_TEXTURE_USAGE_AMBIENT); + maybeAddTexture(fbxMatInfo->texSpecular, RAW_TEXTURE_USAGE_SPECULAR); + rawMatProps.reset(new RawTraditionalMatProps( + shadingModel, + toVec3f(fbxMatInfo->colAmbient), + toVec4f(fbxMatInfo->colDiffuse), + toVec3f(fbxMatInfo->colEmissive), + toVec3f(fbxMatInfo->colSpecular), + fbxMatInfo->shininess)); + } } + + RawVertex rawVertices[3]; + bool vertexTransparency = false; + for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++, polygonVertexIndex++) { + const int controlPointIndex = pMesh->GetPolygonVertex(polygonIndex, vertexIndex); + + // Note that the default values here must be the same as the RawVertex default values! + const FbxVector4 fbxPosition = transform.MultNormalize(controlPoints[controlPointIndex]); + const FbxVector4 fbxNormal = normalLayer.GetElement( + polygonIndex, + polygonVertexIndex, + controlPointIndex, + FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), + inverseTransposeTransform, + true); + const FbxVector4 fbxTangent = tangentLayer.GetElement( + polygonIndex, + polygonVertexIndex, + controlPointIndex, + FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), + inverseTransposeTransform, + true); + const FbxVector4 fbxBinormal = binormalLayer.GetElement( + polygonIndex, + polygonVertexIndex, + controlPointIndex, + FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), + inverseTransposeTransform, + true); + const FbxColor fbxColor = colorLayer.GetElement( + polygonIndex, polygonVertexIndex, controlPointIndex, FbxColor(0.0f, 0.0f, 0.0f, 0.0f)); + const FbxVector2 fbxUV0 = uvLayer0.GetElement( + polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector2(0.0f, 0.0f)); + const FbxVector2 fbxUV1 = uvLayer1.GetElement( + polygonIndex, polygonVertexIndex, controlPointIndex, FbxVector2(0.0f, 0.0f)); + + RawVertex& vertex = rawVertices[vertexIndex]; + vertex.position[0] = (float)fbxPosition[0] * scaleFactor; + vertex.position[1] = (float)fbxPosition[1] * scaleFactor; + vertex.position[2] = (float)fbxPosition[2] * scaleFactor; + vertex.normal[0] = (float)fbxNormal[0]; + vertex.normal[1] = (float)fbxNormal[1]; + vertex.normal[2] = (float)fbxNormal[2]; + vertex.tangent[0] = (float)fbxTangent[0]; + vertex.tangent[1] = (float)fbxTangent[1]; + vertex.tangent[2] = (float)fbxTangent[2]; + vertex.tangent[3] = (float)fbxTangent[3]; + vertex.binormal[0] = (float)fbxBinormal[0]; + vertex.binormal[1] = (float)fbxBinormal[1]; + vertex.binormal[2] = (float)fbxBinormal[2]; + vertex.color[0] = (float)fbxColor.mRed; + vertex.color[1] = (float)fbxColor.mGreen; + vertex.color[2] = (float)fbxColor.mBlue; + vertex.color[3] = (float)fbxColor.mAlpha; + vertex.uv0[0] = (float)fbxUV0[0]; + vertex.uv0[1] = (float)fbxUV0[1]; + vertex.uv1[0] = (float)fbxUV1[0]; + vertex.uv1[1] = (float)fbxUV1[1]; + vertex.jointIndices = skinning.GetVertexIndices(controlPointIndex); + vertex.jointWeights = skinning.GetVertexWeights(controlPointIndex); + vertex.polarityUv0 = false; + + // flag this triangle as transparent if any of its corner vertices substantially deviates from + // fully opaque + vertexTransparency |= colorLayer.LayerPresent() && (fabs(fbxColor.mAlpha - 1.0) > 1e-3); + + rawSurface.bounds.AddPoint(vertex.position); + + if (!targetShapes.empty()) { + vertex.blendSurfaceIx = rawSurfaceIndex; + for (const auto* targetShape : targetShapes) { + RawBlendVertex blendVertex; + // the morph target data must be transformed just as with the vertex positions above + const FbxVector4& shapePosition = + transform.MultNormalize(targetShape->positions[controlPointIndex]); + blendVertex.position = toVec3f(shapePosition - fbxPosition) * scaleFactor; + if (targetShape->normals.LayerPresent()) { + const FbxVector4& normal = targetShape->normals.GetElement( + polygonIndex, + polygonVertexIndex, + controlPointIndex, + FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), + inverseTransposeTransform, + true); + blendVertex.normal = toVec3f(normal - fbxNormal); + } + if (targetShape->tangents.LayerPresent()) { + const FbxVector4& tangent = targetShape->tangents.GetElement( + polygonIndex, + polygonVertexIndex, + controlPointIndex, + FbxVector4(0.0f, 0.0f, 0.0f, 0.0f), + inverseTransposeTransform, + true); + blendVertex.tangent = toVec4f(tangent - fbxTangent); + } + vertex.blends.push_back(blendVertex); + } + } else { + vertex.blendSurfaceIx = -1; + } + + if (skinning.IsSkinned()) { + const int jointIndices[FbxSkinningAccess::MAX_WEIGHTS] = {vertex.jointIndices[0], + vertex.jointIndices[1], + vertex.jointIndices[2], + vertex.jointIndices[3]}; + const float jointWeights[FbxSkinningAccess::MAX_WEIGHTS] = {vertex.jointWeights[0], + vertex.jointWeights[1], + vertex.jointWeights[2], + vertex.jointWeights[3]}; + const FbxMatrix skinningMatrix = + skinning.GetJointSkinningTransform(jointIndices[0]) * jointWeights[0] + + skinning.GetJointSkinningTransform(jointIndices[1]) * jointWeights[1] + + skinning.GetJointSkinningTransform(jointIndices[2]) * jointWeights[2] + + skinning.GetJointSkinningTransform(jointIndices[3]) * jointWeights[3]; + + const FbxVector4 globalPosition = skinningMatrix.MultNormalize(fbxPosition); + for (int i = 0; i < FbxSkinningAccess::MAX_WEIGHTS; i++) { + if (jointWeights[i] > 0.0f) { + const FbxVector4 localPosition = + skinning.GetJointInverseGlobalTransforms(jointIndices[i]) + .MultNormalize(globalPosition); + + Vec3f& mins = rawSurface.jointGeometryMins[jointIndices[i]]; + mins[0] = std::min(mins[0], (float)localPosition[0]); + mins[1] = std::min(mins[1], (float)localPosition[1]); + mins[2] = std::min(mins[2], (float)localPosition[2]); + + Vec3f& maxs = rawSurface.jointGeometryMaxs[jointIndices[i]]; + maxs[0] = std::max(maxs[0], (float)localPosition[0]); + maxs[1] = std::max(maxs[1], (float)localPosition[1]); + maxs[2] = std::max(maxs[2], (float)localPosition[2]); + } + } + } + } + + if (textures[RAW_TEXTURE_USAGE_NORMAL] != -1) { + // Distinguish vertices that are used by triangles with a different texture polarity to avoid + // degenerate tangent space smoothing. + const bool polarity = + TriangleTexturePolarity(rawVertices[0].uv0, rawVertices[1].uv0, rawVertices[2].uv0); + rawVertices[0].polarityUv0 = polarity; + rawVertices[1].polarityUv0 = polarity; + rawVertices[2].polarityUv0 = polarity; + } + + int rawVertexIndices[3]; + for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++) { + rawVertexIndices[vertexIndex] = raw.AddVertex(rawVertices[vertexIndex]); + } + + const RawMaterialType materialType = + GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned()); + const int rawMaterialIndex = + raw.AddMaterial(materialName, materialType, textures, rawMatProps, userProperties); + + raw.AddTriangle( + rawVertexIndices[0], + rawVertexIndices[1], + rawVertexIndices[2], + rawMaterialIndex, + rawSurfaceIndex); + } } -//ar : aspectY / aspectX -double HFOV2VFOV(double h, double ar) -{ - return 2.0 * std::atan((ar) * std::tan((h * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI; +// ar : aspectY / aspectX +double HFOV2VFOV(double h, double ar) { + return 2.0 * std::atan((ar)*std::tan((h * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI; }; -//ar : aspectX / aspectY -double VFOV2HFOV(double v, double ar) -{ - return 2.0 * std::atan((ar) * std::tan((v * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI; +// ar : aspectX / aspectY +double VFOV2HFOV(double v, double ar) { + return 2.0 * std::atan((ar)*std::tan((v * FBXSDK_PI_DIV_180) * 0.5)) * FBXSDK_180_DIV_PI; } -static void ReadLight(RawModel &raw, FbxScene *pScene, FbxNode *pNode) { - const FbxLight *pLight = pNode->GetLight(); +static void ReadLight(RawModel& raw, FbxScene* pScene, FbxNode* pNode) { + const FbxLight* pLight = pNode->GetLight(); int lightIx; float intensity = (float)pLight->Intensity.Get(); Vec3f color = toVec3f(pLight->Color.Get()); switch (pLight->LightType.Get()) { case FbxLight::eDirectional: { - lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_DIRECTIONAL, - color, intensity, 0, 0); + lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_DIRECTIONAL, color, intensity, 0, 0); break; } case FbxLight::ePoint: { - lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_POINT, color, - intensity, 0, 0); + lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_POINT, color, intensity, 0, 0); break; } case FbxLight::eSpot: { - lightIx = raw.AddLight(pLight->GetName(), RAW_LIGHT_TYPE_SPOT, color, - intensity, (float)pLight->InnerAngle.Get(), - (float)pLight->OuterAngle.Get()); + lightIx = raw.AddLight( + pLight->GetName(), + RAW_LIGHT_TYPE_SPOT, + color, + intensity, + (float)pLight->InnerAngle.Get(), + (float)pLight->OuterAngle.Get()); break; } default: { @@ -410,441 +488,460 @@ static void ReadLight(RawModel &raw, FbxScene *pScene, FbxNode *pNode) { } int nodeId = raw.GetNodeById(pNode->GetUniqueID()); - RawNode &node = raw.GetNode(nodeId); + RawNode& node = raw.GetNode(nodeId); node.lightIx = lightIx; } -// Largely adopted from fbx example -static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode) -{ - const FbxCamera *pCamera = pNode->GetCamera(); +// Largely adopted from fbx example +static void ReadCamera(RawModel& raw, FbxScene* pScene, FbxNode* pNode) { + const FbxCamera* pCamera = pNode->GetCamera(); - double filmHeight = pCamera->GetApertureHeight(); - double filmWidth = pCamera->GetApertureWidth() * pCamera->GetSqueezeRatio(); - - // note Height : Width - double apertureRatio = filmHeight / filmWidth; + double filmHeight = pCamera->GetApertureHeight(); + double filmWidth = pCamera->GetApertureWidth() * pCamera->GetSqueezeRatio(); - double fovx = 0.0f; - double fovy = 0.0f; + // note Height : Width + double apertureRatio = filmHeight / filmWidth; - switch(pCamera->GetApertureMode()) - { - case FbxCamera::EApertureMode::eHorizAndVert: { - fovx = pCamera->FieldOfViewX; - fovy = pCamera->FieldOfViewY; - break; - } - case FbxCamera::EApertureMode::eHorizontal: { - fovx = pCamera->FieldOfViewX; - fovy = HFOV2VFOV(fovx, apertureRatio); - break; - } - case FbxCamera::EApertureMode::eVertical: { - fovy = pCamera->FieldOfViewY; - fovx = VFOV2HFOV(fovy, 1.0 / apertureRatio); - break; - } - case FbxCamera::EApertureMode::eFocalLength: { - fovx = pCamera->ComputeFieldOfView(pCamera->FocalLength); - fovy = HFOV2VFOV(fovx, apertureRatio); - break; - } - default: { - fmt::printf("Warning:: Unsupported ApertureMode. Setting FOV to 0.\n"); - break; - } + double fovx = 0.0f; + double fovy = 0.0f; + + switch (pCamera->GetApertureMode()) { + case FbxCamera::EApertureMode::eHorizAndVert: { + fovx = pCamera->FieldOfViewX; + fovy = pCamera->FieldOfViewY; + break; } - - if (pCamera->ProjectionType.Get() == FbxCamera::EProjectionType::ePerspective) { - raw.AddCameraPerspective( - "", pNode->GetUniqueID(), (float) pCamera->FilmAspectRatio, - (float) fovx, (float) fovy, - (float) pCamera->NearPlane, (float) pCamera->FarPlane); - } else { - raw.AddCameraOrthographic( - "", pNode->GetUniqueID(), - (float) pCamera->OrthoZoom, (float) pCamera->OrthoZoom, - (float) pCamera->FarPlane, (float) pCamera->NearPlane); + case FbxCamera::EApertureMode::eHorizontal: { + fovx = pCamera->FieldOfViewX; + fovy = HFOV2VFOV(fovx, apertureRatio); + break; } + case FbxCamera::EApertureMode::eVertical: { + fovy = pCamera->FieldOfViewY; + fovx = VFOV2HFOV(fovy, 1.0 / apertureRatio); + break; + } + case FbxCamera::EApertureMode::eFocalLength: { + fovx = pCamera->ComputeFieldOfView(pCamera->FocalLength); + fovy = HFOV2VFOV(fovx, apertureRatio); + break; + } + default: { + fmt::printf("Warning:: Unsupported ApertureMode. Setting FOV to 0.\n"); + break; + } + } - // Cameras in FBX coordinate space face +X when rotation is (0,0,0) - // We need to adjust this to face glTF specified -Z - auto nodeIdx = raw.GetNodeById(pNode->GetUniqueID()); - auto& rawNode = raw.GetNode(nodeIdx); + if (pCamera->ProjectionType.Get() == FbxCamera::EProjectionType::ePerspective) { + raw.AddCameraPerspective( + "", + pNode->GetUniqueID(), + (float)pCamera->FilmAspectRatio, + (float)fovx, + (float)fovy, + (float)pCamera->NearPlane, + (float)pCamera->FarPlane); + } else { + raw.AddCameraOrthographic( + "", + pNode->GetUniqueID(), + (float)pCamera->OrthoZoom, + (float)pCamera->OrthoZoom, + (float)pCamera->FarPlane, + (float)pCamera->NearPlane); + } - auto r = Quatf::FromAngleAxis(-90 * ((float) M_PI / 180.0f), {0.0, 1.0, 0.0}); - rawNode.rotation = rawNode.rotation * r; + // Cameras in FBX coordinate space face +X when rotation is (0,0,0) + // We need to adjust this to face glTF specified -Z + auto nodeIdx = raw.GetNodeById(pNode->GetUniqueID()); + auto& rawNode = raw.GetNode(nodeIdx); + + auto r = Quatf::FromAngleAxis(-90 * ((float)M_PI / 180.0f), {0.0, 1.0, 0.0}); + rawNode.rotation = rawNode.rotation * r; } -static void ReadNodeProperty(RawModel &raw, FbxNode *pNode, FbxProperty &prop) -{ - int nodeId = raw.GetNodeById(pNode->GetUniqueID()); - if (nodeId >= 0) { - RawNode &node = raw.GetNode(nodeId); - node.userProperties.push_back(TranscribeProperty(prop).dump()); - } +static void ReadNodeProperty(RawModel& raw, FbxNode* pNode, FbxProperty& prop) { + int nodeId = raw.GetNodeById(pNode->GetUniqueID()); + if (nodeId >= 0) { + RawNode& node = raw.GetNode(nodeId); + node.userProperties.push_back(TranscribeProperty(prop).dump()); + } } static void ReadNodeAttributes( - RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std::map &textureLocations) -{ - if (!pNode->GetVisibility()) { - return; + RawModel& raw, + FbxScene* pScene, + FbxNode* pNode, + const std::map& textureLocations) { + if (!pNode->GetVisibility()) { + return; + } + + // Only support non-animated user defined properties for now + FbxProperty objectProperty = pNode->GetFirstProperty(); + while (objectProperty.IsValid()) { + if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) { + ReadNodeProperty(raw, pNode, objectProperty); } - // Only support non-animated user defined properties for now - FbxProperty objectProperty = pNode->GetFirstProperty(); - while (objectProperty.IsValid()) - { - if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) { - ReadNodeProperty(raw, pNode, objectProperty); - } + objectProperty = pNode->GetNextProperty(objectProperty); + } - objectProperty = pNode->GetNextProperty(objectProperty); + FbxNodeAttribute* pNodeAttribute = pNode->GetNodeAttribute(); + if (pNodeAttribute != nullptr) { + const FbxNodeAttribute::EType attributeType = pNodeAttribute->GetAttributeType(); + switch (attributeType) { + case FbxNodeAttribute::eMesh: + case FbxNodeAttribute::eNurbs: + case FbxNodeAttribute::eNurbsSurface: + case FbxNodeAttribute::eTrimNurbsSurface: + case FbxNodeAttribute::ePatch: { + ReadMesh(raw, pScene, pNode, textureLocations); + break; + } + case FbxNodeAttribute::eCamera: { + ReadCamera(raw, pScene, pNode); + break; + } + case FbxNodeAttribute::eLight: + ReadLight(raw, pScene, pNode); + break; + case FbxNodeAttribute::eUnknown: + case FbxNodeAttribute::eNull: + case FbxNodeAttribute::eMarker: + case FbxNodeAttribute::eSkeleton: + case FbxNodeAttribute::eCameraStereo: + case FbxNodeAttribute::eCameraSwitcher: + case FbxNodeAttribute::eOpticalReference: + case FbxNodeAttribute::eOpticalMarker: + case FbxNodeAttribute::eNurbsCurve: + case FbxNodeAttribute::eBoundary: + case FbxNodeAttribute::eShape: + case FbxNodeAttribute::eLODGroup: + case FbxNodeAttribute::eSubDiv: + case FbxNodeAttribute::eCachedEffect: + case FbxNodeAttribute::eLine: { + break; + } } + } - FbxNodeAttribute *pNodeAttribute = pNode->GetNodeAttribute(); - if (pNodeAttribute != nullptr) { - const FbxNodeAttribute::EType attributeType = pNodeAttribute->GetAttributeType(); - switch (attributeType) { - case FbxNodeAttribute::eMesh: - case FbxNodeAttribute::eNurbs: - case FbxNodeAttribute::eNurbsSurface: - case FbxNodeAttribute::eTrimNurbsSurface: - case FbxNodeAttribute::ePatch: { - ReadMesh(raw, pScene, pNode, textureLocations); - break; - } - case FbxNodeAttribute::eCamera: { - ReadCamera(raw, pScene, pNode); - break; - } - case FbxNodeAttribute::eLight: - ReadLight(raw, pScene, pNode); - break; - case FbxNodeAttribute::eUnknown: - case FbxNodeAttribute::eNull: - case FbxNodeAttribute::eMarker: - case FbxNodeAttribute::eSkeleton: - case FbxNodeAttribute::eCameraStereo: - case FbxNodeAttribute::eCameraSwitcher: - case FbxNodeAttribute::eOpticalReference: - case FbxNodeAttribute::eOpticalMarker: - case FbxNodeAttribute::eNurbsCurve: - case FbxNodeAttribute::eBoundary: - case FbxNodeAttribute::eShape: - case FbxNodeAttribute::eLODGroup: - case FbxNodeAttribute::eSubDiv: - case FbxNodeAttribute::eCachedEffect: - case FbxNodeAttribute::eLine: { - break; - } - } - } - - for (int child = 0; child < pNode->GetChildCount(); child++) { - ReadNodeAttributes(raw, pScene, pNode->GetChild(child), textureLocations); - } + for (int child = 0; child < pNode->GetChildCount(); child++) { + ReadNodeAttributes(raw, pScene, pNode->GetChild(child), textureLocations); + } } /** * Compute the local scale vector to use for a given node. This is an imperfect hack to cope with * the FBX node transform's eInheritRrs inheritance type, in which ancestral scale is ignored */ -static FbxVector4 computeLocalScale(FbxNode *pNode, FbxTime pTime = FBXSDK_TIME_INFINITE) -{ - const FbxVector4 lScale = pNode->EvaluateLocalTransform(pTime).GetS(); +static FbxVector4 computeLocalScale(FbxNode* pNode, FbxTime pTime = FBXSDK_TIME_INFINITE) { + const FbxVector4 lScale = pNode->EvaluateLocalTransform(pTime).GetS(); - if (pNode->GetParent() == nullptr || - pNode->GetTransform().GetInheritType() != FbxTransform::eInheritRrs) { - return lScale; - } - // This is a very partial fix that is only correct for models that use identity scale in their rig's joints. - // We could write better support that compares local scale to parent's global scale and apply the ratio to - // our local translation. We'll always want to return scale 1, though -- that's the only way to encode the - // missing 'S' (parent scale) in the transform chain. - return FbxVector4(1, 1, 1, 1); + if (pNode->GetParent() == nullptr || + pNode->GetTransform().GetInheritType() != FbxTransform::eInheritRrs) { + return lScale; + } + // This is a very partial fix that is only correct for models that use identity scale in their + // rig's joints. We could write better support that compares local scale to parent's global scale + // and apply the ratio to our local translation. We'll always want to return scale 1, though -- + // that's the only way to encode the missing 'S' (parent scale) in the transform chain. + return FbxVector4(1, 1, 1, 1); } static void ReadNodeHierarchy( - RawModel &raw, FbxScene *pScene, FbxNode *pNode, - const long parentId, const std::string &path) -{ - const FbxUInt64 nodeId = pNode->GetUniqueID(); - const char *nodeName = pNode->GetName(); - const int nodeIndex = raw.AddNode(nodeId, nodeName, parentId); - RawNode &node = raw.GetNode(nodeIndex); + RawModel& raw, + FbxScene* pScene, + FbxNode* pNode, + const long parentId, + const std::string& path) { + const FbxUInt64 nodeId = pNode->GetUniqueID(); + const char* nodeName = pNode->GetName(); + const int nodeIndex = raw.AddNode(nodeId, nodeName, parentId); + RawNode& node = raw.GetNode(nodeIndex); - FbxTransform::EInheritType lInheritType; - pNode->GetTransformationInheritType(lInheritType); + FbxTransform::EInheritType lInheritType; + pNode->GetTransformationInheritType(lInheritType); - std::string newPath = path + "/" + nodeName; + std::string newPath = path + "/" + nodeName; + if (verboseOutput) { + fmt::printf("node %d: %s\n", nodeIndex, newPath.c_str()); + } + + static int warnRrSsCount = 0; + static int warnRrsCount = 0; + if (lInheritType == FbxTransform::eInheritRrSs && parentId) { + if (++warnRrSsCount == 1) { + fmt::printf( + "Warning: node %s uses unsupported transform inheritance type 'eInheritRrSs'.\n", + newPath); + fmt::printf(" (Further warnings of this type squelched.)\n"); + } + + } else if (lInheritType == FbxTransform::eInheritRrs) { + if (++warnRrsCount == 1) { + fmt::printf( + "Warning: node %s uses unsupported transform inheritance type 'eInheritRrs'\n" + " This tool will attempt to partially compensate, but glTF cannot truly express this mode.\n" + " If this was a Maya export, consider turning off 'Segment Scale Compensate' on all joints.\n" + " (Further warnings of this type squelched.)\n", + newPath); + } + } + + // Set the initial node transform. + const FbxAMatrix localTransform = pNode->EvaluateLocalTransform(); + const FbxVector4 localTranslation = localTransform.GetT(); + const FbxQuaternion localRotation = localTransform.GetQ(); + const FbxVector4 localScaling = computeLocalScale(pNode); + + node.translation = toVec3f(localTranslation) * scaleFactor; + node.rotation = toQuatf(localRotation); + node.scale = toVec3f(localScaling); + + if (parentId) { + RawNode& parentNode = raw.GetNode(raw.GetNodeById(parentId)); + // Add unique child name to the parent node. + if (std::find(parentNode.childIds.begin(), parentNode.childIds.end(), nodeId) == + parentNode.childIds.end()) { + parentNode.childIds.push_back(nodeId); + } + } else { + // If there is no parent then this is the root node. + raw.SetRootNode(nodeId); + } + + for (int child = 0; child < pNode->GetChildCount(); child++) { + ReadNodeHierarchy(raw, pScene, pNode->GetChild(child), nodeId, newPath); + } +} + +static void ReadAnimations(RawModel& raw, FbxScene* pScene) { + FbxTime::EMode eMode = FbxTime::eFrames24; + const double epsilon = 1e-5f; + + const int animationCount = pScene->GetSrcObjectCount(); + for (size_t animIx = 0; animIx < animationCount; animIx++) { + FbxAnimStack* pAnimStack = pScene->GetSrcObject(animIx); + FbxString animStackName = pAnimStack->GetName(); + + pScene->SetCurrentAnimationStack(pAnimStack); + + FbxTakeInfo* takeInfo = pScene->GetTakeInfo(animStackName); + if (takeInfo == nullptr) { + fmt::printf("Warning:: animation '%s' has no Take information. Skipping.\n", animStackName); + // not all animstacks have a take + continue; + } if (verboseOutput) { - fmt::printf("node %d: %s\n", nodeIndex, newPath.c_str()); + fmt::printf("animation %zu: %s (%d%%)", animIx, (const char*)animStackName, 0); } - static int warnRrSsCount = 0; - static int warnRrsCount = 0; - if (lInheritType == FbxTransform::eInheritRrSs && parentId) { - if (++warnRrSsCount == 1) { - fmt::printf("Warning: node %s uses unsupported transform inheritance type 'eInheritRrSs'.\n", newPath); - fmt::printf(" (Further warnings of this type squelched.)\n"); - } + FbxTime start = takeInfo->mLocalTimeSpan.GetStart(); + FbxTime end = takeInfo->mLocalTimeSpan.GetStop(); - } else if (lInheritType == FbxTransform::eInheritRrs) { - if (++warnRrsCount == 1) { - fmt::printf( - "Warning: node %s uses unsupported transform inheritance type 'eInheritRrs'\n" - " This tool will attempt to partially compensate, but glTF cannot truly express this mode.\n" - " If this was a Maya export, consider turning off 'Segment Scale Compensate' on all joints.\n" - " (Further warnings of this type squelched.)\n", - newPath); - } + RawAnimation animation; + animation.name = animStackName; + + FbxLongLong firstFrameIndex = start.GetFrameCount(eMode); + FbxLongLong lastFrameIndex = end.GetFrameCount(eMode); + for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) { + FbxTime pTime; + // first frame is always at t = 0.0 + pTime.SetFrame(frameIndex - firstFrameIndex, eMode); + animation.times.emplace_back((float)pTime.GetSecondDouble()); } - // Set the initial node transform. - const FbxAMatrix localTransform = pNode->EvaluateLocalTransform(); - const FbxVector4 localTranslation = localTransform.GetT(); - const FbxQuaternion localRotation = localTransform.GetQ(); - const FbxVector4 localScaling = computeLocalScale(pNode); + size_t totalSizeInBytes = 0; - node.translation = toVec3f(localTranslation) * scaleFactor; - node.rotation = toQuatf(localRotation); - node.scale = toVec3f(localScaling); + const int nodeCount = pScene->GetNodeCount(); + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { + FbxNode* pNode = pScene->GetNode(nodeIndex); + const FbxAMatrix baseTransform = pNode->EvaluateLocalTransform(); + const FbxVector4 baseTranslation = baseTransform.GetT(); + const FbxQuaternion baseRotation = baseTransform.GetQ(); + const FbxVector4 baseScaling = computeLocalScale(pNode); + bool hasTranslation = false; + bool hasRotation = false; + bool hasScale = false; + bool hasMorphs = false; - if (parentId) { - RawNode &parentNode = raw.GetNode(raw.GetNodeById(parentId)); - // Add unique child name to the parent node. - if (std::find(parentNode.childIds.begin(), parentNode.childIds.end(), nodeId) == parentNode.childIds.end()) { - parentNode.childIds.push_back(nodeId); - } - } else { - // If there is no parent then this is the root node. - raw.SetRootNode(nodeId); - } + RawChannel channel; + channel.nodeIndex = raw.GetNodeById(pNode->GetUniqueID()); - for (int child = 0; child < pNode->GetChildCount(); child++) { - ReadNodeHierarchy(raw, pScene, pNode->GetChild(child), nodeId, newPath); - } -} + for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) { + FbxTime pTime; + pTime.SetFrame(frameIndex, eMode); -static void ReadAnimations(RawModel &raw, FbxScene *pScene) -{ - FbxTime::EMode eMode = FbxTime::eFrames24; - const double epsilon = 1e-5f; + const FbxAMatrix localTransform = pNode->EvaluateLocalTransform(pTime); + const FbxVector4 localTranslation = localTransform.GetT(); + const FbxQuaternion localRotation = localTransform.GetQ(); + const FbxVector4 localScale = computeLocalScale(pNode, pTime); - const int animationCount = pScene->GetSrcObjectCount(); - for (size_t animIx = 0; animIx < animationCount; animIx++) { - FbxAnimStack *pAnimStack = pScene->GetSrcObject(animIx); - FbxString animStackName = pAnimStack->GetName(); + hasTranslation |= + (fabs(localTranslation[0] - baseTranslation[0]) > epsilon || + fabs(localTranslation[1] - baseTranslation[1]) > epsilon || + fabs(localTranslation[2] - baseTranslation[2]) > epsilon); + hasRotation |= + (fabs(localRotation[0] - baseRotation[0]) > epsilon || + fabs(localRotation[1] - baseRotation[1]) > epsilon || + fabs(localRotation[2] - baseRotation[2]) > epsilon || + fabs(localRotation[3] - baseRotation[3]) > epsilon); + hasScale |= + (fabs(localScale[0] - baseScaling[0]) > epsilon || + fabs(localScale[1] - baseScaling[1]) > epsilon || + fabs(localScale[2] - baseScaling[2]) > epsilon); - pScene->SetCurrentAnimationStack(pAnimStack); + channel.translations.push_back(toVec3f(localTranslation) * scaleFactor); + channel.rotations.push_back(toQuatf(localRotation)); + channel.scales.push_back(toVec3f(localScale)); + } - FbxTakeInfo *takeInfo = pScene->GetTakeInfo(animStackName); - if (takeInfo == nullptr) { - fmt::printf("Warning:: animation '%s' has no Take information. Skipping.\n", animStackName); - // not all animstacks have a take - continue; - } - if (verboseOutput) { - fmt::printf("animation %zu: %s (%d%%)", animIx, (const char *) animStackName, 0); - } + std::vector shapeAnimCurves; + FbxNodeAttribute* nodeAttr = pNode->GetNodeAttribute(); + if (nodeAttr != nullptr && nodeAttr->GetAttributeType() == FbxNodeAttribute::EType::eMesh) { + // it's inelegant to recreate this same access class multiple times, but it's also dirt + // cheap... + FbxBlendShapesAccess blendShapes(static_cast(nodeAttr)); - FbxTime start = takeInfo->mLocalTimeSpan.GetStart(); - FbxTime end = takeInfo->mLocalTimeSpan.GetStop(); - - RawAnimation animation; - animation.name = animStackName; - - FbxLongLong firstFrameIndex = start.GetFrameCount(eMode); - FbxLongLong lastFrameIndex = end.GetFrameCount(eMode); for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) { - FbxTime pTime; - // first frame is always at t = 0.0 - pTime.SetFrame(frameIndex - firstFrameIndex, eMode); - animation.times.emplace_back((float) pTime.GetSecondDouble()); + FbxTime pTime; + pTime.SetFrame(frameIndex, eMode); + + for (size_t channelIx = 0; channelIx < blendShapes.GetChannelCount(); channelIx++) { + FbxAnimCurve* curve = blendShapes.GetAnimation(channelIx, animIx); + float influence = (curve != nullptr) ? curve->Evaluate(pTime) : 0; // 0-100 + + int targetCount = static_cast(blendShapes.GetTargetShapeCount(channelIx)); + + // the target shape 'fullWeight' values are a strictly ascending list of floats (between + // 0 and 100), forming a sequence of intervals -- this convenience function figures out + // if 'p' lays between some certain target fullWeights, and if so where (from 0 to 1). + auto findInInterval = [&](const double p, const int n) { + if (n >= targetCount) { + // p is certainly completely left of this interval + return NAN; + } + double leftWeight = 0; + if (n >= 0) { + leftWeight = blendShapes.GetTargetShape(channelIx, n).fullWeight; + if (p < leftWeight) { + return NAN; + } + // the first interval implicitly includes all lesser influence values + } + double rightWeight = blendShapes.GetTargetShape(channelIx, n + 1).fullWeight; + if (p > rightWeight && n + 1 < targetCount - 1) { + return NAN; + // the last interval implicitly includes all greater influence values + } + // transform p linearly such that [leftWeight, rightWeight] => [0, 1] + return static_cast((p - leftWeight) / (rightWeight - leftWeight)); + }; + + for (int targetIx = 0; targetIx < targetCount; targetIx++) { + if (curve) { + float result = findInInterval(influence, targetIx - 1); + if (!std::isnan(result)) { + // we're transitioning into targetIx + channel.weights.push_back(result); + hasMorphs = true; + continue; + } + if (targetIx != targetCount - 1) { + result = findInInterval(influence, targetIx); + if (!std::isnan(result)) { + // we're transitioning AWAY from targetIx + channel.weights.push_back(1.0f - result); + hasMorphs = true; + continue; + } + } + } + + // this is here because we have to fill in a weight for every channelIx/targetIx + // permutation, regardless of whether or not they participate in this animation. + channel.weights.push_back(0.0f); + } + } + } + } + + if (hasTranslation || hasRotation || hasScale || hasMorphs) { + if (!hasTranslation) { + channel.translations.clear(); + } + if (!hasRotation) { + channel.rotations.clear(); + } + if (!hasScale) { + channel.scales.clear(); + } + if (!hasMorphs) { + channel.weights.clear(); } - size_t totalSizeInBytes = 0; + animation.channels.emplace_back(channel); - const int nodeCount = pScene->GetNodeCount(); - for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) { - FbxNode *pNode = pScene->GetNode(nodeIndex); - const FbxAMatrix baseTransform = pNode->EvaluateLocalTransform(); - const FbxVector4 baseTranslation = baseTransform.GetT(); - const FbxQuaternion baseRotation = baseTransform.GetQ(); - const FbxVector4 baseScaling = computeLocalScale(pNode); - bool hasTranslation = false; - bool hasRotation = false; - bool hasScale = false; - bool hasMorphs = false; + totalSizeInBytes += channel.translations.size() * sizeof(channel.translations[0]) + + channel.rotations.size() * sizeof(channel.rotations[0]) + + channel.scales.size() * sizeof(channel.scales[0]) + + channel.weights.size() * sizeof(channel.weights[0]); + } - RawChannel channel; - channel.nodeIndex = raw.GetNodeById(pNode->GetUniqueID()); - - for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) { - FbxTime pTime; - pTime.SetFrame(frameIndex, eMode); - - const FbxAMatrix localTransform = pNode->EvaluateLocalTransform(pTime); - const FbxVector4 localTranslation = localTransform.GetT(); - const FbxQuaternion localRotation = localTransform.GetQ(); - const FbxVector4 localScale = computeLocalScale(pNode, pTime); - - hasTranslation |= ( - fabs(localTranslation[0] - baseTranslation[0]) > epsilon || - fabs(localTranslation[1] - baseTranslation[1]) > epsilon || - fabs(localTranslation[2] - baseTranslation[2]) > epsilon); - hasRotation |= ( - fabs(localRotation[0] - baseRotation[0]) > epsilon || - fabs(localRotation[1] - baseRotation[1]) > epsilon || - fabs(localRotation[2] - baseRotation[2]) > epsilon || - fabs(localRotation[3] - baseRotation[3]) > epsilon); - hasScale |= ( - fabs(localScale[0] - baseScaling[0]) > epsilon || - fabs(localScale[1] - baseScaling[1]) > epsilon || - fabs(localScale[2] - baseScaling[2]) > epsilon); - - channel.translations.push_back(toVec3f(localTranslation) * scaleFactor); - channel.rotations.push_back(toQuatf(localRotation)); - channel.scales.push_back(toVec3f(localScale)); - } - - std::vector shapeAnimCurves; - FbxNodeAttribute *nodeAttr = pNode->GetNodeAttribute(); - if (nodeAttr != nullptr && nodeAttr->GetAttributeType() == FbxNodeAttribute::EType::eMesh) { - // it's inelegant to recreate this same access class multiple times, but it's also dirt cheap... - FbxBlendShapesAccess blendShapes(static_cast(nodeAttr)); - - for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) { - FbxTime pTime; - pTime.SetFrame(frameIndex, eMode); - - for (size_t channelIx = 0; channelIx < blendShapes.GetChannelCount(); channelIx++) { - FbxAnimCurve *curve = blendShapes.GetAnimation(channelIx, animIx); - float influence = (curve != nullptr) ? curve->Evaluate(pTime) : 0; // 0-100 - - int targetCount = static_cast(blendShapes.GetTargetShapeCount(channelIx)); - - // the target shape 'fullWeight' values are a strictly ascending list of floats (between - // 0 and 100), forming a sequence of intervals -- this convenience function figures out if - // 'p' lays between some certain target fullWeights, and if so where (from 0 to 1). - auto findInInterval = [&](const double p, const int n) { - if (n >= targetCount) { - // p is certainly completely left of this interval - return NAN; - } - double leftWeight = 0; - if (n >= 0) { - leftWeight = blendShapes.GetTargetShape(channelIx, n).fullWeight; - if (p < leftWeight) { - return NAN; - } - // the first interval implicitly includes all lesser influence values - } - double rightWeight = blendShapes.GetTargetShape(channelIx, n+1).fullWeight; - if (p > rightWeight && n+1 < targetCount-1) { - return NAN; - // the last interval implicitly includes all greater influence values - } - // transform p linearly such that [leftWeight, rightWeight] => [0, 1] - return static_cast((p - leftWeight) / (rightWeight - leftWeight)); - }; - - for (int targetIx = 0; targetIx < targetCount; targetIx++) { - if (curve) { - float result = findInInterval(influence, targetIx-1); - if (!std::isnan(result)) { - // we're transitioning into targetIx - channel.weights.push_back(result); - hasMorphs = true; - continue; - } - if (targetIx != targetCount-1) { - result = findInInterval(influence, targetIx); - if (!std::isnan(result)) { - // we're transitioning AWAY from targetIx - channel.weights.push_back(1.0f - result); - hasMorphs = true; - continue; - } - } - } - - // this is here because we have to fill in a weight for every channelIx/targetIx permutation, - // regardless of whether or not they participate in this animation. - channel.weights.push_back(0.0f); - } - } - } - } - - if (hasTranslation || hasRotation || hasScale || hasMorphs) { - if (!hasTranslation) { - channel.translations.clear(); - } - if (!hasRotation) { - channel.rotations.clear(); - } - if (!hasScale) { - channel.scales.clear(); - } - if (!hasMorphs) { - channel.weights.clear(); - } - - animation.channels.emplace_back(channel); - - totalSizeInBytes += channel.translations.size() * sizeof(channel.translations[0]) + - channel.rotations.size() * sizeof(channel.rotations[0]) + - channel.scales.size() * sizeof(channel.scales[0]) + - channel.weights.size() * sizeof(channel.weights[0]); - } - - if (verboseOutput) { - fmt::printf("\ranimation %d: %s (%d%%)", animIx, (const char *) animStackName, nodeIndex * 100 / nodeCount); - } - } - - raw.AddAnimation(animation); - - if (verboseOutput) { - fmt::printf( - "\ranimation %d: %s (%d channels, %3.1f MB)\n", animIx, (const char *) animStackName, - (int) animation.channels.size(), (float) totalSizeInBytes * 1e-6f); - } + if (verboseOutput) { + fmt::printf( + "\ranimation %d: %s (%d%%)", + animIx, + (const char*)animStackName, + nodeIndex * 100 / nodeCount); + } } + + raw.AddAnimation(animation); + + if (verboseOutput) { + fmt::printf( + "\ranimation %d: %s (%d channels, %3.1f MB)\n", + animIx, + (const char*)animStackName, + (int)animation.channels.size(), + (float)totalSizeInBytes * 1e-6f); + } + } } -static std::string GetInferredFileName(const std::string &fbxFileName, const std::string &directory, const std::vector &directoryFileList) -{ - if (FileUtils::FileExists(fbxFileName)) { - return fbxFileName; +static std::string GetInferredFileName( + const std::string& fbxFileName, + const std::string& directory, + const std::vector& directoryFileList) { + if (FileUtils::FileExists(fbxFileName)) { + return fbxFileName; + } + // Get the file name with file extension. + const std::string fileName = + StringUtils::GetFileNameString(StringUtils::GetCleanPathString(fbxFileName)); + + // Try to find a match with extension. + for (const auto& file : directoryFileList) { + if (StringUtils::CompareNoCase(fileName, file) == 0) { + return std::string(directory) + file; } - // Get the file name with file extension. - const std::string fileName = StringUtils::GetFileNameString(StringUtils::GetCleanPathString(fbxFileName)); + } - // Try to find a match with extension. - for (const auto &file : directoryFileList) { - if (StringUtils::CompareNoCase(fileName, file) == 0) { - return std::string(directory) + file; - } + // Get the file name without file extension. + 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 (StringUtils::CompareNoCase(fileBase, StringUtils::GetFileBaseString(file)) == 0) { + // Return the name with extension of the file in the directory. + return std::string(directory) + file; } + } - // Get the file name without file extension. - 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 (StringUtils::CompareNoCase(fileBase, StringUtils::GetFileBaseString(file)) == 0) { - // Return the name with extension of the file in the directory. - return std::string(directory) + file; - } - } - - return ""; + return ""; } /* @@ -856,176 +953,209 @@ static std::string GetInferredFileName(const std::string &fbxFileName, const std path on the author's computer such as "C:\MyProject\TextureName.psd", and matches it to a list of existing texture files in the same directory as the FBX file. */ -static void -FindFbxTextures( - FbxScene *pScene, const char *fbxFileName, const char *extensions, std::map &textureLocations) -{ - // Get the folder the FBX file is in. - const std::string folder = StringUtils::GetFolderString(fbxFileName); +static void FindFbxTextures( + FbxScene* pScene, + const char* fbxFileName, + const char* extensions, + std::map& textureLocations) { + // Get the folder the FBX file is in. + 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 + StringUtils::GetFileBaseString(fbxFileName) + ".fbm/"; + // Check if there is a filename.fbm folder to which embedded textures were extracted. + 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; + // 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; - // Get a list with all the texture files from either the folder with embedded textures or the same folder as the FBX file. - std::vector fileList = FileUtils::ListFolderFiles(searchFolder.c_str(), extensions); + // Get a list with all the texture files from either the folder with embedded textures or the same + // folder as the FBX file. + std::vector fileList = FileUtils::ListFolderFiles(searchFolder.c_str(), extensions); - // Try to match the FBX texture names with the actual files on disk. - for (int i = 0; i < pScene->GetTextureCount(); i++) { - const FbxFileTexture *pFileTexture = FbxCast(pScene->GetTexture(i)); - if (pFileTexture == nullptr) { - continue; - } - const std::string inferredName = GetInferredFileName(pFileTexture->GetFileName(), searchFolder, fileList); - if (inferredName.empty()) { - fmt::printf("Warning: could not find a local image file for texture: %s.\n" - "Original filename: %s\n", pFileTexture->GetName(), pFileTexture->GetFileName()); - } - // always extend the mapping, even for files we didn't find - textureLocations.emplace(pFileTexture, inferredName.c_str()); + // Try to match the FBX texture names with the actual files on disk. + for (int i = 0; i < pScene->GetTextureCount(); i++) { + const FbxFileTexture* pFileTexture = FbxCast(pScene->GetTexture(i)); + if (pFileTexture == nullptr) { + continue; } + const std::string inferredName = + GetInferredFileName(pFileTexture->GetFileName(), searchFolder, fileList); + if (inferredName.empty()) { + fmt::printf( + "Warning: could not find a local image file for texture: %s.\n" + "Original filename: %s\n", + pFileTexture->GetName(), + pFileTexture->GetFileName()); + } + // always extend the mapping, even for files we didn't find + textureLocations.emplace(pFileTexture, inferredName.c_str()); + } } -bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions) -{ - FbxManager *pManager = FbxManager::Create(); - FbxIOSettings *pIoSettings = FbxIOSettings::Create(pManager, IOSROOT); - pManager->SetIOSettings(pIoSettings); +bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions) { + FbxManager* pManager = FbxManager::Create(); + FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT); + pManager->SetIOSettings(pIoSettings); - FbxImporter *pImporter = FbxImporter::Create(pManager, ""); + FbxImporter* pImporter = FbxImporter::Create(pManager, ""); - if (!pImporter->Initialize(fbxFileName, -1, pManager->GetIOSettings())) { - if (verboseOutput) { - fmt::printf("%s\n", pImporter->GetStatus().GetErrorString()); - } - pImporter->Destroy(); - pManager->Destroy(); - return false; + if (!pImporter->Initialize(fbxFileName, -1, pManager->GetIOSettings())) { + if (verboseOutput) { + fmt::printf("%s\n", pImporter->GetStatus().GetErrorString()); } - - FbxScene *pScene = FbxScene::Create(pManager, "fbxScene"); - pImporter->Import(pScene); pImporter->Destroy(); - - if (pScene == nullptr) { - pImporter->Destroy(); - pManager->Destroy(); - return false; - } - - std::map textureLocations; - FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations); - - // Use Y up for glTF - FbxAxisSystem::MayaYUp.ConvertScene(pScene); - - // FBX's internal unscaled unit is centimetres, and if you choose not to work in that unit, - // you will find scaling transforms on all the children of the root node. Those transforms are - // superfluous and cause a lot of people a lot of trouble. Luckily we can get rid of them by - // converting to CM here (which just gets rid of the scaling), and then we pre-multiply the - // scale factor into every vertex position (and related attributes) instead. - FbxSystemUnit sceneSystemUnit = pScene->GetGlobalSettings().GetSystemUnit(); - if (sceneSystemUnit != FbxSystemUnit::cm) { - FbxSystemUnit::cm.ConvertScene(pScene); - } - // this is always 0.01, but let's opt for clarity. - scaleFactor = FbxSystemUnit::m.GetConversionFactorFrom(FbxSystemUnit::cm); - - ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), 0, ""); - ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations); - ReadAnimations(raw, pScene); - - pScene->Destroy(); pManager->Destroy(); + return false; + } - return true; + FbxScene* pScene = FbxScene::Create(pManager, "fbxScene"); + pImporter->Import(pScene); + pImporter->Destroy(); + + if (pScene == nullptr) { + pImporter->Destroy(); + pManager->Destroy(); + return false; + } + + std::map textureLocations; + FindFbxTextures(pScene, fbxFileName, textureExtensions, textureLocations); + + // Use Y up for glTF + FbxAxisSystem::MayaYUp.ConvertScene(pScene); + + // FBX's internal unscaled unit is centimetres, and if you choose not to work in that unit, + // you will find scaling transforms on all the children of the root node. Those transforms are + // superfluous and cause a lot of people a lot of trouble. Luckily we can get rid of them by + // converting to CM here (which just gets rid of the scaling), and then we pre-multiply the + // scale factor into every vertex position (and related attributes) instead. + FbxSystemUnit sceneSystemUnit = pScene->GetGlobalSettings().GetSystemUnit(); + if (sceneSystemUnit != FbxSystemUnit::cm) { + FbxSystemUnit::cm.ConvertScene(pScene); + } + // this is always 0.01, but let's opt for clarity. + scaleFactor = FbxSystemUnit::m.GetConversionFactorFrom(FbxSystemUnit::cm); + + ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), 0, ""); + ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations); + ReadAnimations(raw, pScene); + + pScene->Destroy(); + pManager->Destroy(); + + return true; } // convenience method for describing a property in JSON -json TranscribeProperty(FbxProperty &prop) -{ - using fbxsdk::EFbxType; - std::string ename; +json TranscribeProperty(FbxProperty& prop) { + using fbxsdk::EFbxType; + std::string ename; - // Convert property type - switch (prop.GetPropertyDataType().GetType()) { - case eFbxBool: ename = "eFbxBool"; break; - case eFbxChar: ename = "eFbxChar"; break; - case eFbxUChar: ename = "eFbxUChar"; break; - case eFbxShort: ename = "eFbxShort"; break; - case eFbxUShort: ename = "eFbxUShort"; break; - case eFbxInt: ename = "eFbxInt"; break; - case eFbxUInt: ename = "eFbxUint"; break; - case eFbxLongLong: ename = "eFbxLongLong"; break; - case eFbxULongLong: ename = "eFbxULongLong"; break; - case eFbxFloat: ename = "eFbxFloat"; break; - case eFbxHalfFloat: ename = "eFbxHalfFloat"; break; - case eFbxDouble: ename = "eFbxDouble"; break; - case eFbxDouble2: ename = "eFbxDouble2"; break; - case eFbxDouble3: ename = "eFbxDouble3"; break; - case eFbxDouble4: ename = "eFbxDouble4"; break; - case eFbxString: ename = "eFbxString"; break; + // Convert property type + switch (prop.GetPropertyDataType().GetType()) { + case eFbxBool: + ename = "eFbxBool"; + break; + case eFbxChar: + ename = "eFbxChar"; + break; + case eFbxUChar: + ename = "eFbxUChar"; + break; + case eFbxShort: + ename = "eFbxShort"; + break; + case eFbxUShort: + ename = "eFbxUShort"; + break; + case eFbxInt: + ename = "eFbxInt"; + break; + case eFbxUInt: + ename = "eFbxUint"; + break; + case eFbxLongLong: + ename = "eFbxLongLong"; + break; + case eFbxULongLong: + ename = "eFbxULongLong"; + break; + case eFbxFloat: + ename = "eFbxFloat"; + break; + case eFbxHalfFloat: + ename = "eFbxHalfFloat"; + break; + case eFbxDouble: + ename = "eFbxDouble"; + break; + case eFbxDouble2: + ename = "eFbxDouble2"; + break; + case eFbxDouble3: + ename = "eFbxDouble3"; + break; + case eFbxDouble4: + ename = "eFbxDouble4"; + break; + case eFbxString: + ename = "eFbxString"; + break; - // Use this as fallback because it does not give very descriptive names - default: ename = prop.GetPropertyDataType().GetName(); break; + // Use this as fallback because it does not give very descriptive names + default: + ename = prop.GetPropertyDataType().GetName(); + break; + } + + json p = {{"type", ename}}; + + // Convert property value + switch (prop.GetPropertyDataType().GetType()) { + case eFbxBool: + case eFbxChar: + case eFbxUChar: + case eFbxShort: + case eFbxUShort: + case eFbxInt: + case eFbxUInt: + case eFbxLongLong: { + p["value"] = prop.EvaluateValue(FBXSDK_TIME_INFINITE); + break; } - - json p = { - {"type", ename} - }; - - // Convert property value - switch (prop.GetPropertyDataType().GetType()) { - case eFbxBool: - case eFbxChar: - case eFbxUChar: - case eFbxShort: - case eFbxUShort: - case eFbxInt: - case eFbxUInt: - case eFbxLongLong: { - p["value"] = prop.EvaluateValue(FBXSDK_TIME_INFINITE); - break; - } - case eFbxULongLong: { - p["value"] = prop.EvaluateValue(FBXSDK_TIME_INFINITE); - break; - } - case eFbxFloat: - case eFbxHalfFloat: - case eFbxDouble: { - p["value"] = prop.EvaluateValue(FBXSDK_TIME_INFINITE); - break; - } - case eFbxDouble2: { - auto v = prop.EvaluateValue(FBXSDK_TIME_INFINITE); - p["value"] = {v[0], v[1]}; - break; - } - case eFbxDouble3: { - auto v = prop.EvaluateValue(FBXSDK_TIME_INFINITE); - p["value"] = {v[0], v[1], v[2]}; - break; - } - case eFbxDouble4: { - auto v = prop.EvaluateValue(FBXSDK_TIME_INFINITE); - p["value"] = {v[0], v[1], v[2], v[3]}; - break; - } - case eFbxString: { - p["value"] = std::string{prop.Get()}; - break; - } - default: { - p["value"] = "UNSUPPORTED_VALUE_TYPE"; - break; - } + case eFbxULongLong: { + p["value"] = prop.EvaluateValue(FBXSDK_TIME_INFINITE); + break; } + case eFbxFloat: + case eFbxHalfFloat: + case eFbxDouble: { + p["value"] = prop.EvaluateValue(FBXSDK_TIME_INFINITE); + break; + } + case eFbxDouble2: { + auto v = prop.EvaluateValue(FBXSDK_TIME_INFINITE); + p["value"] = {v[0], v[1]}; + break; + } + case eFbxDouble3: { + auto v = prop.EvaluateValue(FBXSDK_TIME_INFINITE); + p["value"] = {v[0], v[1], v[2]}; + break; + } + case eFbxDouble4: { + auto v = prop.EvaluateValue(FBXSDK_TIME_INFINITE); + p["value"] = {v[0], v[1], v[2], v[3]}; + break; + } + case eFbxString: { + p["value"] = std::string{prop.Get()}; + break; + } + default: { + p["value"] = "UNSUPPORTED_VALUE_TYPE"; + break; + } + } - return { - {prop.GetNameAsCStr(), p} - }; + return {{prop.GetNameAsCStr(), p}}; } - diff --git a/src/fbx/Fbx2Raw.hpp b/src/fbx/Fbx2Raw.hpp index d1ae70c..a5fd231 100644 --- a/src/fbx/Fbx2Raw.hpp +++ b/src/fbx/Fbx2Raw.hpp @@ -11,6 +11,6 @@ #include "raw/RawModel.hpp" -bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExtensions); +bool LoadFBXFile(RawModel& raw, const char* fbxFileName, const char* textureExtensions); -json TranscribeProperty(FbxProperty &prop); \ No newline at end of file +json TranscribeProperty(FbxProperty& prop); \ No newline at end of file diff --git a/src/fbx/FbxBlendShapesAccess.cpp b/src/fbx/FbxBlendShapesAccess.cpp index 5371750..3530e23 100644 --- a/src/fbx/FbxBlendShapesAccess.cpp +++ b/src/fbx/FbxBlendShapesAccess.cpp @@ -9,57 +9,65 @@ #include "FbxBlendShapesAccess.hpp" -FbxBlendShapesAccess::TargetShape::TargetShape(const FbxShape *shape, FbxDouble fullWeight) : - shape(shape), - fullWeight(fullWeight), - count(shape->GetControlPointsCount()), - positions(shape->GetControlPoints()), - normals(FbxLayerElementAccess(shape->GetElementNormal(), shape->GetElementNormalCount())), - tangents(FbxLayerElementAccess(shape->GetElementTangent(), shape->GetElementTangentCount())) -{} +FbxBlendShapesAccess::TargetShape::TargetShape(const FbxShape* shape, FbxDouble fullWeight) + : shape(shape), + fullWeight(fullWeight), + count(shape->GetControlPointsCount()), + positions(shape->GetControlPoints()), + normals(FbxLayerElementAccess( + shape->GetElementNormal(), + shape->GetElementNormalCount())), + tangents(FbxLayerElementAccess( + shape->GetElementTangent(), + shape->GetElementTangentCount())) {} -FbxAnimCurve *FbxBlendShapesAccess::BlendChannel::ExtractAnimation(unsigned int animIx) const -{ - FbxAnimStack *stack = mesh->GetScene()->GetSrcObject(animIx); - FbxAnimLayer *layer = stack->GetMember(0); - return mesh->GetShapeChannel(blendShapeIx, channelIx, layer, true); +FbxAnimCurve* FbxBlendShapesAccess::BlendChannel::ExtractAnimation(unsigned int animIx) const { + FbxAnimStack* stack = mesh->GetScene()->GetSrcObject(animIx); + FbxAnimLayer* layer = stack->GetMember(0); + return mesh->GetShapeChannel(blendShapeIx, channelIx, layer, true); } FbxBlendShapesAccess::BlendChannel::BlendChannel( - FbxMesh *mesh, const unsigned int blendShapeIx, const unsigned int channelIx, const FbxDouble deformPercent, - const std::vector &targetShapes, std::string name) : mesh(mesh), - blendShapeIx(blendShapeIx), - channelIx(channelIx), - deformPercent(deformPercent), - targetShapes(targetShapes), - name(name) -{} + FbxMesh* mesh, + const unsigned int blendShapeIx, + const unsigned int channelIx, + const FbxDouble deformPercent, + const std::vector& targetShapes, + std::string name) + : mesh(mesh), + blendShapeIx(blendShapeIx), + channelIx(channelIx), + deformPercent(deformPercent), + targetShapes(targetShapes), + name(name) {} -std::vector FbxBlendShapesAccess::extractChannels(FbxMesh *mesh) const -{ - std::vector channels; - for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) { - auto *fbxBlendShape = static_cast(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape)); +std::vector FbxBlendShapesAccess::extractChannels( + FbxMesh* mesh) const { + std::vector channels; + for (int shapeIx = 0; shapeIx < mesh->GetDeformerCount(FbxDeformer::eBlendShape); shapeIx++) { + auto* fbxBlendShape = + static_cast(mesh->GetDeformer(shapeIx, FbxDeformer::eBlendShape)); - for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) { - FbxBlendShapeChannel *fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx); + for (int channelIx = 0; channelIx < fbxBlendShape->GetBlendShapeChannelCount(); ++channelIx) { + FbxBlendShapeChannel* fbxChannel = fbxBlendShape->GetBlendShapeChannel(channelIx); - if (fbxChannel->GetTargetShapeCount() > 0) { - std::vector targetShapes; - const double *fullWeights = fbxChannel->GetTargetShapeFullWeights(); - std::string name = std::string(fbxChannel->GetName()); + if (fbxChannel->GetTargetShapeCount() > 0) { + std::vector targetShapes; + const double* fullWeights = fbxChannel->GetTargetShapeFullWeights(); + std::string name = std::string(fbxChannel->GetName()); - if (verboseOutput) { - fmt::printf("\rblendshape channel: %s\n", name); - } - - for (int targetIx = 0; targetIx < fbxChannel->GetTargetShapeCount(); targetIx ++) { - FbxShape *fbxShape = fbxChannel->GetTargetShape(targetIx); - targetShapes.emplace_back(fbxShape, fullWeights[targetIx]); - } - channels.emplace_back(mesh, shapeIx, channelIx, fbxChannel->DeformPercent * 0.01, targetShapes, name); - } + if (verboseOutput) { + fmt::printf("\rblendshape channel: %s\n", name); } + + for (int targetIx = 0; targetIx < fbxChannel->GetTargetShapeCount(); targetIx++) { + FbxShape* fbxShape = fbxChannel->GetTargetShape(targetIx); + targetShapes.emplace_back(fbxShape, fullWeights[targetIx]); + } + channels.emplace_back( + mesh, shapeIx, channelIx, fbxChannel->DeformPercent * 0.01, targetShapes, name); + } } - return channels; + } + return channels; } diff --git a/src/fbx/FbxBlendShapesAccess.hpp b/src/fbx/FbxBlendShapesAccess.hpp index aea1352..fc99ec9 100644 --- a/src/fbx/FbxBlendShapesAccess.hpp +++ b/src/fbx/FbxBlendShapesAccess.hpp @@ -9,97 +9,97 @@ #pragma once +#include #include #include #include -#include #include "FBX2glTF.h" #include "FbxLayerElementAccess.hpp" /** - * At the FBX level, each Mesh can have a set of FbxBlendShape deformers; organisational units that contain no data - * of their own. The actual deformation is determined by one or more FbxBlendShapeChannels, whose influences are all - * additively applied to the mesh. In a simpler world, each such channel would extend each base vertex with alternate - * position, and optionally normal and tangent. + * At the FBX level, each Mesh can have a set of FbxBlendShape deformers; organisational units that + * contain no data of their own. The actual deformation is determined by one or more + * FbxBlendShapeChannels, whose influences are all additively applied to the mesh. In a simpler + * world, each such channel would extend each base vertex with alternate position, and optionally + * normal and tangent. * - * It's not quite so simple, though. We also have progressive morphing, where one logical morph actually consists of - * several concrete ones, each applied in sequence. For us, this means each channel contains a sequence of FbxShapes - * (aka target shape); these are the actual data-holding entities that provide the alternate vertex attributes. As such - * a channel is given more weight, it moves from one target shape to another. + * It's not quite so simple, though. We also have progressive morphing, where one logical morph + * actually consists of several concrete ones, each applied in sequence. For us, this means each + * channel contains a sequence of FbxShapes (aka target shape); these are the actual data-holding + * entities that provide the alternate vertex attributes. As such a channel is given more weight, it + * moves from one target shape to another. * - * The total number of alternate sets of attributes, then, is the total number of target shapes across all the channels - * of all the blend shapes of the mesh. + * The total number of alternate sets of attributes, then, is the total number of target shapes + * across all the channels of all the blend shapes of the mesh. * - * Each animation in the scene stack can yield one or zero FbxAnimCurves per channel (not target shape). We evaluate - * these curves to get the weight of the channel: this weight is further introspected on to figure out which target - * shapes we're currently interpolation between. + * Each animation in the scene stack can yield one or zero FbxAnimCurves per channel (not target + * shape). We evaluate these curves to get the weight of the channel: this weight is further + * introspected on to figure out which target shapes we're currently interpolation between. */ -class FbxBlendShapesAccess -{ -public: - /** - * A target shape is on a 1:1 basis with the eventual glTF morph target, and is the object which contains the - * actual morphed vertex data. - */ - struct TargetShape - { - explicit TargetShape(const FbxShape *shape, FbxDouble fullWeight); +class FbxBlendShapesAccess { + public: + /** + * A target shape is on a 1:1 basis with the eventual glTF morph target, and is the object which + * contains the actual morphed vertex data. + */ + struct TargetShape { + explicit TargetShape(const FbxShape* shape, FbxDouble fullWeight); - const FbxShape *shape; - const FbxDouble fullWeight; - const unsigned int count; - const FbxVector4 *positions; - const FbxLayerElementAccess normals; - const FbxLayerElementAccess tangents; - }; + const FbxShape* shape; + const FbxDouble fullWeight; + const unsigned int count; + const FbxVector4* positions; + const FbxLayerElementAccess normals; + const FbxLayerElementAccess tangents; + }; - /** - * A channel collects a sequence (often of length 1) of target shapes. - */ - struct BlendChannel - { - BlendChannel( - FbxMesh *mesh, - const unsigned int blendShapeIx, - const unsigned int channelIx, - const FbxDouble deformPercent, - const std::vector &targetShapes, - const std::string name - ); + /** + * A channel collects a sequence (often of length 1) of target shapes. + */ + struct BlendChannel { + BlendChannel( + FbxMesh* mesh, + const unsigned int blendShapeIx, + const unsigned int channelIx, + const FbxDouble deformPercent, + const std::vector& targetShapes, + const std::string name); - FbxAnimCurve *ExtractAnimation(unsigned int animIx) const; + FbxAnimCurve* ExtractAnimation(unsigned int animIx) const; - FbxMesh *const mesh; + FbxMesh* const mesh; - const unsigned int blendShapeIx; - const unsigned int channelIx; - const std::vector targetShapes; - const std::string name; + const unsigned int blendShapeIx; + const unsigned int channelIx; + const std::vector targetShapes; + const std::string name; - const FbxDouble deformPercent; - }; + const FbxDouble deformPercent; + }; - explicit FbxBlendShapesAccess(FbxMesh *mesh) : - channels(extractChannels(mesh)) - { } + explicit FbxBlendShapesAccess(FbxMesh* mesh) : channels(extractChannels(mesh)) {} - size_t GetChannelCount() const { return channels.size(); } - const BlendChannel &GetBlendChannel(size_t channelIx) const { - return channels.at(channelIx); - } + size_t GetChannelCount() const { + return channels.size(); + } + const BlendChannel& GetBlendChannel(size_t channelIx) const { + return channels.at(channelIx); + } - size_t GetTargetShapeCount(size_t channelIx) const { return channels[channelIx].targetShapes.size(); } - const TargetShape &GetTargetShape(size_t channelIx, size_t targetShapeIx) const { - return channels.at(channelIx).targetShapes[targetShapeIx]; - } + size_t GetTargetShapeCount(size_t channelIx) const { + return channels[channelIx].targetShapes.size(); + } + const TargetShape& GetTargetShape(size_t channelIx, size_t targetShapeIx) const { + return channels.at(channelIx).targetShapes[targetShapeIx]; + } - FbxAnimCurve * GetAnimation(size_t channelIx, size_t animIx) const { - return channels.at(channelIx).ExtractAnimation(animIx); - } + FbxAnimCurve* GetAnimation(size_t channelIx, size_t animIx) const { + return channels.at(channelIx).ExtractAnimation(animIx); + } -private: - std::vector extractChannels(FbxMesh *mesh) const; + private: + std::vector extractChannels(FbxMesh* mesh) const; - const std::vector channels; + const std::vector channels; }; diff --git a/src/fbx/FbxLayerElementAccess.hpp b/src/fbx/FbxLayerElementAccess.hpp index f35f74f..e335ca1 100644 --- a/src/fbx/FbxLayerElementAccess.hpp +++ b/src/fbx/FbxLayerElementAccess.hpp @@ -9,75 +9,87 @@ #pragma once #include "FBX2glTF.h" -template -class FbxLayerElementAccess -{ -public: +template +class FbxLayerElementAccess { + public: + FbxLayerElementAccess(const FbxLayerElementTemplate<_type_>* layer, int count); - FbxLayerElementAccess(const FbxLayerElementTemplate<_type_> *layer, int count); + bool LayerPresent() const { + return (mappingMode != FbxLayerElement::eNone); + } - bool LayerPresent() const - { - return (mappingMode != FbxLayerElement::eNone); - } + _type_ GetElement( + const int polygonIndex, + const int polygonVertexIndex, + const int controlPointIndex, + const _type_ defaultValue) const; + _type_ GetElement( + const int polygonIndex, + const int polygonVertexIndex, + const int controlPointIndex, + const _type_ defaultValue, + const FbxMatrix& transform, + const bool normalize) const; - _type_ GetElement(const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue) const; - _type_ GetElement( - const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue, - const FbxMatrix &transform, const bool normalize) const; - -private: - FbxLayerElement::EMappingMode mappingMode; - const FbxLayerElementArrayTemplate<_type_> *elements; - const FbxLayerElementArrayTemplate *indices; + private: + FbxLayerElement::EMappingMode mappingMode; + const FbxLayerElementArrayTemplate<_type_>* elements; + const FbxLayerElementArrayTemplate* indices; }; -template -FbxLayerElementAccess<_type_>::FbxLayerElementAccess(const FbxLayerElementTemplate<_type_> *layer, int count) : - mappingMode(FbxLayerElement::eNone), - elements(nullptr), - indices(nullptr) -{ - if (count <= 0 || layer == nullptr) { - return; - } - const FbxLayerElement::EMappingMode newMappingMode = layer->GetMappingMode(); - if (newMappingMode == FbxLayerElement::eByControlPoint || - newMappingMode == FbxLayerElement::eByPolygonVertex || - newMappingMode == FbxLayerElement::eByPolygon) { - mappingMode = newMappingMode; - elements = &layer->GetDirectArray(); - indices = ( - layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect || - layer->GetReferenceMode() == FbxLayerElement::eIndex) ? &layer->GetIndexArray() : nullptr; - } +template +FbxLayerElementAccess<_type_>::FbxLayerElementAccess( + const FbxLayerElementTemplate<_type_>* layer, + int count) + : mappingMode(FbxLayerElement::eNone), elements(nullptr), indices(nullptr) { + if (count <= 0 || layer == nullptr) { + return; + } + const FbxLayerElement::EMappingMode newMappingMode = layer->GetMappingMode(); + if (newMappingMode == FbxLayerElement::eByControlPoint || + newMappingMode == FbxLayerElement::eByPolygonVertex || + newMappingMode == FbxLayerElement::eByPolygon) { + mappingMode = newMappingMode; + elements = &layer->GetDirectArray(); + indices = (layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect || + layer->GetReferenceMode() == FbxLayerElement::eIndex) + ? &layer->GetIndexArray() + : nullptr; + } } -template +template _type_ FbxLayerElementAccess<_type_>::GetElement( - const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue) const -{ - if (mappingMode != FbxLayerElement::eNone) { - int index = (mappingMode == FbxLayerElement::eByControlPoint) ? controlPointIndex : - ((mappingMode == FbxLayerElement::eByPolygonVertex) ? polygonVertexIndex : polygonIndex); - index = (indices != nullptr) ? (*indices)[index] : index; - _type_ element = elements->GetAt(index); - return element; - } - return defaultValue; + const int polygonIndex, + const int polygonVertexIndex, + const int controlPointIndex, + const _type_ defaultValue) const { + if (mappingMode != FbxLayerElement::eNone) { + int index = (mappingMode == FbxLayerElement::eByControlPoint) + ? controlPointIndex + : ((mappingMode == FbxLayerElement::eByPolygonVertex) ? polygonVertexIndex : polygonIndex); + index = (indices != nullptr) ? (*indices)[index] : index; + _type_ element = elements->GetAt(index); + return element; + } + return defaultValue; } -template +template _type_ FbxLayerElementAccess<_type_>::GetElement( - const int polygonIndex, const int polygonVertexIndex, const int controlPointIndex, const _type_ defaultValue, - const FbxMatrix &transform, const bool normalize) const -{ - if (mappingMode != FbxLayerElement::eNone) { - _type_ element = transform.MultNormalize(GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, defaultValue)); - if (normalize) { - element.Normalize(); - } - return element; + const int polygonIndex, + const int polygonVertexIndex, + const int controlPointIndex, + const _type_ defaultValue, + const FbxMatrix& transform, + const bool normalize) const { + if (mappingMode != FbxLayerElement::eNone) { + _type_ element = transform.MultNormalize( + GetElement(polygonIndex, polygonVertexIndex, controlPointIndex, defaultValue)); + if (normalize) { + element.Normalize(); } - return defaultValue; + return element; + } + return defaultValue; } diff --git a/src/fbx/FbxMaterialInfo.hpp b/src/fbx/FbxMaterialInfo.hpp index cb026e5..aa23ece 100644 --- a/src/fbx/FbxMaterialInfo.hpp +++ b/src/fbx/FbxMaterialInfo.hpp @@ -12,11 +12,10 @@ #include "FBX2glTF.h" class FbxMaterialInfo { -public: - FbxMaterialInfo(const FbxString &name, const FbxString &shadingModel) - : name(name), - shadingModel(shadingModel) {} + public: + FbxMaterialInfo(const FbxString& name, const FbxString& shadingModel) + : name(name), shadingModel(shadingModel) {} - const FbxString name; - const FbxString shadingModel; + const FbxString name; + const FbxString shadingModel; }; diff --git a/src/fbx/FbxMaterialsAccess.cpp b/src/fbx/FbxMaterialsAccess.cpp index 13f2c2c..5063a7b 100644 --- a/src/fbx/FbxMaterialsAccess.cpp +++ b/src/fbx/FbxMaterialsAccess.cpp @@ -10,95 +10,95 @@ #include "FbxMaterialsAccess.hpp" #include "Fbx2Raw.hpp" -FbxMaterialsAccess::FbxMaterialsAccess(const FbxMesh *pMesh, const std::map &textureLocations) : - mappingMode(FbxGeometryElement::eNone), - mesh(nullptr), - indices(nullptr) -{ - if (pMesh->GetElementMaterialCount() <= 0) { - return; +FbxMaterialsAccess::FbxMaterialsAccess( + const FbxMesh* pMesh, + const std::map& textureLocations) + : mappingMode(FbxGeometryElement::eNone), mesh(nullptr), indices(nullptr) { + if (pMesh->GetElementMaterialCount() <= 0) { + return; + } + + const FbxGeometryElement::EMappingMode materialMappingMode = + pMesh->GetElementMaterial()->GetMappingMode(); + if (materialMappingMode != FbxGeometryElement::eByPolygon && + materialMappingMode != FbxGeometryElement::eAllSame) { + return; + } + + const FbxGeometryElement::EReferenceMode materialReferenceMode = + pMesh->GetElementMaterial()->GetReferenceMode(); + if (materialReferenceMode != FbxGeometryElement::eIndexToDirect) { + return; + } + + mappingMode = materialMappingMode; + mesh = pMesh; + indices = &pMesh->GetElementMaterial()->GetIndexArray(); + + for (int ii = 0; ii < indices->GetCount(); ii++) { + int materialNum = indices->GetAt(ii); + if (materialNum < 0) { + continue; } - const FbxGeometryElement::EMappingMode materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode(); - if (materialMappingMode != FbxGeometryElement::eByPolygon && materialMappingMode != FbxGeometryElement::eAllSame) { - return; + FbxSurfaceMaterial* surfaceMaterial = + mesh->GetNode()->GetSrcObject(materialNum); + + if (materialNum >= summaries.size()) { + summaries.resize(materialNum + 1); + } + auto summary = summaries[materialNum]; + if (summary == nullptr) { + summary = summaries[materialNum] = GetMaterialInfo(surfaceMaterial, textureLocations); } - const FbxGeometryElement::EReferenceMode materialReferenceMode = pMesh->GetElementMaterial()->GetReferenceMode(); - if (materialReferenceMode != FbxGeometryElement::eIndexToDirect) { - return; + if (materialNum >= userProperties.size()) { + userProperties.resize(materialNum + 1); } - - mappingMode = materialMappingMode; - mesh = pMesh; - indices = &pMesh->GetElementMaterial()->GetIndexArray(); - - for (int ii = 0; ii < indices->GetCount(); ii++) { - int materialNum = indices->GetAt(ii); - if (materialNum < 0) { - continue; - } - - FbxSurfaceMaterial* surfaceMaterial = mesh->GetNode()->GetSrcObject(materialNum); - - if (materialNum >= summaries.size()) { - summaries.resize(materialNum + 1); - } - auto summary = summaries[materialNum]; - if (summary == nullptr) { - summary = summaries[materialNum] = GetMaterialInfo( - surfaceMaterial, - textureLocations); - } - - if (materialNum >= userProperties.size()) { - userProperties.resize(materialNum + 1); - } - if (userProperties[materialNum].empty()) { - FbxProperty objectProperty = surfaceMaterial->GetFirstProperty(); - while (objectProperty.IsValid()) - { - if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) { - userProperties[materialNum].push_back(TranscribeProperty(objectProperty).dump()); - } - objectProperty = surfaceMaterial->GetNextProperty(objectProperty); - } + if (userProperties[materialNum].empty()) { + FbxProperty objectProperty = surfaceMaterial->GetFirstProperty(); + while (objectProperty.IsValid()) { + if (objectProperty.GetFlag(FbxPropertyFlags::eUserDefined)) { + userProperties[materialNum].push_back(TranscribeProperty(objectProperty).dump()); } + objectProperty = surfaceMaterial->GetNextProperty(objectProperty); + } } + } } -const std::shared_ptr FbxMaterialsAccess::GetMaterial(const int polygonIndex) const -{ - if (mappingMode != FbxGeometryElement::eNone) { - const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); - if (materialNum < 0) { - return nullptr; - } - return summaries.at((unsigned long) materialNum); +const std::shared_ptr FbxMaterialsAccess::GetMaterial( + const int polygonIndex) const { + if (mappingMode != FbxGeometryElement::eNone) { + const int materialNum = + indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); + if (materialNum < 0) { + return nullptr; } - return nullptr; + return summaries.at((unsigned long)materialNum); + } + return nullptr; } -const std::vector FbxMaterialsAccess::GetUserProperties(const int polygonIndex) const -{ - if (mappingMode != FbxGeometryElement::eNone) { - const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); - if (materialNum < 0) { - return std::vector(); - } - return userProperties.at((unsigned long)materialNum); +const std::vector FbxMaterialsAccess::GetUserProperties(const int polygonIndex) const { + if (mappingMode != FbxGeometryElement::eNone) { + const int materialNum = + indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); + if (materialNum < 0) { + return std::vector(); } - return std::vector(); + return userProperties.at((unsigned long)materialNum); + } + return std::vector(); } -std::unique_ptr -FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations) -{ - std::unique_ptr res; - res = FbxRoughMetMaterialInfo::From(material, textureLocations); - if (!res) { - res = FbxTraditionalMaterialInfo::From(material, textureLocations); - } - return res; +std::unique_ptr FbxMaterialsAccess::GetMaterialInfo( + FbxSurfaceMaterial* material, + const std::map& textureLocations) { + std::unique_ptr res; + res = FbxRoughMetMaterialInfo::From(material, textureLocations); + if (!res) { + res = FbxTraditionalMaterialInfo::From(material, textureLocations); + } + return res; } - diff --git a/src/fbx/FbxMaterialsAccess.hpp b/src/fbx/FbxMaterialsAccess.hpp index 229c839..9e7c8a3 100644 --- a/src/fbx/FbxMaterialsAccess.hpp +++ b/src/fbx/FbxMaterialsAccess.hpp @@ -11,26 +11,27 @@ #include "Fbx2Raw.hpp" #include "FbxMaterialInfo.hpp" -#include "FbxTraditionalMaterialInfo.hpp" #include "FbxRoughMetMaterialInfo.hpp" +#include "FbxTraditionalMaterialInfo.hpp" -class FbxMaterialsAccess -{ -public: +class FbxMaterialsAccess { + public: + FbxMaterialsAccess( + const FbxMesh* pMesh, + const std::map& textureLocations); - FbxMaterialsAccess(const FbxMesh *pMesh, const std::map &textureLocations); + const std::shared_ptr GetMaterial(const int polygonIndex) const; - const std::shared_ptr GetMaterial(const int polygonIndex) const; + const std::vector GetUserProperties(const int polygonIndex) const; - const std::vector GetUserProperties(const int polygonIndex) const; + std::unique_ptr GetMaterialInfo( + FbxSurfaceMaterial* material, + const std::map& textureLocations); - std::unique_ptr - GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations); - -private: - FbxGeometryElement::EMappingMode mappingMode; - std::vector> summaries {}; - std::vector> userProperties; - const FbxMesh *mesh; - const FbxLayerElementArrayTemplate *indices; + private: + FbxGeometryElement::EMappingMode mappingMode; + std::vector> summaries{}; + std::vector> userProperties; + const FbxMesh* mesh; + const FbxLayerElementArrayTemplate* indices; }; diff --git a/src/fbx/FbxRoughMetMaterialInfo.cpp b/src/fbx/FbxRoughMetMaterialInfo.cpp index 6d3eb67..10271a6 100644 --- a/src/fbx/FbxRoughMetMaterialInfo.cpp +++ b/src/fbx/FbxRoughMetMaterialInfo.cpp @@ -7,62 +7,67 @@ * of patent rights can be found in the PATENTS file in the same directory. */ - #include "FbxRoughMetMaterialInfo.hpp" +#include "FbxRoughMetMaterialInfo.hpp" -std::unique_ptr -FbxRoughMetMaterialInfo::From(FbxSurfaceMaterial *fbxMaterial, const std::map &textureLocations) -{ - std::unique_ptr res(new FbxRoughMetMaterialInfo(fbxMaterial->GetName(), FBX_SHADER_METROUGH)); +std::unique_ptr FbxRoughMetMaterialInfo::From( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations) { + std::unique_ptr res( + new FbxRoughMetMaterialInfo(fbxMaterial->GetName(), FBX_SHADER_METROUGH)); - const FbxProperty mayaProp = fbxMaterial->FindProperty("Maya"); - if (mayaProp.GetPropertyDataType() != FbxCompoundDT) { - return nullptr; - } - if (!fbxMaterial->ShadingModel.Get().IsEmpty()) { - ::fmt::printf("Warning: Material %s has surprising shading model: %s\n", - fbxMaterial->GetName(), fbxMaterial->ShadingModel.Get()); - } + const FbxProperty mayaProp = fbxMaterial->FindProperty("Maya"); + if (mayaProp.GetPropertyDataType() != FbxCompoundDT) { + return nullptr; + } + if (!fbxMaterial->ShadingModel.Get().IsEmpty()) { + ::fmt::printf( + "Warning: Material %s has surprising shading model: %s\n", + fbxMaterial->GetName(), + fbxMaterial->ShadingModel.Get()); + } - auto getTex = [&](std::string propName) { - const FbxFileTexture *ptr = nullptr; + auto getTex = [&](std::string propName) { + const FbxFileTexture* ptr = nullptr; - const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str()); - if (useProp.IsValid() && useProp.Get()) { - const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str()); - if (texProp.IsValid()) { - ptr = texProp.GetSrcObject(); - if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { - ptr = nullptr; - } - } - } else if (verboseOutput && useProp.IsValid()) { - fmt::printf("Note: Property '%s' of material '%s' exists, but is flagged as 'do not use'.\n", - propName, fbxMaterial->GetName()); + const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str()); + if (useProp.IsValid() && useProp.Get()) { + const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str()); + if (texProp.IsValid()) { + ptr = texProp.GetSrcObject(); + if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { + ptr = nullptr; } - return ptr; - }; + } + } else if (verboseOutput && useProp.IsValid()) { + fmt::printf( + "Note: Property '%s' of material '%s' exists, but is flagged as 'do not use'.\n", + propName, + fbxMaterial->GetName()); + } + return ptr; + }; - auto getVec = [&](std::string propName) -> FbxDouble3 { - const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); - return vecProp.IsValid() ? vecProp.Get() : FbxDouble3(1, 1, 1); - }; + auto getVec = [&](std::string propName) -> FbxDouble3 { + const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); + return vecProp.IsValid() ? vecProp.Get() : FbxDouble3(1, 1, 1); + }; - auto getVal = [&](std::string propName) -> FbxDouble { - const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str()); - return vecProp.IsValid() ? vecProp.Get() : 0; - }; + auto getVal = [&](std::string propName) -> FbxDouble { + const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); + return vecProp.IsValid() ? vecProp.Get() : 0; + }; - res->texNormal = getTex("normal"); - res->texColor = getTex("color"); - res->colBase = getVec("base_color"); - res->texAmbientOcclusion = getTex("ao"); - res->texEmissive = getTex("emissive"); - res->colEmissive = getVec("emissive"); - res->emissiveIntensity = getVal("emissive_intensity"); - res->texMetallic = getTex("metallic"); - res->metallic = getVal("metallic"); - res->texRoughness = getTex("roughness"); - res->roughness = getVal("roughness"); + res->texNormal = getTex("normal"); + res->texColor = getTex("color"); + res->colBase = getVec("base_color"); + res->texAmbientOcclusion = getTex("ao"); + res->texEmissive = getTex("emissive"); + res->colEmissive = getVec("emissive"); + res->emissiveIntensity = getVal("emissive_intensity"); + res->texMetallic = getTex("metallic"); + res->metallic = getVal("metallic"); + res->texRoughness = getTex("roughness"); + res->roughness = getVal("roughness"); - return res; + return res; } diff --git a/src/fbx/FbxRoughMetMaterialInfo.hpp b/src/fbx/FbxRoughMetMaterialInfo.hpp index 0058744..f954c1d 100644 --- a/src/fbx/FbxRoughMetMaterialInfo.hpp +++ b/src/fbx/FbxRoughMetMaterialInfo.hpp @@ -7,36 +7,35 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include #include -#include -#include #include +#include +#include #include #include -#include #include "FbxMaterialInfo.hpp" struct FbxRoughMetMaterialInfo : FbxMaterialInfo { - static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness"; + static constexpr const char* FBX_SHADER_METROUGH = "MetallicRoughness"; - static std::unique_ptr From( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations); + static std::unique_ptr From( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations); - FbxRoughMetMaterialInfo(const FbxString &name, const FbxString &shadingModel) - : FbxMaterialInfo(name, shadingModel) - {} + FbxRoughMetMaterialInfo(const FbxString& name, const FbxString& shadingModel) + : FbxMaterialInfo(name, shadingModel) {} - const FbxFileTexture *texColor {}; - FbxVector4 colBase {}; - const FbxFileTexture *texNormal {}; - const FbxFileTexture *texMetallic {}; - FbxDouble metallic {}; - const FbxFileTexture *texRoughness {}; - FbxDouble roughness {}; - const FbxFileTexture *texEmissive {}; - FbxVector4 colEmissive {}; - FbxDouble emissiveIntensity {}; - const FbxFileTexture *texAmbientOcclusion {}; + const FbxFileTexture* texColor{}; + FbxVector4 colBase{}; + const FbxFileTexture* texNormal{}; + const FbxFileTexture* texMetallic{}; + FbxDouble metallic{}; + const FbxFileTexture* texRoughness{}; + FbxDouble roughness{}; + const FbxFileTexture* texEmissive{}; + FbxVector4 colEmissive{}; + FbxDouble emissiveIntensity{}; + const FbxFileTexture* texAmbientOcclusion{}; }; diff --git a/src/fbx/FbxSkinningAccess.cpp b/src/fbx/FbxSkinningAccess.cpp index 7279065..f76d8ec 100644 --- a/src/fbx/FbxSkinningAccess.cpp +++ b/src/fbx/FbxSkinningAccess.cpp @@ -9,84 +9,89 @@ #include "FbxSkinningAccess.hpp" -FbxSkinningAccess::FbxSkinningAccess(const FbxMesh *pMesh, FbxScene *pScene, FbxNode *pNode) - : rootIndex(-1) -{ - for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) { - FbxSkin *skin = reinterpret_cast< FbxSkin * >( pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); - if (skin != nullptr) { - const int clusterCount = skin->GetClusterCount(); - if (clusterCount == 0) { - continue; - } - int controlPointCount = pMesh->GetControlPointsCount(); - vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0)); - vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f)); - - for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { - FbxCluster *cluster = skin->GetCluster(clusterIndex); - const int indexCount = cluster->GetControlPointIndicesCount(); - const int *clusterIndices = cluster->GetControlPointIndices(); - const double *clusterWeights = cluster->GetControlPointWeights(); - - assert(cluster->GetLinkMode() == FbxCluster::eNormalize); - - // Transform link matrix. - FbxAMatrix transformLinkMatrix; - cluster->GetTransformLinkMatrix(transformLinkMatrix); - - // The transformation of the mesh at binding time - FbxAMatrix transformMatrix; - cluster->GetTransformMatrix(transformMatrix); - - // Inverse bind matrix. - FbxAMatrix globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; - inverseBindMatrices.emplace_back(globalBindposeInverseMatrix); - - jointNodes.push_back(cluster->GetLink()); - jointIds.push_back(cluster->GetLink()->GetUniqueID()); - - const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform(); - jointSkinningTransforms.push_back(FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix)); - jointInverseGlobalTransforms.push_back(FbxMatrix(globalNodeTransform.Inverse())); - - for (int i = 0; i < indexCount; i++) { - if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) { - continue; - } - if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) { - continue; - } - vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex; - vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float) clusterWeights[i]; - for (int j = MAX_WEIGHTS - 1; j > 0; j--) { - if (vertexJointWeights[clusterIndices[i]][j - 1] >= vertexJointWeights[clusterIndices[i]][j]) { - break; - } - std::swap(vertexJointIndices[clusterIndices[i]][j - 1], vertexJointIndices[clusterIndices[i]][j]); - std::swap(vertexJointWeights[clusterIndices[i]][j - 1], vertexJointWeights[clusterIndices[i]][j]); - } - } - - } - for (int i = 0; i < controlPointCount; i++) { - vertexJointWeights[i] = vertexJointWeights[i].Normalized(); +FbxSkinningAccess::FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, FbxNode* pNode) + : rootIndex(-1) { + for (int deformerIndex = 0; deformerIndex < pMesh->GetDeformerCount(); deformerIndex++) { + FbxSkin* skin = + reinterpret_cast(pMesh->GetDeformer(deformerIndex, FbxDeformer::eSkin)); + if (skin != nullptr) { + const int clusterCount = skin->GetClusterCount(); + if (clusterCount == 0) { + continue; + } + int controlPointCount = pMesh->GetControlPointsCount(); + vertexJointIndices.resize(controlPointCount, Vec4i(0, 0, 0, 0)); + vertexJointWeights.resize(controlPointCount, Vec4f(0.0f, 0.0f, 0.0f, 0.0f)); + + for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { + FbxCluster* cluster = skin->GetCluster(clusterIndex); + const int indexCount = cluster->GetControlPointIndicesCount(); + const int* clusterIndices = cluster->GetControlPointIndices(); + const double* clusterWeights = cluster->GetControlPointWeights(); + + assert(cluster->GetLinkMode() == FbxCluster::eNormalize); + + // Transform link matrix. + FbxAMatrix transformLinkMatrix; + cluster->GetTransformLinkMatrix(transformLinkMatrix); + + // The transformation of the mesh at binding time + FbxAMatrix transformMatrix; + cluster->GetTransformMatrix(transformMatrix); + + // Inverse bind matrix. + FbxAMatrix globalBindposeInverseMatrix = transformLinkMatrix.Inverse() * transformMatrix; + inverseBindMatrices.emplace_back(globalBindposeInverseMatrix); + + jointNodes.push_back(cluster->GetLink()); + jointIds.push_back(cluster->GetLink()->GetUniqueID()); + + const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform(); + jointSkinningTransforms.push_back( + FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix)); + jointInverseGlobalTransforms.push_back(FbxMatrix(globalNodeTransform.Inverse())); + + for (int i = 0; i < indexCount; i++) { + if (clusterIndices[i] < 0 || clusterIndices[i] >= controlPointCount) { + continue; + } + if (clusterWeights[i] <= vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1]) { + continue; + } + vertexJointIndices[clusterIndices[i]][MAX_WEIGHTS - 1] = clusterIndex; + vertexJointWeights[clusterIndices[i]][MAX_WEIGHTS - 1] = (float)clusterWeights[i]; + for (int j = MAX_WEIGHTS - 1; j > 0; j--) { + if (vertexJointWeights[clusterIndices[i]][j - 1] >= + vertexJointWeights[clusterIndices[i]][j]) { + break; } + std::swap( + vertexJointIndices[clusterIndices[i]][j - 1], + vertexJointIndices[clusterIndices[i]][j]); + std::swap( + vertexJointWeights[clusterIndices[i]][j - 1], + vertexJointWeights[clusterIndices[i]][j]); + } } + } + for (int i = 0; i < controlPointCount; i++) { + vertexJointWeights[i] = vertexJointWeights[i].Normalized(); + } } + } - rootIndex = -1; - for (size_t i = 0; i < jointNodes.size() && rootIndex == -1; i++) { - rootIndex = (int) i; - FbxNode *parent = jointNodes[i]->GetParent(); - if (parent == nullptr) { - break; - } - for (size_t j = 0; j < jointNodes.size(); j++) { - if (jointNodes[j] == parent) { - rootIndex = -1; - break; - } - } + rootIndex = -1; + for (size_t i = 0; i < jointNodes.size() && rootIndex == -1; i++) { + rootIndex = (int)i; + FbxNode* parent = jointNodes[i]->GetParent(); + if (parent == nullptr) { + break; } + for (size_t j = 0; j < jointNodes.size(); j++) { + if (jointNodes[j] == parent) { + rootIndex = -1; + break; + } + } + } } diff --git a/src/fbx/FbxSkinningAccess.hpp b/src/fbx/FbxSkinningAccess.hpp index cabf48b..d2b42d8 100644 --- a/src/fbx/FbxSkinningAccess.hpp +++ b/src/fbx/FbxSkinningAccess.hpp @@ -9,84 +9,72 @@ #pragma once +#include #include -#include -#include #include +#include +#include #include #include -#include #include "FBX2glTF.h" -class FbxSkinningAccess -{ -public: +class FbxSkinningAccess { + public: + static const int MAX_WEIGHTS = 4; - static const int MAX_WEIGHTS = 4; + FbxSkinningAccess(const FbxMesh* pMesh, FbxScene* pScene, FbxNode* pNode); - FbxSkinningAccess(const FbxMesh *pMesh, FbxScene *pScene, FbxNode *pNode); + bool IsSkinned() const { + return (vertexJointWeights.size() > 0); + } - bool IsSkinned() const - { - return (vertexJointWeights.size() > 0); - } + int GetNodeCount() const { + return (int)jointNodes.size(); + } - int GetNodeCount() const - { - return (int) jointNodes.size(); - } + FbxNode* GetJointNode(const int jointIndex) const { + return jointNodes[jointIndex]; + } - FbxNode *GetJointNode(const int jointIndex) const - { - return jointNodes[jointIndex]; - } + const long GetJointId(const int jointIndex) const { + return jointIds[jointIndex]; + } - const long GetJointId(const int jointIndex) const - { - return jointIds[jointIndex]; - } + const FbxMatrix& GetJointSkinningTransform(const int jointIndex) const { + return jointSkinningTransforms[jointIndex]; + } - const FbxMatrix &GetJointSkinningTransform(const int jointIndex) const - { - return jointSkinningTransforms[jointIndex]; - } + const FbxMatrix& GetJointInverseGlobalTransforms(const int jointIndex) const { + return jointInverseGlobalTransforms[jointIndex]; + } - const FbxMatrix &GetJointInverseGlobalTransforms(const int jointIndex) const - { - return jointInverseGlobalTransforms[jointIndex]; - } + const long GetRootNode() const { + assert(rootIndex != -1); + return jointIds[rootIndex]; + } - const long GetRootNode() const - { - assert(rootIndex != -1); - return jointIds[rootIndex]; - } + const FbxAMatrix& GetInverseBindMatrix(const int jointIndex) const { + return inverseBindMatrices[jointIndex]; + } - const FbxAMatrix &GetInverseBindMatrix(const int jointIndex) const - { - return inverseBindMatrices[jointIndex]; - } + const Vec4i GetVertexIndices(const int controlPointIndex) const { + return (!vertexJointIndices.empty()) ? vertexJointIndices[controlPointIndex] + : Vec4i(0, 0, 0, 0); + } - const Vec4i GetVertexIndices(const int controlPointIndex) const - { - return (!vertexJointIndices.empty()) ? - vertexJointIndices[controlPointIndex] : Vec4i(0, 0, 0, 0); - } + const Vec4f GetVertexWeights(const int controlPointIndex) const { + return (!vertexJointWeights.empty()) ? vertexJointWeights[controlPointIndex] + : Vec4f(0, 0, 0, 0); + } - const Vec4f GetVertexWeights(const int controlPointIndex) const - { - return (!vertexJointWeights.empty()) ? - vertexJointWeights[controlPointIndex] : Vec4f(0, 0, 0, 0); - } - -private: - int rootIndex; - std::vector jointIds; - std::vector jointNodes; - std::vector jointSkinningTransforms; - std::vector jointInverseGlobalTransforms; - std::vector inverseBindMatrices; - std::vector vertexJointIndices; - std::vector vertexJointWeights; + private: + int rootIndex; + std::vector jointIds; + std::vector jointNodes; + std::vector jointSkinningTransforms; + std::vector jointInverseGlobalTransforms; + std::vector inverseBindMatrices; + std::vector vertexJointIndices; + std::vector vertexJointWeights; }; diff --git a/src/fbx/FbxTraditionalMaterialInfo.cpp b/src/fbx/FbxTraditionalMaterialInfo.cpp index 0b42713..370973d 100644 --- a/src/fbx/FbxTraditionalMaterialInfo.cpp +++ b/src/fbx/FbxTraditionalMaterialInfo.cpp @@ -9,116 +9,129 @@ #include "FbxTraditionalMaterialInfo.hpp" -std::unique_ptr -FbxTraditionalMaterialInfo::From(FbxSurfaceMaterial *fbxMaterial, const std::map &textureLocations) -{ - auto getSurfaceScalar = [&](const char *propName) -> std::tuple { - const FbxProperty prop = fbxMaterial->FindProperty(propName); +std::unique_ptr FbxTraditionalMaterialInfo::From( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations) { + auto getSurfaceScalar = [&](const char* propName) -> std::tuple { + const FbxProperty prop = fbxMaterial->FindProperty(propName); - FbxDouble val(0); - FbxFileTexture *tex = prop.GetSrcObject(); - if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { - tex = nullptr; - } - if (tex == nullptr && prop.IsValid()) { - val = prop.Get(); - } - return std::make_tuple(val, tex); - }; + FbxDouble val(0); + FbxFileTexture* tex = prop.GetSrcObject(); + if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { + tex = nullptr; + } + if (tex == nullptr && prop.IsValid()) { + val = prop.Get(); + } + return std::make_tuple(val, tex); + }; - auto getSurfaceVector = [&](const char *propName) -> std::tuple { - const FbxProperty prop = fbxMaterial->FindProperty(propName); + auto getSurfaceVector = [&](const char* propName) -> std::tuple { + const FbxProperty prop = fbxMaterial->FindProperty(propName); - FbxDouble3 val(1, 1, 1); - FbxFileTexture *tex = prop.GetSrcObject(); - if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { - tex = nullptr; - } - if (tex == nullptr && prop.IsValid()) { - val = prop.Get(); - } - return std::make_tuple(val, tex); - }; + FbxDouble3 val(1, 1, 1); + FbxFileTexture* tex = prop.GetSrcObject(); + if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { + tex = nullptr; + } + if (tex == nullptr && prop.IsValid()) { + val = prop.Get(); + } + return std::make_tuple(val, tex); + }; - auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple { - const FbxProperty colProp = fbxMaterial->FindProperty(colName); - const FbxProperty facProp = fbxMaterial->FindProperty(facName); + auto getSurfaceValues = + [&](const char* colName, + const char* facName) -> std::tuple { + const FbxProperty colProp = fbxMaterial->FindProperty(colName); + const FbxProperty facProp = fbxMaterial->FindProperty(facName); - FbxDouble3 colorVal(1, 1, 1); - FbxDouble factorVal(1); + FbxDouble3 colorVal(1, 1, 1); + FbxDouble factorVal(1); - FbxFileTexture *colTex = colProp.GetSrcObject(); - if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) { - colTex = nullptr; - } - if (colTex == nullptr && colProp.IsValid()) { - colorVal = colProp.Get(); - } - FbxFileTexture *facTex = facProp.GetSrcObject(); - if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) { - facTex = nullptr; - } - if (facTex == nullptr && facProp.IsValid()) { - factorVal = facProp.Get(); - } + FbxFileTexture* colTex = colProp.GetSrcObject(); + if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) { + colTex = nullptr; + } + if (colTex == nullptr && colProp.IsValid()) { + colorVal = colProp.Get(); + } + FbxFileTexture* facTex = facProp.GetSrcObject(); + if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) { + facTex = nullptr; + } + if (facTex == nullptr && facProp.IsValid()) { + factorVal = facProp.Get(); + } - auto val = FbxVector4( - colorVal[0] * factorVal, - colorVal[1] * factorVal, - colorVal[2] * factorVal, - factorVal); - return std::make_tuple(val, colTex, facTex); - }; + auto val = FbxVector4( + colorVal[0] * factorVal, colorVal[1] * factorVal, colorVal[2] * factorVal, factorVal); + return std::make_tuple(val, colTex, facTex); + }; - std::string name = fbxMaterial->GetName(); - std::unique_ptr res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get())); + std::string name = fbxMaterial->GetName(); + std::unique_ptr res( + new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get())); - // four properties are on the same structure and follow the same rules - auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple{ - FbxFileTexture *colTex, *facTex; - FbxVector4 vec; - - std::tie(vec, colTex, facTex) = getSurfaceValues(colName, facName); - if (colTex) { - if (facTex) { - fmt::printf("Warning: Mat [%s]: Can't handle both %s and %s textures; discarding %s.\n", name, colName, facName, facName); - } - return std::make_tuple(vec, colTex); - } - return std::make_tuple(vec, facTex); - }; - - std::tie(res->colAmbient, res->texAmbient) = - handleBasicProperty(FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor); - std::tie(res->colSpecular, res->texSpecular) = - handleBasicProperty(FbxSurfaceMaterial::sSpecular, FbxSurfaceMaterial::sSpecularFactor); - std::tie(res->colDiffuse, res->texDiffuse) = - handleBasicProperty(FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor); - std::tie(res->colEmissive, res->texEmissive) = - handleBasicProperty(FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor); - - // the normal map can only ever be a map, ignore everything else - tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap); - - // shininess can be a map or a factor; afaict the map is always 'ShininessExponent' and the - // value is always found in 'Shininess' but only sometimes in 'ShininessExponent'. - tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent"); - tie(res->shininess, std::ignore) = getSurfaceScalar("Shininess"); - - // for transparency we just want a constant vector value; - FbxVector4 transparency; - // extract any existing textures only so we can warn that we're throwing them away + // four properties are on the same structure and follow the same rules + auto handleBasicProperty = [&](const char* colName, + const char* facName) -> std::tuple { FbxFileTexture *colTex, *facTex; - std::tie(transparency, colTex, facTex) = - getSurfaceValues(FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor); - if (colTex) { - fmt::printf("Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", name, FbxSurfaceMaterial::sTransparentColor); - } - if (facTex) { - fmt::printf("Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", name, FbxSurfaceMaterial::sTransparencyFactor); - } - // FBX color is RGB, so we calculate the A channel as the average of the FBX transparency color vector - res->colDiffuse[3] = 1.0 - (transparency[0] + transparency[1] + transparency[2])/3.0; + FbxVector4 vec; - return res; + std::tie(vec, colTex, facTex) = getSurfaceValues(colName, facName); + if (colTex) { + if (facTex) { + fmt::printf( + "Warning: Mat [%s]: Can't handle both %s and %s textures; discarding %s.\n", + name, + colName, + facName, + facName); + } + return std::make_tuple(vec, colTex); + } + return std::make_tuple(vec, facTex); + }; + + std::tie(res->colAmbient, res->texAmbient) = + handleBasicProperty(FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor); + std::tie(res->colSpecular, res->texSpecular) = + handleBasicProperty(FbxSurfaceMaterial::sSpecular, FbxSurfaceMaterial::sSpecularFactor); + std::tie(res->colDiffuse, res->texDiffuse) = + handleBasicProperty(FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor); + std::tie(res->colEmissive, res->texEmissive) = + handleBasicProperty(FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor); + + // the normal map can only ever be a map, ignore everything else + tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap); + + // shininess can be a map or a factor; afaict the map is always 'ShininessExponent' and the + // value is always found in 'Shininess' but only sometimes in 'ShininessExponent'. + tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent"); + tie(res->shininess, std::ignore) = getSurfaceScalar("Shininess"); + + // for transparency we just want a constant vector value; + FbxVector4 transparency; + // extract any existing textures only so we can warn that we're throwing them away + FbxFileTexture *colTex, *facTex; + std::tie(transparency, colTex, facTex) = getSurfaceValues( + FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor); + if (colTex) { + fmt::printf( + "Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", + name, + FbxSurfaceMaterial::sTransparentColor); + } + if (facTex) { + fmt::printf( + "Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", + name, + FbxSurfaceMaterial::sTransparencyFactor); + } + // FBX color is RGB, so we calculate the A channel as the average of the FBX transparency color + // vector + res->colDiffuse[3] = 1.0 - (transparency[0] + transparency[1] + transparency[2]) / 3.0; + + return res; } diff --git a/src/fbx/FbxTraditionalMaterialInfo.hpp b/src/fbx/FbxTraditionalMaterialInfo.hpp index ed86387..edb23b6 100644 --- a/src/fbx/FbxTraditionalMaterialInfo.hpp +++ b/src/fbx/FbxTraditionalMaterialInfo.hpp @@ -7,38 +7,37 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include #include -#include -#include #include +#include +#include #include #include -#include #include "FbxMaterialInfo.hpp" struct FbxTraditionalMaterialInfo : FbxMaterialInfo { - static constexpr const char *FBX_SHADER_LAMBERT = "Lambert"; - static constexpr const char *FBX_SHADER_BLINN = "Blinn"; - static constexpr const char *FBX_SHADER_PHONG = "Phong"; + static constexpr const char* FBX_SHADER_LAMBERT = "Lambert"; + static constexpr const char* FBX_SHADER_BLINN = "Blinn"; + static constexpr const char* FBX_SHADER_PHONG = "Phong"; - FbxTraditionalMaterialInfo(const FbxString &name, const FbxString &shadingModel) - : FbxMaterialInfo(name, shadingModel) - {} + FbxTraditionalMaterialInfo(const FbxString& name, const FbxString& shadingModel) + : FbxMaterialInfo(name, shadingModel) {} - FbxFileTexture *texAmbient {}; - FbxVector4 colAmbient {}; - FbxFileTexture *texSpecular {}; - FbxVector4 colSpecular {}; - FbxFileTexture *texDiffuse {}; - FbxVector4 colDiffuse {}; - FbxFileTexture *texEmissive {}; - FbxVector4 colEmissive {}; - FbxFileTexture *texNormal {}; - FbxFileTexture *texShininess {}; - FbxDouble shininess {}; + FbxFileTexture* texAmbient{}; + FbxVector4 colAmbient{}; + FbxFileTexture* texSpecular{}; + FbxVector4 colSpecular{}; + FbxFileTexture* texDiffuse{}; + FbxVector4 colDiffuse{}; + FbxFileTexture* texEmissive{}; + FbxVector4 colEmissive{}; + FbxFileTexture* texNormal{}; + FbxFileTexture* texShininess{}; + FbxDouble shininess{}; - static std::unique_ptr From( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations); + static std::unique_ptr From( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations); }; diff --git a/src/fbx/materials/3dsMaxPhysicalMaterial.cpp b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp index 3a6d64e..deb0cc1 100644 --- a/src/fbx/materials/3dsMaxPhysicalMaterial.cpp +++ b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp @@ -10,116 +10,117 @@ #include "RoughnessMetallicMaterials.hpp" std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::resolve() const { - const FbxProperty topProp = fbxMaterial->FindProperty("3dsMax"); - if (topProp.GetPropertyDataType() != FbxCompoundDT) { - return nullptr; - } - const FbxProperty props = fbxMaterial->FindProperty("Parameters"); + const FbxProperty topProp = fbxMaterial->FindProperty("3dsMax"); + if (topProp.GetPropertyDataType() != FbxCompoundDT) { + return nullptr; + } + const FbxProperty props = fbxMaterial->FindProperty("Parameters"); - FbxString shadingModel = fbxMaterial->ShadingModel.Get(); - if (!shadingModel.IsEmpty() && shadingModel != "unknown") { - ::fmt::printf("Warning: Material %s has surprising shading model: %s\n", - fbxMaterial->GetName(), shadingModel); - } + FbxString shadingModel = fbxMaterial->ShadingModel.Get(); + if (!shadingModel.IsEmpty() && shadingModel != "unknown") { + ::fmt::printf( + "Warning: Material %s has surprising shading model: %s\n", + fbxMaterial->GetName(), + shadingModel); + } - auto getTex = [&](std::string propName) -> const FbxFileTexture * { - const FbxFileTexture *ptr = nullptr; + auto getTex = [&](std::string propName) -> const FbxFileTexture* { + const FbxFileTexture* ptr = nullptr; - const FbxProperty useProp = props.FindHierarchical((propName + "_map_on").c_str()); - if (useProp.IsValid() && useProp.Get()) { - const FbxProperty texProp = useProp.FindHierarchical((propName + "_map").c_str()); - if (texProp.IsValid()) { - ptr = texProp.GetSrcObject(); - if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { - ptr = nullptr; - } - } - } else if (verboseOutput && useProp.IsValid()) { - fmt::printf("Note: property '%s' of 3dsMax Physical material '%s' exists, but is flagged as 'off'.\n", - propName, fbxMaterial->GetName()); + const FbxProperty useProp = props.FindHierarchical((propName + "_map_on").c_str()); + if (useProp.IsValid() && useProp.Get()) { + const FbxProperty texProp = useProp.FindHierarchical((propName + "_map").c_str()); + if (texProp.IsValid()) { + ptr = texProp.GetSrcObject(); + if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { + ptr = nullptr; } - return ptr; - }; - - int materialMode = getValue(props, "material_mode", 0); - fmt::printf("Note: 3dsMax Physical material has material_mode = %d.\n", materialMode); - - // baseWeight && baseColor - FbxDouble baseWeight = getValue(props, "base_weight", 1.0); - const auto *baseWeightMap = getTex("base_weight"); - FbxDouble4 baseCol = getValue(props, "base_color", FbxDouble4(0.5, 0.5, 0.5, 1.0)); - const auto *baseTex = getTex("base_color"); - - double emissiveWeight = getValue(props, "emission", 0.0); - const auto *emissiveWeightMap = getTex("emission"); - FbxDouble4 emissiveColor = getValue(props, "emit_color", FbxDouble4(1, 1, 1, 1)); - const auto *emissiveColorMap = getTex("emit_color"); - // TODO: emit_luminance, emit_kelvin? - - // roughness & metalness: supported - double roughness = getValue(props, "roughness", 0.0); - const auto *roughnessMap = getTex("roughness"); - double metalness = getValue(props, "metalness", 0.0); - const auto *metalnessMap = getTex("metalness"); - - // TODO: does invertRoughness affect roughness_map too? - bool invertRoughness = getValue(props, "inv_roughness", false); - if (invertRoughness) { - roughness = 1.0f - roughness; + } + } else if (verboseOutput && useProp.IsValid()) { + fmt::printf( + "Note: property '%s' of 3dsMax Physical material '%s' exists, but is flagged as 'off'.\n", + propName, + fbxMaterial->GetName()); } + return ptr; + }; - // TODO: attempt to bake transparency > 0.0f into the alpha of baseColour? - double transparency = getValue(props, "transparency", 0.0); - const auto *transparencyMap = getTex("transparency"); + int materialMode = getValue(props, "material_mode", 0); + fmt::printf("Note: 3dsMax Physical material has material_mode = %d.\n", materialMode); - // SSS: not supported - double scattering = getValue(props, "scattering", 0.0); - const auto *scatteringMap = getTex("scattering"); + // baseWeight && baseColor + FbxDouble baseWeight = getValue(props, "base_weight", 1.0); + const auto* baseWeightMap = getTex("base_weight"); + FbxDouble4 baseCol = getValue(props, "base_color", FbxDouble4(0.5, 0.5, 0.5, 1.0)); + const auto* baseTex = getTex("base_color"); - // reflectivity: not supported - double reflectivityWeight = getValue(props, "reflectivity", 1.); - const auto *reflectivityWeightMap = getTex("reflectivity"); - FbxDouble4 reflectivityColor = getValue(props, "refl_color", FbxDouble4(1, 1, 1, 1)); - const auto *reflectivityColorMap = getTex("refl_color"); + double emissiveWeight = getValue(props, "emission", 0.0); + const auto* emissiveWeightMap = getTex("emission"); + FbxDouble4 emissiveColor = getValue(props, "emit_color", FbxDouble4(1, 1, 1, 1)); + const auto* emissiveColorMap = getTex("emit_color"); + // TODO: emit_luminance, emit_kelvin? - // coatings: not supported - double coating = getValue(props, "coating", 0.0); + // roughness & metalness: supported + double roughness = getValue(props, "roughness", 0.0); + const auto* roughnessMap = getTex("roughness"); + double metalness = getValue(props, "metalness", 0.0); + const auto* metalnessMap = getTex("metalness"); - // diffuse roughness: not supported - double diffuseRoughness = getValue(props, "diff_roughness", 0.); + // TODO: does invertRoughness affect roughness_map too? + bool invertRoughness = getValue(props, "inv_roughness", false); + if (invertRoughness) { + roughness = 1.0f - roughness; + } - // explicit brdf curve control: not supported - bool isBrdfMode = getValue(props, "brdf_mode", false); + // TODO: attempt to bake transparency > 0.0f into the alpha of baseColour? + double transparency = getValue(props, "transparency", 0.0); + const auto* transparencyMap = getTex("transparency"); - // anisotrophy: not supported - double anisotropy = getValue(props, "anisotropy", 1.0); + // SSS: not supported + double scattering = getValue(props, "scattering", 0.0); + const auto* scatteringMap = getTex("scattering"); - // TODO: how the heck do we combine these to generate a normal map? - const auto *bumpMap = getTex("bump"); - const auto *displacementMap = getTex("displacement"); + // reflectivity: not supported + double reflectivityWeight = getValue(props, "reflectivity", 1.); + const auto* reflectivityWeightMap = getTex("reflectivity"); + FbxDouble4 reflectivityColor = getValue(props, "refl_color", FbxDouble4(1, 1, 1, 1)); + const auto* reflectivityColorMap = getTex("refl_color"); - std::unique_ptr res( - new FbxRoughMetMaterialInfo( - fbxMaterial->GetName(), - FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, - baseCol, - metalness, - roughness - ) - ); - res->texBaseColor = baseTex; - res->baseWeight = baseWeight; - res->texBaseWeight = baseWeightMap; + // coatings: not supported + double coating = getValue(props, "coating", 0.0); - res->texMetallic = metalnessMap; - res->texRoughness = roughnessMap; + // diffuse roughness: not supported + double diffuseRoughness = getValue(props, "diff_roughness", 0.); - res->texNormal = bumpMap; // TODO LOL NO NONO + // explicit brdf curve control: not supported + bool isBrdfMode = getValue(props, "brdf_mode", false); - res->emissive = emissiveColor; - res->emissiveIntensity = emissiveWeight; - res->texEmissive = emissiveColorMap; - res->texEmissiveWeight = emissiveWeightMap; + // anisotrophy: not supported + double anisotropy = getValue(props, "anisotropy", 1.0); - return res; + // TODO: how the heck do we combine these to generate a normal map? + const auto* bumpMap = getTex("bump"); + const auto* displacementMap = getTex("displacement"); + + std::unique_ptr res(new FbxRoughMetMaterialInfo( + fbxMaterial->GetName(), + FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, + baseCol, + metalness, + roughness)); + res->texBaseColor = baseTex; + res->baseWeight = baseWeight; + res->texBaseWeight = baseWeightMap; + + res->texMetallic = metalnessMap; + res->texRoughness = roughnessMap; + + res->texNormal = bumpMap; // TODO LOL NO NONO + + res->emissive = emissiveColor; + res->emissiveIntensity = emissiveWeight; + res->texEmissive = emissiveColorMap; + res->texEmissiveWeight = emissiveWeightMap; + + return res; } diff --git a/src/fbx/materials/FbxMaterials.cpp b/src/fbx/materials/FbxMaterials.cpp index 542d837..05237db 100644 --- a/src/fbx/materials/FbxMaterials.cpp +++ b/src/fbx/materials/FbxMaterials.cpp @@ -11,68 +11,70 @@ #include "RoughnessMetallicMaterials.hpp" #include "TraditionalMaterials.hpp" -FbxMaterialsAccess::FbxMaterialsAccess(const FbxMesh *pMesh, const std::map &textureLocations) : - mappingMode(FbxGeometryElement::eNone), - mesh(nullptr), - indices(nullptr) -{ - if (pMesh->GetElementMaterialCount() <= 0) { - return; - } +FbxMaterialsAccess::FbxMaterialsAccess( + const FbxMesh* pMesh, + const std::map& textureLocations) + : mappingMode(FbxGeometryElement::eNone), mesh(nullptr), indices(nullptr) { + if (pMesh->GetElementMaterialCount() <= 0) { + return; + } - const FbxGeometryElement::EMappingMode materialMappingMode = pMesh->GetElementMaterial()->GetMappingMode(); - if (materialMappingMode != FbxGeometryElement::eByPolygon && materialMappingMode != FbxGeometryElement::eAllSame) { - return; - } + const FbxGeometryElement::EMappingMode materialMappingMode = + pMesh->GetElementMaterial()->GetMappingMode(); + if (materialMappingMode != FbxGeometryElement::eByPolygon && + materialMappingMode != FbxGeometryElement::eAllSame) { + return; + } - const FbxGeometryElement::EReferenceMode materialReferenceMode = pMesh->GetElementMaterial()->GetReferenceMode(); - if (materialReferenceMode != FbxGeometryElement::eIndexToDirect) { - return; - } + const FbxGeometryElement::EReferenceMode materialReferenceMode = + pMesh->GetElementMaterial()->GetReferenceMode(); + if (materialReferenceMode != FbxGeometryElement::eIndexToDirect) { + return; + } - mappingMode = materialMappingMode; - mesh = pMesh; - indices = &pMesh->GetElementMaterial()->GetIndexArray(); + mappingMode = materialMappingMode; + mesh = pMesh; + indices = &pMesh->GetElementMaterial()->GetIndexArray(); - for (int ii = 0; ii < indices->GetCount(); ii++) { - int materialNum = indices->GetAt(ii); - if (materialNum < 0) { - continue; - } - if (materialNum >= summaries.size()) { - summaries.resize(materialNum + 1); - } - auto summary = summaries[materialNum]; - if (summary == nullptr) { - summary = summaries[materialNum] = GetMaterialInfo( - mesh->GetNode()->GetSrcObject(materialNum), - textureLocations); - } + for (int ii = 0; ii < indices->GetCount(); ii++) { + int materialNum = indices->GetAt(ii); + if (materialNum < 0) { + continue; } + if (materialNum >= summaries.size()) { + summaries.resize(materialNum + 1); + } + auto summary = summaries[materialNum]; + if (summary == nullptr) { + summary = summaries[materialNum] = GetMaterialInfo( + mesh->GetNode()->GetSrcObject(materialNum), textureLocations); + } + } } -const std::shared_ptr FbxMaterialsAccess::GetMaterial(const int polygonIndex) const -{ - if (mappingMode != FbxGeometryElement::eNone) { - const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); - if (materialNum < 0) { - return nullptr; - } - return summaries.at((unsigned long) materialNum); +const std::shared_ptr FbxMaterialsAccess::GetMaterial( + const int polygonIndex) const { + if (mappingMode != FbxGeometryElement::eNone) { + const int materialNum = + indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0); + if (materialNum < 0) { + return nullptr; } - return nullptr; + return summaries.at((unsigned long)materialNum); + } + return nullptr; } -std::unique_ptr -FbxMaterialsAccess::GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations) -{ - std::unique_ptr res = FbxStingrayPBSMaterialResolver(material, textureLocations).resolve(); +std::unique_ptr FbxMaterialsAccess::GetMaterialInfo( + FbxSurfaceMaterial* material, + const std::map& textureLocations) { + std::unique_ptr res = + FbxStingrayPBSMaterialResolver(material, textureLocations).resolve(); + if (res == nullptr) { + res = Fbx3dsMaxPhysicalMaterialResolver(material, textureLocations).resolve(); if (res == nullptr) { - res = Fbx3dsMaxPhysicalMaterialResolver(material, textureLocations).resolve(); - if (res == nullptr) { - res = FbxTraditionalMaterialResolver(material, textureLocations).resolve(); - } + res = FbxTraditionalMaterialResolver(material, textureLocations).resolve(); } - return res; + } + return res; } - diff --git a/src/fbx/materials/FbxMaterials.hpp b/src/fbx/materials/FbxMaterials.hpp index 9bd3403..808d6a1 100644 --- a/src/fbx/materials/FbxMaterials.hpp +++ b/src/fbx/materials/FbxMaterials.hpp @@ -15,46 +15,43 @@ #include "FBX2glTF.h" class FbxMaterialInfo { -public: - FbxMaterialInfo(const FbxString &name, const FbxString &shadingModel) - : name(name) - , shadingModel(shadingModel) - {} + public: + FbxMaterialInfo(const FbxString& name, const FbxString& shadingModel) + : name(name), shadingModel(shadingModel) {} - const FbxString name; - const FbxString shadingModel; + const FbxString name; + const FbxString shadingModel; }; template -class FbxMaterialResolver -{ -public: - FbxMaterialResolver( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations) - : fbxMaterial(fbxMaterial) - , textureLocations(textureLocations) - {} - virtual std::unique_ptr resolve() const = 0; +class FbxMaterialResolver { + public: + FbxMaterialResolver( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations) + : fbxMaterial(fbxMaterial), textureLocations(textureLocations) {} + virtual std::unique_ptr resolve() const = 0; -protected: - const FbxSurfaceMaterial *fbxMaterial; - const std::map textureLocations; + protected: + const FbxSurfaceMaterial* fbxMaterial; + const std::map textureLocations; }; -class FbxMaterialsAccess -{ -public: - FbxMaterialsAccess(const FbxMesh *pMesh, const std::map &textureLocations); +class FbxMaterialsAccess { + public: + FbxMaterialsAccess( + const FbxMesh* pMesh, + const std::map& textureLocations); - const std::shared_ptr GetMaterial(const int polygonIndex) const; + const std::shared_ptr GetMaterial(const int polygonIndex) const; - std::unique_ptr - GetMaterialInfo(FbxSurfaceMaterial *material, const std::map &textureLocations); + std::unique_ptr GetMaterialInfo( + FbxSurfaceMaterial* material, + const std::map& textureLocations); -private: - FbxGeometryElement::EMappingMode mappingMode; - std::vector> summaries {}; - const FbxMesh *mesh; - const FbxLayerElementArrayTemplate *indices; + private: + FbxGeometryElement::EMappingMode mappingMode; + std::vector> summaries{}; + const FbxMesh* mesh; + const FbxLayerElementArrayTemplate* indices; }; diff --git a/src/fbx/materials/RoughnessMetallicMaterials.hpp b/src/fbx/materials/RoughnessMetallicMaterials.hpp index 2fe95d9..b20a306 100644 --- a/src/fbx/materials/RoughnessMetallicMaterials.hpp +++ b/src/fbx/materials/RoughnessMetallicMaterials.hpp @@ -14,68 +14,64 @@ #include "FbxMaterials.hpp" struct FbxRoughMetMaterialInfo : FbxMaterialInfo { - static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness"; + static constexpr const char* FBX_SHADER_METROUGH = "MetallicRoughness"; - static std::unique_ptr From( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations); + static std::unique_ptr From( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations); - FbxRoughMetMaterialInfo( - const FbxString &name, - const FbxString &shadingModel, - FbxDouble4 baseColor, - FbxDouble metallic, - FbxDouble roughness - ) - : FbxMaterialInfo(name, shadingModel) - , baseColor(baseColor) - , metallic(metallic) - , roughness(roughness) - {} + FbxRoughMetMaterialInfo( + const FbxString& name, + const FbxString& shadingModel, + FbxDouble4 baseColor, + FbxDouble metallic, + FbxDouble roughness) + : FbxMaterialInfo(name, shadingModel), + baseColor(baseColor), + metallic(metallic), + roughness(roughness) {} - const FbxVector4 baseColor; - const FbxDouble metallic; - const FbxDouble roughness; + const FbxVector4 baseColor; + const FbxDouble metallic; + const FbxDouble roughness; - FbxDouble baseWeight = 1; - FbxVector4 emissive = FbxVector4(0, 0, 0, 1); - FbxDouble emissiveIntensity = 1; + FbxDouble baseWeight = 1; + FbxVector4 emissive = FbxVector4(0, 0, 0, 1); + FbxDouble emissiveIntensity = 1; - const FbxFileTexture *texNormal = nullptr; - const FbxFileTexture *texBaseColor = nullptr; - const FbxFileTexture *texBaseWeight = nullptr; - const FbxFileTexture *texMetallic = nullptr; - const FbxFileTexture *texRoughness = nullptr; - const FbxFileTexture *texEmissive = nullptr; - const FbxFileTexture *texEmissiveWeight = nullptr; - const FbxFileTexture *texAmbientOcclusion = nullptr; + const FbxFileTexture* texNormal = nullptr; + const FbxFileTexture* texBaseColor = nullptr; + const FbxFileTexture* texBaseWeight = nullptr; + const FbxFileTexture* texMetallic = nullptr; + const FbxFileTexture* texRoughness = nullptr; + const FbxFileTexture* texEmissive = nullptr; + const FbxFileTexture* texEmissiveWeight = nullptr; + const FbxFileTexture* texAmbientOcclusion = nullptr; }; class FbxStingrayPBSMaterialResolver : FbxMaterialResolver { -public: - FbxStingrayPBSMaterialResolver( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations) - : FbxMaterialResolver(fbxMaterial, textureLocations) - {} + public: + FbxStingrayPBSMaterialResolver( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations) + : FbxMaterialResolver(fbxMaterial, textureLocations) {} - virtual std::unique_ptr resolve() const; + virtual std::unique_ptr resolve() const; }; class Fbx3dsMaxPhysicalMaterialResolver : FbxMaterialResolver { -public: - Fbx3dsMaxPhysicalMaterialResolver( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations) - : FbxMaterialResolver(fbxMaterial, textureLocations) - {} + public: + Fbx3dsMaxPhysicalMaterialResolver( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations) + : FbxMaterialResolver(fbxMaterial, textureLocations) {} - virtual std::unique_ptr resolve() const; + virtual std::unique_ptr resolve() const; -private: - template - T getValue(const FbxProperty &props, std::string propName, const T& default) const { - const FbxProperty prop = props.FindHierarchical(propName.c_str()); - return prop.IsValid() ? prop.Get() : default; - } + private: + template + T getValue(const FbxProperty& props, std::string propName, const T& default) const { + const FbxProperty prop = props.FindHierarchical(propName.c_str()); + return prop.IsValid() ? prop.Get() : default; + } }; diff --git a/src/fbx/materials/StingrayPBSMaterial.cpp b/src/fbx/materials/StingrayPBSMaterial.cpp index 5d757ad..05dc5e1 100644 --- a/src/fbx/materials/StingrayPBSMaterial.cpp +++ b/src/fbx/materials/StingrayPBSMaterial.cpp @@ -10,62 +10,63 @@ #include "RoughnessMetallicMaterials.hpp" std::unique_ptr FbxStingrayPBSMaterialResolver::resolve() const { - const FbxProperty mayaProp = fbxMaterial->FindProperty("Maya"); - if (mayaProp.GetPropertyDataType() != FbxCompoundDT) { - return nullptr; - } - if (!fbxMaterial->ShadingModel.Get().IsEmpty()) { - ::fmt::printf("Warning: Material %s has surprising shading model: %s\n", - fbxMaterial->GetName(), fbxMaterial->ShadingModel.Get()); - } + const FbxProperty mayaProp = fbxMaterial->FindProperty("Maya"); + if (mayaProp.GetPropertyDataType() != FbxCompoundDT) { + return nullptr; + } + if (!fbxMaterial->ShadingModel.Get().IsEmpty()) { + ::fmt::printf( + "Warning: Material %s has surprising shading model: %s\n", + fbxMaterial->GetName(), + fbxMaterial->ShadingModel.Get()); + } - auto getTex = [&](std::string propName) { - const FbxFileTexture *ptr = nullptr; + auto getTex = [&](std::string propName) { + const FbxFileTexture* ptr = nullptr; - const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str()); - if (useProp.IsValid() && useProp.Get()) { - const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str()); - if (texProp.IsValid()) { - ptr = texProp.GetSrcObject(); - if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { - ptr = nullptr; - } - } - } else if (verboseOutput && useProp.IsValid()) { - fmt::printf("Note: Property '%s' of Stingray PBS material '%s' exists, but is flagged as 'do not use'.\n", - propName, fbxMaterial->GetName()); + const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str()); + if (useProp.IsValid() && useProp.Get()) { + const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str()); + if (texProp.IsValid()) { + ptr = texProp.GetSrcObject(); + if (ptr != nullptr && textureLocations.find(ptr) == textureLocations.end()) { + ptr = nullptr; } - return ptr; - }; + } + } else if (verboseOutput && useProp.IsValid()) { + fmt::printf( + "Note: Property '%s' of Stingray PBS material '%s' exists, but is flagged as 'do not use'.\n", + propName, + fbxMaterial->GetName()); + } + return ptr; + }; - auto getVec = [&](std::string propName) -> FbxDouble3 { - const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); - return vecProp.IsValid() ? vecProp.Get() : FbxDouble3(1, 1, 1); - }; + auto getVec = [&](std::string propName) -> FbxDouble3 { + const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); + return vecProp.IsValid() ? vecProp.Get() : FbxDouble3(1, 1, 1); + }; - auto getVal = [&](std::string propName) -> FbxDouble { - const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str()); - return vecProp.IsValid() ? vecProp.Get() : 0; - }; + auto getVal = [&](std::string propName) -> FbxDouble { + const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str()); + return vecProp.IsValid() ? vecProp.Get() : 0; + }; - FbxDouble3 baseColor = getVec("base_color"); - std::unique_ptr res( - new FbxRoughMetMaterialInfo( - fbxMaterial->GetName(), - FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, - FbxDouble4(baseColor[0], baseColor[1], baseColor[2], 1), - getVal("metallic"), - getVal("roughness") - ) - ); - res->texNormal = getTex("normal"); - res->texBaseColor = getTex("color"); - res->texAmbientOcclusion = getTex("ao"); - res->texEmissive = getTex("emissive"); - res->emissive = getVec("emissive"); - res->emissiveIntensity = getVal("emissive_intensity"); - res->texMetallic = getTex("metallic"); - res->texRoughness = getTex("roughness"); + FbxDouble3 baseColor = getVec("base_color"); + std::unique_ptr res(new FbxRoughMetMaterialInfo( + fbxMaterial->GetName(), + FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, + FbxDouble4(baseColor[0], baseColor[1], baseColor[2], 1), + getVal("metallic"), + getVal("roughness"))); + res->texNormal = getTex("normal"); + res->texBaseColor = getTex("color"); + res->texAmbientOcclusion = getTex("ao"); + res->texEmissive = getTex("emissive"); + res->emissive = getVec("emissive"); + res->emissiveIntensity = getVal("emissive_intensity"); + res->texMetallic = getTex("metallic"); + res->texRoughness = getTex("roughness"); - return res; + return res; }; diff --git a/src/fbx/materials/TraditionalMaterials.cpp b/src/fbx/materials/TraditionalMaterials.cpp index 23fe89f..6b0bc17 100644 --- a/src/fbx/materials/TraditionalMaterials.cpp +++ b/src/fbx/materials/TraditionalMaterials.cpp @@ -9,115 +9,127 @@ #include "TraditionalMaterials.hpp" -std::unique_ptr FbxTraditionalMaterialResolver::resolve() const -{ - auto getSurfaceScalar = [&](const char *propName) -> std::tuple { - const FbxProperty prop = fbxMaterial->FindProperty(propName); +std::unique_ptr FbxTraditionalMaterialResolver::resolve() const { + auto getSurfaceScalar = [&](const char* propName) -> std::tuple { + const FbxProperty prop = fbxMaterial->FindProperty(propName); - FbxDouble val(0); - FbxFileTexture *tex = prop.GetSrcObject(); - if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { - tex = nullptr; - } - if (tex == nullptr && prop.IsValid()) { - val = prop.Get(); - } - return std::make_tuple(val, tex); - }; + FbxDouble val(0); + FbxFileTexture* tex = prop.GetSrcObject(); + if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { + tex = nullptr; + } + if (tex == nullptr && prop.IsValid()) { + val = prop.Get(); + } + return std::make_tuple(val, tex); + }; - auto getSurfaceVector = [&](const char *propName) -> std::tuple { - const FbxProperty prop = fbxMaterial->FindProperty(propName); + auto getSurfaceVector = [&](const char* propName) -> std::tuple { + const FbxProperty prop = fbxMaterial->FindProperty(propName); - FbxDouble3 val(1, 1, 1); - FbxFileTexture *tex = prop.GetSrcObject(); - if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { - tex = nullptr; - } - if (tex == nullptr && prop.IsValid()) { - val = prop.Get(); - } - return std::make_tuple(val, tex); - }; + FbxDouble3 val(1, 1, 1); + FbxFileTexture* tex = prop.GetSrcObject(); + if (tex != nullptr && textureLocations.find(tex) == textureLocations.end()) { + tex = nullptr; + } + if (tex == nullptr && prop.IsValid()) { + val = prop.Get(); + } + return std::make_tuple(val, tex); + }; - auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple { - const FbxProperty colProp = fbxMaterial->FindProperty(colName); - const FbxProperty facProp = fbxMaterial->FindProperty(facName); + auto getSurfaceValues = + [&](const char* colName, + const char* facName) -> std::tuple { + const FbxProperty colProp = fbxMaterial->FindProperty(colName); + const FbxProperty facProp = fbxMaterial->FindProperty(facName); - FbxDouble3 colorVal(1, 1, 1); - FbxDouble factorVal(1); + FbxDouble3 colorVal(1, 1, 1); + FbxDouble factorVal(1); - FbxFileTexture *colTex = colProp.GetSrcObject(); - if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) { - colTex = nullptr; - } - if (colTex == nullptr && colProp.IsValid()) { - colorVal = colProp.Get(); - } - FbxFileTexture *facTex = facProp.GetSrcObject(); - if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) { - facTex = nullptr; - } - if (facTex == nullptr && facProp.IsValid()) { - factorVal = facProp.Get(); - } + FbxFileTexture* colTex = colProp.GetSrcObject(); + if (colTex != nullptr && textureLocations.find(colTex) == textureLocations.end()) { + colTex = nullptr; + } + if (colTex == nullptr && colProp.IsValid()) { + colorVal = colProp.Get(); + } + FbxFileTexture* facTex = facProp.GetSrcObject(); + if (facTex != nullptr && textureLocations.find(facTex) == textureLocations.end()) { + facTex = nullptr; + } + if (facTex == nullptr && facProp.IsValid()) { + factorVal = facProp.Get(); + } - auto val = FbxVector4( - colorVal[0] * factorVal, - colorVal[1] * factorVal, - colorVal[2] * factorVal, - factorVal); - return std::make_tuple(val, colTex, facTex); - }; + auto val = FbxVector4( + colorVal[0] * factorVal, colorVal[1] * factorVal, colorVal[2] * factorVal, factorVal); + return std::make_tuple(val, colTex, facTex); + }; - std::string name = fbxMaterial->GetName(); - std::unique_ptr res(new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get())); + std::string name = fbxMaterial->GetName(); + std::unique_ptr res( + new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get())); - // four properties are on the same structure and follow the same rules - auto handleBasicProperty = [&](const char *colName, const char *facName) -> std::tuple{ - FbxFileTexture *colTex, *facTex; - FbxVector4 vec; - - std::tie(vec, colTex, facTex) = getSurfaceValues(colName, facName); - if (colTex) { - if (facTex) { - fmt::printf("Warning: Mat [%s]: Can't handle both %s and %s textures; discarding %s.\n", name, colName, facName, facName); - } - return std::make_tuple(vec, colTex); - } - return std::make_tuple(vec, facTex); - }; - - std::tie(res->colAmbient, res->texAmbient) = - handleBasicProperty(FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor); - std::tie(res->colSpecular, res->texSpecular) = - handleBasicProperty(FbxSurfaceMaterial::sSpecular, FbxSurfaceMaterial::sSpecularFactor); - std::tie(res->colDiffuse, res->texDiffuse) = - handleBasicProperty(FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor); - std::tie(res->colEmissive, res->texEmissive) = - handleBasicProperty(FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor); - - // the normal map can only ever be a map, ignore everything else - tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap); - - // shininess can be a map or a factor; afaict the map is always 'ShininessExponent' and the - // value is always found in 'Shininess' but only sometimes in 'ShininessExponent'. - tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent"); - tie(res->shininess, std::ignore) = getSurfaceScalar("Shininess"); - - // for transparency we just want a constant vector value; - FbxVector4 transparency; - // extract any existing textures only so we can warn that we're throwing them away + // four properties are on the same structure and follow the same rules + auto handleBasicProperty = [&](const char* colName, + const char* facName) -> std::tuple { FbxFileTexture *colTex, *facTex; - std::tie(transparency, colTex, facTex) = - getSurfaceValues(FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor); - if (colTex) { - fmt::printf("Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", name, FbxSurfaceMaterial::sTransparentColor); - } - if (facTex) { - fmt::printf("Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", name, FbxSurfaceMaterial::sTransparencyFactor); - } - // FBX color is RGB, so we calculate the A channel as the average of the FBX transparency color vector - res->colDiffuse[3] = 1.0 - (transparency[0] + transparency[1] + transparency[2])/3.0; + FbxVector4 vec; - return res; + std::tie(vec, colTex, facTex) = getSurfaceValues(colName, facName); + if (colTex) { + if (facTex) { + fmt::printf( + "Warning: Mat [%s]: Can't handle both %s and %s textures; discarding %s.\n", + name, + colName, + facName, + facName); + } + return std::make_tuple(vec, colTex); + } + return std::make_tuple(vec, facTex); + }; + + std::tie(res->colAmbient, res->texAmbient) = + handleBasicProperty(FbxSurfaceMaterial::sAmbient, FbxSurfaceMaterial::sAmbientFactor); + std::tie(res->colSpecular, res->texSpecular) = + handleBasicProperty(FbxSurfaceMaterial::sSpecular, FbxSurfaceMaterial::sSpecularFactor); + std::tie(res->colDiffuse, res->texDiffuse) = + handleBasicProperty(FbxSurfaceMaterial::sDiffuse, FbxSurfaceMaterial::sDiffuseFactor); + std::tie(res->colEmissive, res->texEmissive) = + handleBasicProperty(FbxSurfaceMaterial::sEmissive, FbxSurfaceMaterial::sEmissiveFactor); + + // the normal map can only ever be a map, ignore everything else + tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap); + + // shininess can be a map or a factor; afaict the map is always 'ShininessExponent' and the + // value is always found in 'Shininess' but only sometimes in 'ShininessExponent'. + tie(std::ignore, res->texShininess) = getSurfaceScalar("ShininessExponent"); + tie(res->shininess, std::ignore) = getSurfaceScalar("Shininess"); + + // for transparency we just want a constant vector value; + FbxVector4 transparency; + // extract any existing textures only so we can warn that we're throwing them away + FbxFileTexture *colTex, *facTex; + std::tie(transparency, colTex, facTex) = getSurfaceValues( + FbxSurfaceMaterial::sTransparentColor, FbxSurfaceMaterial::sTransparencyFactor); + if (colTex) { + fmt::printf( + "Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", + name, + FbxSurfaceMaterial::sTransparentColor); + } + if (facTex) { + fmt::printf( + "Warning: Mat [%s]: Can't handle texture for %s; discarding.\n", + name, + FbxSurfaceMaterial::sTransparencyFactor); + } + // FBX color is RGB, so we calculate the A channel as the average of the FBX transparency color + // vector + res->colDiffuse[3] = 1.0 - (transparency[0] + transparency[1] + transparency[2]) / 3.0; + + return res; } diff --git a/src/fbx/materials/TraditionalMaterials.hpp b/src/fbx/materials/TraditionalMaterials.hpp index 12b08f9..c0fa51d 100644 --- a/src/fbx/materials/TraditionalMaterials.hpp +++ b/src/fbx/materials/TraditionalMaterials.hpp @@ -10,34 +10,32 @@ #include "FbxMaterials.hpp" struct FbxTraditionalMaterialInfo : FbxMaterialInfo { - static constexpr const char *FBX_SHADER_LAMBERT = "Lambert"; - static constexpr const char *FBX_SHADER_BLINN = "Blinn"; - static constexpr const char *FBX_SHADER_PHONG = "Phong"; + static constexpr const char* FBX_SHADER_LAMBERT = "Lambert"; + static constexpr const char* FBX_SHADER_BLINN = "Blinn"; + static constexpr const char* FBX_SHADER_PHONG = "Phong"; - FbxTraditionalMaterialInfo(const FbxString &name, const FbxString &shadingModel) - : FbxMaterialInfo(name, shadingModel) - {} + FbxTraditionalMaterialInfo(const FbxString& name, const FbxString& shadingModel) + : FbxMaterialInfo(name, shadingModel) {} - FbxFileTexture *texAmbient {}; - FbxVector4 colAmbient {}; - FbxFileTexture *texSpecular {}; - FbxVector4 colSpecular {}; - FbxFileTexture *texDiffuse {}; - FbxVector4 colDiffuse {}; - FbxFileTexture *texEmissive {}; - FbxVector4 colEmissive {}; - FbxFileTexture *texNormal {}; - FbxFileTexture *texShininess {}; - FbxDouble shininess {}; + FbxFileTexture* texAmbient{}; + FbxVector4 colAmbient{}; + FbxFileTexture* texSpecular{}; + FbxVector4 colSpecular{}; + FbxFileTexture* texDiffuse{}; + FbxVector4 colDiffuse{}; + FbxFileTexture* texEmissive{}; + FbxVector4 colEmissive{}; + FbxFileTexture* texNormal{}; + FbxFileTexture* texShininess{}; + FbxDouble shininess{}; }; class FbxTraditionalMaterialResolver : FbxMaterialResolver { -public: - FbxTraditionalMaterialResolver( - FbxSurfaceMaterial *fbxMaterial, - const std::map &textureLocations) - : FbxMaterialResolver(fbxMaterial, textureLocations) - {} + public: + FbxTraditionalMaterialResolver( + FbxSurfaceMaterial* fbxMaterial, + const std::map& textureLocations) + : FbxMaterialResolver(fbxMaterial, textureLocations) {} - virtual std::unique_ptr resolve() const; + virtual std::unique_ptr resolve() const; }; diff --git a/src/gltf/GltfModel.cpp b/src/gltf/GltfModel.cpp index 780d047..c3c6524 100644 --- a/src/gltf/GltfModel.cpp +++ b/src/gltf/GltfModel.cpp @@ -1,85 +1,86 @@ /** -* Copyright (c) 2014-present, Facebook, Inc. -* All rights reserved. -* -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -*/ + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #include "GltfModel.hpp" -std::shared_ptr GltfModel::GetAlignedBufferView(BufferData &buffer, const BufferViewData::GL_ArrayType target) -{ - unsigned long bufferSize = this->binary->size(); - if ((bufferSize % 4) > 0) { - bufferSize += (4 - (bufferSize % 4)); - this->binary->resize(bufferSize); - } - return this->bufferViews.hold(new BufferViewData(buffer, bufferSize, target)); +std::shared_ptr GltfModel::GetAlignedBufferView( + BufferData& buffer, + const BufferViewData::GL_ArrayType target) { + unsigned long bufferSize = this->binary->size(); + if ((bufferSize % 4) > 0) { + bufferSize += (4 - (bufferSize % 4)); + this->binary->resize(bufferSize); + } + return this->bufferViews.hold(new BufferViewData(buffer, bufferSize, target)); } // add a bufferview on the fly and copy data into it -std::shared_ptr GltfModel::AddRawBufferView(BufferData &buffer, const char *source, uint32_t bytes) -{ - auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); - bufferView->byteLength = bytes; +std::shared_ptr +GltfModel::AddRawBufferView(BufferData& buffer, const char* source, uint32_t bytes) { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); + bufferView->byteLength = bytes; - // make space for the new bytes (possibly moving the underlying data) - unsigned long bufferSize = this->binary->size(); - this->binary->resize(bufferSize + bytes); + // make space for the new bytes (possibly moving the underlying data) + unsigned long bufferSize = this->binary->size(); + this->binary->resize(bufferSize + bytes); - // and copy them into place - memcpy(&(*this->binary)[bufferSize], source, bytes); - return bufferView; + // and copy them into place + memcpy(&(*this->binary)[bufferSize], source, bytes); + return bufferView; } -std::shared_ptr GltfModel::AddBufferViewForFile(BufferData &buffer, const std::string &filename) -{ - // see if we've already created a BufferViewData for this precise file - auto iter = filenameToBufferView.find(filename); - if (iter != filenameToBufferView.end()) { - return iter->second; - } +std::shared_ptr GltfModel::AddBufferViewForFile( + BufferData& buffer, + const std::string& filename) { + // see if we've already created a BufferViewData for this precise file + auto iter = filenameToBufferView.find(filename); + if (iter != filenameToBufferView.end()) { + return iter->second; + } - std::shared_ptr result; - std::ifstream file(filename, std::ios::binary | std::ios::ate); - if (file) { - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); + std::shared_ptr result; + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (file) { + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); - std::vector fileBuffer(size); - if (file.read(fileBuffer.data(), size)) { - result = AddRawBufferView(buffer, fileBuffer.data(), size); - } else { - fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename); - } + std::vector fileBuffer(size); + if (file.read(fileBuffer.data(), size)) { + result = AddRawBufferView(buffer, fileBuffer.data(), size); } else { - fmt::printf("Warning: Couldn't open file %s, skipping file.\n", filename); + fmt::printf("Warning: Couldn't read %lu bytes from %s, skipping file.\n", size, filename); } - // note that we persist here not only success, but also failure, as nullptr - filenameToBufferView[filename] = result; - return result; + } else { + fmt::printf("Warning: Couldn't open file %s, skipping file.\n", filename); + } + // note that we persist here not only success, but also failure, as nullptr + filenameToBufferView[filename] = result; + return result; } -void GltfModel::serializeHolders(json &glTFJson) -{ - serializeHolder(glTFJson, "buffers", buffers); - serializeHolder(glTFJson, "bufferViews", bufferViews); - serializeHolder(glTFJson, "scenes", scenes); - serializeHolder(glTFJson, "accessors", accessors); - serializeHolder(glTFJson, "images", images); - serializeHolder(glTFJson, "samplers", samplers); - serializeHolder(glTFJson, "textures", textures); - serializeHolder(glTFJson, "materials", materials); - serializeHolder(glTFJson, "meshes", meshes); - serializeHolder(glTFJson, "skins", skins); - serializeHolder(glTFJson, "animations", animations); - serializeHolder(glTFJson, "cameras", cameras); - serializeHolder(glTFJson, "nodes", nodes); - if (!lights.ptrs.empty()) { - json lightsJson = json::object(); - serializeHolder(lightsJson, "lights", lights); - glTFJson["extensions"][KHR_LIGHTS_PUNCTUAL] = lightsJson; - } +void GltfModel::serializeHolders(json& glTFJson) { + serializeHolder(glTFJson, "buffers", buffers); + serializeHolder(glTFJson, "bufferViews", bufferViews); + serializeHolder(glTFJson, "scenes", scenes); + serializeHolder(glTFJson, "accessors", accessors); + serializeHolder(glTFJson, "images", images); + serializeHolder(glTFJson, "samplers", samplers); + serializeHolder(glTFJson, "textures", textures); + serializeHolder(glTFJson, "materials", materials); + serializeHolder(glTFJson, "meshes", meshes); + serializeHolder(glTFJson, "skins", skins); + serializeHolder(glTFJson, "animations", animations); + serializeHolder(glTFJson, "cameras", cameras); + serializeHolder(glTFJson, "nodes", nodes); + if (!lights.ptrs.empty()) { + json lightsJson = json::object(); + serializeHolder(lightsJson, "lights", lights); + glTFJson["extensions"][KHR_LIGHTS_PUNCTUAL] = lightsJson; + } } diff --git a/src/gltf/GltfModel.hpp b/src/gltf/GltfModel.hpp index ab4d384..f82b09f 100644 --- a/src/gltf/GltfModel.hpp +++ b/src/gltf/GltfModel.hpp @@ -1,11 +1,11 @@ /** -* Copyright (c) 2014-present, Facebook, Inc. -* All rights reserved. -* -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -*/ + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #pragma once @@ -30,139 +30,143 @@ #include "gltf/properties/TextureData.hpp" /** -* glTF 2.0 is based on the idea that data structs within a file are referenced by index; an accessor will -* point to the n:th buffer view, and so on. The Holder class takes a freshly instantiated class, and then -* creates, stored, and returns a shared_ptr for it. -* -* The idea is that every glTF resource in the file will live as long as the Holder does, and the Holders -* are all kept in the GLTFData struct. Clients may certainly cnhoose to perpetuate the full shared_ptr -* reference counting type, but generally speaking we pass around simple T& and T* types because the GLTFData -* struct will, by design, outlive all other activity that takes place during in a single conversion run. -*/ -template -class Holder -{ -public: - std::shared_ptr hold(T *ptr) - { - ptr->ix = ptrs.size(); - ptrs.emplace_back(ptr); - return ptrs.back(); - } - std::vector> ptrs; + * glTF 2.0 is based on the idea that data structs within a file are referenced by index; an + * accessor will point to the n:th buffer view, and so on. The Holder class takes a freshly + * instantiated class, and then creates, stored, and returns a shared_ptr for it. + * + * The idea is that every glTF resource in the file will live as long as the Holder does, and the + * Holders are all kept in the GLTFData struct. Clients may certainly cnhoose to perpetuate the full + * shared_ptr reference counting type, but generally speaking we pass around simple T& and T* + * types because the GLTFData struct will, by design, outlive all other activity that takes place + * during in a single conversion run. + */ +template +class Holder { + public: + std::shared_ptr hold(T* ptr) { + ptr->ix = ptrs.size(); + ptrs.emplace_back(ptr); + return ptrs.back(); + } + std::vector> ptrs; }; -class GltfModel -{ -public: - explicit GltfModel(const GltfOptions &options) - : binary(new std::vector) - , isGlb(options.outputBinary) - , defaultSampler(nullptr) - , defaultBuffer(buffers.hold(buildDefaultBuffer(options))) - { - defaultSampler = samplers.hold(buildDefaultSampler()); +class GltfModel { + public: + explicit GltfModel(const GltfOptions& options) + : binary(new std::vector), + isGlb(options.outputBinary), + defaultSampler(nullptr), + defaultBuffer(buffers.hold(buildDefaultBuffer(options))) { + defaultSampler = samplers.hold(buildDefaultSampler()); + } + + std::shared_ptr GetAlignedBufferView( + BufferData& buffer, + const BufferViewData::GL_ArrayType target); + std::shared_ptr + AddRawBufferView(BufferData& buffer, const char* source, uint32_t bytes); + std::shared_ptr AddBufferViewForFile( + BufferData& buffer, + const std::string& filename); + + template + std::shared_ptr AddAccessorWithView( + BufferViewData& bufferView, + const GLType& type, + const std::vector& source, + std::string name) { + auto accessor = accessors.hold(new AccessorData(bufferView, type, name)); + accessor->appendAsBinaryArray(source, *binary); + bufferView.byteLength = accessor->byteLength(); + return accessor; + } + + template + std::shared_ptr + AddAccessorAndView(BufferData& buffer, const GLType& type, const std::vector& source) { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); + return AddAccessorWithView(*bufferView, type, source, std::string("")); + } + + template + std::shared_ptr AddAccessorAndView( + BufferData& buffer, + const GLType& type, + const std::vector& source, + std::string name) { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); + return AddAccessorWithView(*bufferView, type, source, name); + } + + template + std::shared_ptr AddAttributeToPrimitive( + BufferData& buffer, + const RawModel& surfaceModel, + PrimitiveData& primitive, + const AttributeDefinition& attrDef) { + // copy attribute data into vector + std::vector attribArr; + surfaceModel.GetAttributeArray(attribArr, attrDef.rawAttributeIx); + + std::shared_ptr accessor; + if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) { + primitive.AddDracoAttrib(attrDef, attribArr); + + accessor = accessors.hold(new AccessorData(attrDef.glType)); + accessor->count = attribArr.size(); + } else { + auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); + accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); } + primitive.AddAttrib(attrDef.gltfName, *accessor); + return accessor; + }; - std::shared_ptr GetAlignedBufferView(BufferData &buffer, const BufferViewData::GL_ArrayType target); - std::shared_ptr AddRawBufferView(BufferData &buffer, const char *source, uint32_t bytes); - std::shared_ptr AddBufferViewForFile(BufferData &buffer, const std::string &filename); - - template - std::shared_ptr AddAccessorWithView( - BufferViewData &bufferView, const GLType &type, const std::vector &source, std::string name) - { - auto accessor = accessors.hold(new AccessorData(bufferView, type, name)); - accessor->appendAsBinaryArray(source, *binary); - bufferView.byteLength = accessor->byteLength(); - return accessor; + template + void serializeHolder(json& glTFJson, std::string key, const Holder holder) { + if (!holder.ptrs.empty()) { + std::vector bits; + for (const auto& ptr : holder.ptrs) { + bits.push_back(ptr->serialize()); + } + glTFJson[key] = bits; } + } - template - std::shared_ptr AddAccessorAndView( - BufferData &buffer, const GLType &type, const std::vector &source) - { - auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); - return AddAccessorWithView(*bufferView, type, source, std::string("")); - } + void serializeHolders(json& glTFJson); - template - std::shared_ptr AddAccessorAndView( - BufferData &buffer, const GLType &type, const std::vector &source, std::string name) - { - auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_NONE); - return AddAccessorWithView(*bufferView, type, source, name); - } + const bool isGlb; - template - std::shared_ptr AddAttributeToPrimitive( - BufferData &buffer, const RawModel &surfaceModel, PrimitiveData &primitive, - const AttributeDefinition &attrDef) - { - // copy attribute data into vector - std::vector attribArr; - surfaceModel.GetAttributeArray(attribArr, attrDef.rawAttributeIx); + // cache BufferViewData instances that've already been created from a given filename + std::map> filenameToBufferView; - std::shared_ptr accessor; - if (attrDef.dracoComponentType != draco::DT_INVALID && primitive.dracoMesh != nullptr) { - primitive.AddDracoAttrib(attrDef, attribArr); + std::shared_ptr> binary; - accessor = accessors.hold(new AccessorData(attrDef.glType)); - accessor->count = attribArr.size(); - } else { - auto bufferView = GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER); - accessor = AddAccessorWithView(*bufferView, attrDef.glType, attribArr, std::string("")); - } - primitive.AddAttrib(attrDef.gltfName, *accessor); - return accessor; - }; + Holder buffers; + Holder bufferViews; + Holder accessors; + Holder images; + Holder samplers; + Holder textures; + Holder materials; + Holder meshes; + Holder skins; + Holder animations; + Holder cameras; + Holder nodes; + Holder scenes; + Holder lights; - template - void serializeHolder(json &glTFJson, std::string key, const Holder holder) - { - if (!holder.ptrs.empty()) { - std::vector bits; - for (const auto &ptr : holder.ptrs) { - bits.push_back(ptr->serialize()); - } - glTFJson[key] = bits; - } - } + std::shared_ptr defaultSampler; + std::shared_ptr defaultBuffer; - void serializeHolders(json &glTFJson); - - const bool isGlb; - - // cache BufferViewData instances that've already been created from a given filename - std::map> filenameToBufferView; - - std::shared_ptr> binary; - - Holder buffers; - Holder bufferViews; - Holder accessors; - Holder images; - Holder samplers; - Holder textures; - Holder materials; - Holder meshes; - Holder skins; - Holder animations; - Holder cameras; - Holder nodes; - Holder scenes; - Holder lights; - - std::shared_ptr defaultSampler; - std::shared_ptr defaultBuffer; - -private: - SamplerData *buildDefaultSampler() { - return new SamplerData(); - } - BufferData *buildDefaultBuffer(const GltfOptions &options) { - return options.outputBinary ? - new BufferData(binary) : - new BufferData(extBufferFilename, binary, options.embedResources); - } + private: + SamplerData* buildDefaultSampler() { + return new SamplerData(); + } + BufferData* buildDefaultBuffer(const GltfOptions& options) { + return options.outputBinary ? new BufferData(binary) + : new BufferData(extBufferFilename, binary, options.embedResources); + } }; diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 55988e9..d14c725 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -9,17 +9,17 @@ #include "Raw2Gltf.hpp" -#include #include -#include +#include #include +#include #include #include -#include "utils/String_Utils.hpp" -#include "utils/Image_Utils.hpp" #include +#include "utils/Image_Utils.hpp" +#include "utils/String_Utils.hpp" #include "raw/RawModel.hpp" @@ -38,8 +38,8 @@ #include "gltf/properties/SkinData.hpp" #include "gltf/properties/TextureData.hpp" -#include "TextureBuilder.hpp" #include "GltfModel.hpp" +#include "TextureBuilder.hpp" typedef uint32_t TriangleIndex; @@ -50,727 +50,810 @@ typedef uint32_t TriangleIndex; * registered under that name. This is safe in the context of this tool, where all such data * classes are guaranteed to stick around for the duration of the process. */ -template -T &require(std::map> map, const std::string &key) -{ - auto iter = map.find(key); - assert(iter != map.end()); - T &result = *iter->second; - return result; +template +T& require(std::map> map, const std::string& key) { + auto iter = map.find(key); + assert(iter != map.end()); + T& result = *iter->second; + return result; } -template -T &require(std::map> map, long key) -{ - auto iter = map.find(key); - assert(iter != map.end()); - T &result = *iter->second; - return result; +template +T& require(std::map> map, long key) { + auto iter = map.find(key); + assert(iter != map.end()); + T& result = *iter->second; + return result; } -static const std::vector getIndexArray(const RawModel &raw) -{ - std::vector result; +static const std::vector getIndexArray(const RawModel& raw) { + std::vector result; - for (int i = 0; i < raw.GetTriangleCount(); i++) { - result.push_back((TriangleIndex) raw.GetTriangle(i).verts[0]); - result.push_back((TriangleIndex) raw.GetTriangle(i).verts[1]); - result.push_back((TriangleIndex) raw.GetTriangle(i).verts[2]); - } - return result; + for (int i = 0; i < raw.GetTriangleCount(); i++) { + result.push_back((TriangleIndex)raw.GetTriangle(i).verts[0]); + result.push_back((TriangleIndex)raw.GetTriangle(i).verts[1]); + result.push_back((TriangleIndex)raw.GetTriangle(i).verts[2]); + } + return result; } // TODO: replace with a proper MaterialHasher class -static const std::string materialHash(const RawMaterial &m) { - return m.name + "_" + std::to_string(m.type); +static const std::string materialHash(const RawMaterial& m) { + return m.name + "_" + std::to_string(m.type); } -ModelData *Raw2Gltf( - std::ofstream &gltfOutStream, - const std::string &outputFolder, - const RawModel &raw, - const GltfOptions &options -) -{ - if (verboseOutput) { - fmt::printf("Building render model...\n"); - for (int i = 0; i < raw.GetMaterialCount(); i++) { - fmt::printf( - "Material %d: %s [shading: %s]\n", i, raw.GetMaterial(i).name.c_str(), - Describe(raw.GetMaterial(i).info->shadingModel)); - } - if (raw.GetVertexCount() > 2 * raw.GetTriangleCount()) { - fmt::printf( - "Warning: High vertex count. Make sure there are no unnecessary vertex attributes. (see -keepAttribute cmd-line option)"); - } +ModelData* Raw2Gltf( + std::ofstream& gltfOutStream, + const std::string& outputFolder, + const RawModel& raw, + const GltfOptions& options) { + if (verboseOutput) { + fmt::printf("Building render model...\n"); + for (int i = 0; i < raw.GetMaterialCount(); i++) { + fmt::printf( + "Material %d: %s [shading: %s]\n", + i, + raw.GetMaterial(i).name.c_str(), + Describe(raw.GetMaterial(i).info->shadingModel)); + } + if (raw.GetVertexCount() > 2 * raw.GetTriangleCount()) { + fmt::printf( + "Warning: High vertex count. Make sure there are no unnecessary vertex attributes. (see -keepAttribute cmd-line option)"); + } + } + + std::vector materialModels; + raw.CreateMaterialModels( + materialModels, + options.useLongIndices == UseLongIndicesOptions::NEVER, + options.keepAttribs, + true); + + if (verboseOutput) { + fmt::printf("%7d vertices\n", raw.GetVertexCount()); + fmt::printf("%7d triangles\n", raw.GetTriangleCount()); + fmt::printf("%7d textures\n", raw.GetTextureCount()); + fmt::printf("%7d nodes\n", raw.GetNodeCount()); + fmt::printf("%7d surfaces\n", (int)materialModels.size()); + fmt::printf("%7d animations\n", raw.GetAnimationCount()); + fmt::printf("%7d cameras\n", raw.GetCameraCount()); + fmt::printf("%7d lights\n", raw.GetLightCount()); + } + + std::unique_ptr gltf(new GltfModel(options)); + + std::map> nodesById; + std::map> materialsByName; + std::map> textureByIndicesKey; + std::map> meshBySurfaceId; + + // for now, we only have one buffer; data->binary points to the same vector as that BufferData + // does. + BufferData& buffer = *gltf->defaultBuffer; + { + // + // nodes + // + + for (int i = 0; i < raw.GetNodeCount(); i++) { + // assumption: RawNode index == NodeData index + const RawNode& node = raw.GetNode(i); + + auto nodeData = gltf->nodes.hold( + new NodeData(node.name, node.translation, node.rotation, node.scale, node.isJoint)); + + if (options.enableUserProperties) { + nodeData->userProperties = node.userProperties; + } + + for (const auto& childId : node.childIds) { + int childIx = raw.GetNodeById(childId); + assert(childIx >= 0); + nodeData->AddChildNode(childIx); + } + + nodesById.insert(std::make_pair(node.id, nodeData)); } - std::vector materialModels; - raw.CreateMaterialModels( - materialModels, - options.useLongIndices == UseLongIndicesOptions::NEVER, - options.keepAttribs, - true); + // + // animations + // - if (verboseOutput) { - fmt::printf("%7d vertices\n", raw.GetVertexCount()); - fmt::printf("%7d triangles\n", raw.GetTriangleCount()); - fmt::printf("%7d textures\n", raw.GetTextureCount()); - fmt::printf("%7d nodes\n", raw.GetNodeCount()); - fmt::printf("%7d surfaces\n", (int) materialModels.size()); - fmt::printf("%7d animations\n", raw.GetAnimationCount()); - fmt::printf("%7d cameras\n", raw.GetCameraCount()); - fmt::printf("%7d lights\n", raw.GetLightCount()); + for (int i = 0; i < raw.GetAnimationCount(); i++) { + const RawAnimation& animation = raw.GetAnimation(i); + + if (animation.channels.size() == 0) { + fmt::printf( + "Warning: animation '%s' has zero channels. Skipping.\n", animation.name.c_str()); + continue; + } + + auto accessor = gltf->AddAccessorAndView(buffer, GLT_FLOAT, animation.times); + accessor->min = {*std::min_element(std::begin(animation.times), std::end(animation.times))}; + accessor->max = {*std::max_element(std::begin(animation.times), std::end(animation.times))}; + + AnimationData& aDat = *gltf->animations.hold(new AnimationData(animation.name, *accessor)); + if (verboseOutput) { + fmt::printf( + "Animation '%s' has %lu channels:\n", + animation.name.c_str(), + animation.channels.size()); + } + + for (size_t channelIx = 0; channelIx < animation.channels.size(); channelIx++) { + const RawChannel& channel = animation.channels[channelIx]; + const RawNode& node = raw.GetNode(channel.nodeIndex); + + if (verboseOutput) { + fmt::printf( + " Channel %lu (%s) has translations/rotations/scales/weights: [%lu, %lu, %lu, %lu]\n", + channelIx, + node.name.c_str(), + channel.translations.size(), + channel.rotations.size(), + channel.scales.size(), + channel.weights.size()); + } + + NodeData& nDat = require(nodesById, node.id); + if (!channel.translations.empty()) { + aDat.AddNodeChannel( + nDat, + *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), + "translation"); + } + if (!channel.rotations.empty()) { + aDat.AddNodeChannel( + nDat, *gltf->AddAccessorAndView(buffer, GLT_QUATF, channel.rotations), "rotation"); + } + if (!channel.scales.empty()) { + aDat.AddNodeChannel( + nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.scales), "scale"); + } + if (!channel.weights.empty()) { + aDat.AddNodeChannel( + nDat, + *gltf->AddAccessorAndView(buffer, {CT_FLOAT, 1, "SCALAR"}, channel.weights), + "weights"); + } + } } - std::unique_ptr gltf(new GltfModel(options)); + // + // samplers + // - std::map> nodesById; - std::map> materialsByName; - std::map> textureByIndicesKey; - std::map> meshBySurfaceId; + // textures + // - // for now, we only have one buffer; data->binary points to the same vector as that BufferData does. - BufferData &buffer = *gltf->defaultBuffer; - { - // - // nodes - // + TextureBuilder textureBuilder(raw, options, outputFolder, *gltf); - for (int i = 0; i < raw.GetNodeCount(); i++) { - // assumption: RawNode index == NodeData index - const RawNode &node = raw.GetNode(i); + // + // materials + // - auto nodeData = gltf->nodes.hold( - new NodeData(node.name, node.translation, node.rotation, node.scale, node.isJoint)); + for (int materialIndex = 0; materialIndex < raw.GetMaterialCount(); materialIndex++) { + const RawMaterial& material = raw.GetMaterial(materialIndex); + const bool isTransparent = material.type == RAW_MATERIAL_TYPE_TRANSPARENT || + material.type == RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT; - if (options.enableUserProperties) { - nodeData->userProperties = node.userProperties; - } + Vec3f emissiveFactor; + float emissiveIntensity; - for (const auto &childId : node.childIds) { - int childIx = raw.GetNodeById(childId); - assert(childIx >= 0); - nodeData->AddChildNode(childIx); - } - - nodesById.insert(std::make_pair(node.id, nodeData)); - } + // acquire the texture of a specific RawTextureUsage as *TextData, or nullptr if none exists + auto simpleTex = [&](RawTextureUsage usage) -> std::shared_ptr { + return (material.textures[usage] >= 0) + ? textureBuilder.simple(material.textures[usage], "simple") + : nullptr; + }; - // - // animations - // + TextureData* normalTexture = simpleTex(RAW_TEXTURE_USAGE_NORMAL).get(); + TextureData* emissiveTexture = simpleTex(RAW_TEXTURE_USAGE_EMISSIVE).get(); + TextureData* occlusionTexture = nullptr; - for (int i = 0; i < raw.GetAnimationCount(); i++) { - const RawAnimation &animation = raw.GetAnimation(i); + std::shared_ptr pbrMetRough; + if (options.usePBRMetRough) { + // albedo is a basic texture, no merging needed + std::shared_ptr baseColorTex, aoMetRoughTex; - if (animation.channels.size() == 0) { - fmt::printf("Warning: animation '%s' has zero channels. Skipping.\n", animation.name.c_str()); - continue; - } + Vec4f diffuseFactor; + float metallic, roughness; + 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(); + // merge metallic into the blue channel and roughness into the green, of a new combinatory + // texture + aoMetRoughTex = textureBuilder.combine( + { + material.textures[RAW_TEXTURE_USAGE_OCCLUSION], + material.textures[RAW_TEXTURE_USAGE_METALLIC], + material.textures[RAW_TEXTURE_USAGE_ROUGHNESS], + }, + "ao_met_rough", + [&](const std::vector pixels) -> TextureBuilder::pixel { + return {{(*pixels[0])[0], (*pixels[2])[0], (*pixels[1])[0], 1}}; + }, + false); - auto accessor = gltf->AddAccessorAndView(buffer, GLT_FLOAT, animation.times); - accessor->min = { *std::min_element(std::begin(animation.times), std::end(animation.times)) }; - accessor->max = { *std::max_element(std::begin(animation.times), std::end(animation.times)) }; + baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO); + diffuseFactor = props->diffuseFactor; + metallic = props->metallic; + roughness = props->roughness; + emissiveFactor = props->emissiveFactor; + emissiveIntensity = props->emissiveIntensity; + // add the occlusion texture only if actual occlusion pixels exist in the aoNetRough + // texture. + if (material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0) { + occlusionTexture = aoMetRoughTex.get(); + } + } else { + /** + * Traditional FBX Material -> PBR Met/Rough glTF. + * + * Diffuse channel is used as base colour. Simple constants for metallic and roughness. + */ + const RawTraditionalMatProps* props = ((RawTraditionalMatProps*)material.info.get()); + diffuseFactor = props->diffuseFactor; - AnimationData &aDat = *gltf->animations.hold(new AnimationData(animation.name, *accessor)); - if (verboseOutput) { - fmt::printf("Animation '%s' has %lu channels:\n", animation.name.c_str(), animation.channels.size()); - } + if (material.info->shadingModel == RAW_SHADING_MODEL_BLINN || + material.info->shadingModel == RAW_SHADING_MODEL_PHONG) { + // blinn/phong hardcoded to 0.4 metallic + metallic = 0.4f; - for (size_t channelIx = 0; channelIx < animation.channels.size(); channelIx++) { - const RawChannel &channel = animation.channels[channelIx]; - const RawNode &node = raw.GetNode(channel.nodeIndex); + // fairly arbitrary conversion equation, with properties: + // shininess 0 -> roughness 1 + // shininess 2 -> roughness ~0.7 + // shininess 6 -> roughness 0.5 + // shininess 16 -> roughness ~0.33 + // as shininess ==> oo, roughness ==> 0 + auto getRoughness = [&](float shininess) { return sqrtf(2.0f / (2.0f + shininess)); }; - if (verboseOutput) { - fmt::printf( - " Channel %lu (%s) has translations/rotations/scales/weights: [%lu, %lu, %lu, %lu]\n", - channelIx, node.name.c_str(), channel.translations.size(), channel.rotations.size(), - channel.scales.size(), channel.weights.size()); - } - - NodeData &nDat = require(nodesById, node.id); - if (!channel.translations.empty()) { - aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), "translation"); - } - if (!channel.rotations.empty()) { - aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_QUATF, channel.rotations), "rotation"); - } - if (!channel.scales.empty()) { - aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.scales), "scale"); - } - if (!channel.weights.empty()) { - aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, {CT_FLOAT, 1, "SCALAR"}, channel.weights), "weights"); - } - } - } - - // - // samplers - // - - // textures - // - - TextureBuilder textureBuilder(raw, options, outputFolder, *gltf); - - // - // materials - // - - for (int materialIndex = 0; materialIndex < raw.GetMaterialCount(); materialIndex++) { - const RawMaterial &material = raw.GetMaterial(materialIndex); - const bool isTransparent = - material.type == RAW_MATERIAL_TYPE_TRANSPARENT || - material.type == RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT; - - Vec3f emissiveFactor; - float emissiveIntensity; - - // acquire the texture of a specific RawTextureUsage as *TextData, or nullptr if none exists - auto simpleTex = [&](RawTextureUsage usage) -> std::shared_ptr { - return (material.textures[usage] >= 0) ? textureBuilder.simple(material.textures[usage], "simple") : nullptr; - }; - - TextureData *normalTexture = simpleTex(RAW_TEXTURE_USAGE_NORMAL).get(); - TextureData *emissiveTexture = simpleTex(RAW_TEXTURE_USAGE_EMISSIVE).get(); - TextureData *occlusionTexture = nullptr; - - std::shared_ptr pbrMetRough; - if (options.usePBRMetRough) { - // albedo is a basic texture, no merging needed - std::shared_ptr baseColorTex, aoMetRoughTex; - - Vec4f diffuseFactor; - float metallic, roughness; - 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(); - // merge metallic into the blue channel and roughness into the green, of a new combinatory texture - aoMetRoughTex = textureBuilder.combine( - { - material.textures[RAW_TEXTURE_USAGE_OCCLUSION], - material.textures[RAW_TEXTURE_USAGE_METALLIC], - material.textures[RAW_TEXTURE_USAGE_ROUGHNESS], - }, - "ao_met_rough", - [&](const std::vector pixels) -> TextureBuilder::pixel { - return { {(*pixels[0])[0], (*pixels[2])[0], (*pixels[1])[0], 1} }; - }, - false); - - baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO); - diffuseFactor = props->diffuseFactor; - metallic = props->metallic; - roughness = props->roughness; - emissiveFactor = props->emissiveFactor; - emissiveIntensity = props->emissiveIntensity; - // add the occlusion texture only if actual occlusion pixels exist in the aoNetRough texture. - if (material.textures[RAW_TEXTURE_USAGE_OCCLUSION] >= 0) { - occlusionTexture = aoMetRoughTex.get(); - } - } else { - /** - * Traditional FBX Material -> PBR Met/Rough glTF. - * - * Diffuse channel is used as base colour. Simple constants for metallic and roughness. - */ - const RawTraditionalMatProps *props = ((RawTraditionalMatProps *) material.info.get()); - diffuseFactor = props->diffuseFactor; - - if (material.info->shadingModel == RAW_SHADING_MODEL_BLINN || - material.info->shadingModel == RAW_SHADING_MODEL_PHONG) - { - // blinn/phong hardcoded to 0.4 metallic - metallic = 0.4f; - - // fairly arbitrary conversion equation, with properties: - // shininess 0 -> roughness 1 - // shininess 2 -> roughness ~0.7 - // shininess 6 -> roughness 0.5 - // shininess 16 -> roughness ~0.33 - // as shininess ==> oo, roughness ==> 0 - auto getRoughness = [&](float shininess) { - return sqrtf(2.0f / (2.0f + shininess)); - }; - - aoMetRoughTex = textureBuilder.combine( - { material.textures[RAW_TEXTURE_USAGE_SHININESS], }, - "ao_met_rough", - [&](const std::vector pixels) -> TextureBuilder::pixel { - // do not multiply with props->shininess; that doesn't work like the other factors. - float shininess = props->shininess * (*pixels[0])[0]; - return { {0, getRoughness(shininess), metallic, 1} }; - }, - false); - - if (aoMetRoughTex != nullptr) { - // if we successfully built a texture, factors are just multiplicative identity - metallic = roughness = 1.0f; - } else { - // no shininess texture, - roughness = getRoughness(props->shininess); - } - - } else { - metallic = 0.2f; - roughness = 0.8f; - } - - baseColorTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE); - - emissiveFactor = props->emissiveFactor; - emissiveIntensity = 1.0f; - } - pbrMetRough.reset(new PBRMetallicRoughness(baseColorTex.get(), aoMetRoughTex.get(), diffuseFactor, metallic, roughness)); - } - - std::shared_ptr khrCmnUnlitMat; - if (options.useKHRMatUnlit) { - normalTexture = nullptr; - - emissiveTexture = nullptr; - emissiveFactor = Vec3f(0.00f, 0.00f, 0.00f); - - Vec4f diffuseFactor; - std::shared_ptr baseColorTex; - - if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) { - RawMetRoughMatProps *props = (RawMetRoughMatProps *) material.info.get(); - diffuseFactor = props->diffuseFactor; - baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO); - } else { - RawTraditionalMatProps *props = ((RawTraditionalMatProps *) material.info.get()); - diffuseFactor = props->diffuseFactor; - baseColorTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE); - } - - pbrMetRough.reset(new PBRMetallicRoughness(baseColorTex.get(), nullptr, diffuseFactor, 0.0f, 1.0f)); - - khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial()); - } - if (!occlusionTexture) { - occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get(); - } - - std::shared_ptr mData = gltf->materials.hold( - new MaterialData( - material.name, isTransparent, material.info->shadingModel, - normalTexture, occlusionTexture, emissiveTexture, - emissiveFactor * emissiveIntensity, khrCmnUnlitMat, pbrMetRough)); - materialsByName[materialHash(material)] = mData; - - if (options.enableUserProperties) { - mData->userProperties = material.userProperties; - } - } - - for (const auto &surfaceModel : materialModels) { - assert(surfaceModel.GetSurfaceCount() == 1); - const RawSurface &rawSurface = surfaceModel.GetSurface(0); - const long surfaceId = rawSurface.id; - - const RawMaterial &rawMaterial = surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex); - const MaterialData &mData = require(materialsByName, materialHash(rawMaterial)); - - MeshData *mesh = nullptr; - auto meshIter = meshBySurfaceId.find(surfaceId); - if (meshIter != meshBySurfaceId.end()) { - mesh = meshIter->second.get(); + aoMetRoughTex = textureBuilder.combine( + { + material.textures[RAW_TEXTURE_USAGE_SHININESS], + }, + "ao_met_rough", + [&](const std::vector pixels) + -> TextureBuilder::pixel { + // do not multiply with props->shininess; that doesn't work like the other + // factors. + float shininess = props->shininess * (*pixels[0])[0]; + return {{0, getRoughness(shininess), metallic, 1}}; + }, + false); + if (aoMetRoughTex != nullptr) { + // if we successfully built a texture, factors are just multiplicative identity + metallic = roughness = 1.0f; } else { - std::vector defaultDeforms; - for (const auto &channel : rawSurface.blendChannels) { - defaultDeforms.push_back(channel.defaultDeform); - } - auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms)); - meshBySurfaceId[surfaceId] = meshPtr; - mesh = meshPtr.get(); + // no shininess texture, + roughness = getRoughness(props->shininess); } - bool useLongIndices = - (options.useLongIndices == UseLongIndicesOptions::ALWAYS) - || (options.useLongIndices == UseLongIndicesOptions::AUTO - && surfaceModel.GetVertexCount() > 65535); + } else { + metallic = 0.2f; + roughness = 0.8f; + } - std::shared_ptr primitive; - if (options.draco.enabled) { - int triangleCount = surfaceModel.GetTriangleCount(); + baseColorTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE); - // initialize Draco mesh with vertex index information - auto dracoMesh(std::make_shared()); - dracoMesh->SetNumFaces(static_cast(triangleCount)); + emissiveFactor = props->emissiveFactor; + emissiveIntensity = 1.0f; + } + pbrMetRough.reset(new PBRMetallicRoughness( + baseColorTex.get(), aoMetRoughTex.get(), diffuseFactor, metallic, roughness)); + } - for (uint32_t ii = 0; ii < triangleCount; ii++) { - draco::Mesh::Face face; - face[0] = surfaceModel.GetTriangle(ii).verts[0]; - face[1] = surfaceModel.GetTriangle(ii).verts[1]; - face[2] = surfaceModel.GetTriangle(ii).verts[2]; - dracoMesh->SetFace(draco::FaceIndex(ii), face); - } + std::shared_ptr khrCmnUnlitMat; + if (options.useKHRMatUnlit) { + normalTexture = nullptr; - AccessorData &indexes = *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT)); - indexes.count = 3 * triangleCount; - primitive.reset(new PrimitiveData(indexes, mData, dracoMesh)); - } else { - const AccessorData &indexes = *gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER), - useLongIndices ? GLT_UINT : GLT_USHORT, getIndexArray(surfaceModel), std::string("")); - primitive.reset(new PrimitiveData(indexes, mData)); - }; + emissiveTexture = nullptr; + emissiveFactor = Vec3f(0.00f, 0.00f, 0.00f); - // - // surface vertices - // - { - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { - const AttributeDefinition ATTR_POSITION("POSITION", &RawVertex::position, - GLT_VEC3F, draco::GeometryAttribute::POSITION, draco::DT_FLOAT32); - auto accessor = gltf->AddAttributeToPrimitive( - buffer, surfaceModel, *primitive, ATTR_POSITION); + Vec4f diffuseFactor; + std::shared_ptr baseColorTex; - accessor->min = toStdVec(rawSurface.bounds.min); - accessor->max = toStdVec(rawSurface.bounds.max); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { - const AttributeDefinition ATTR_NORMAL("NORMAL", &RawVertex::normal, - GLT_VEC3F, draco::GeometryAttribute::NORMAL, draco::DT_FLOAT32); - 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); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { - const AttributeDefinition ATTR_COLOR("COLOR_0", &RawVertex::color, GLT_VEC4F, - draco::GeometryAttribute::COLOR, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_COLOR); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { - const AttributeDefinition ATTR_TEXCOORD_0("TEXCOORD_0", &RawVertex::uv0, - GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { - const AttributeDefinition ATTR_TEXCOORD_1("TEXCOORD_1", &RawVertex::uv1, - GLT_VEC2F, draco::GeometryAttribute::TEX_COORD, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) { - const AttributeDefinition ATTR_JOINTS("JOINTS_0", &RawVertex::jointIndices, - GLT_VEC4I, draco::GeometryAttribute::GENERIC, draco::DT_UINT16); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); - } - if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) { - const AttributeDefinition ATTR_WEIGHTS("WEIGHTS_0", &RawVertex::jointWeights, - GLT_VEC4F, draco::GeometryAttribute::GENERIC, draco::DT_FLOAT32); - gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); - } - - // each channel present in the mesh always ends up a target in the primitive - for (int channelIx = 0; channelIx < rawSurface.blendChannels.size(); channelIx ++) { - const auto &channel = rawSurface.blendChannels[channelIx]; - - // track the bounds of each shape channel - Bounds shapeBounds; - - std::vector positions, normals; - std::vector tangents; - for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj ++) { - auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx]; - shapeBounds.AddPoint(blendVertex.position); - positions.push_back(blendVertex.position); - if (options.useBlendShapeTangents && channel.hasNormals) { - normals.push_back(blendVertex.normal); - } - if (options.useBlendShapeTangents && channel.hasTangents) { - tangents.push_back(blendVertex.tangent); - } - } - std::shared_ptr pAcc = gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), - GLT_VEC3F, positions, channel.name); - pAcc->min = toStdVec(shapeBounds.min); - pAcc->max = toStdVec(shapeBounds.max); - - std::shared_ptr nAcc; - if (!normals.empty()) { - nAcc = gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), - GLT_VEC3F, normals, channel.name); - } - - std::shared_ptr tAcc; - if (!tangents.empty()) { - nAcc = gltf->AddAccessorWithView( - *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), - GLT_VEC4F, tangents, channel.name); - } - - primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get()); - } - } - if (options.draco.enabled) { - // Set up the encoder. - draco::Encoder encoder; - - if (options.draco.compressionLevel != -1) { - int dracoSpeed = 10 - options.draco.compressionLevel; - encoder.SetSpeedOptions(dracoSpeed, dracoSpeed); - } - if (options.draco.quantBitsPosition != -1) { - encoder.SetAttributeQuantization(draco::GeometryAttribute::POSITION, options.draco.quantBitsPosition); - } - if (options.draco.quantBitsTexCoord != -1) { - encoder.SetAttributeQuantization(draco::GeometryAttribute::TEX_COORD, options.draco.quantBitsTexCoord); - } - if (options.draco.quantBitsNormal != -1) { - encoder.SetAttributeQuantization(draco::GeometryAttribute::NORMAL, options.draco.quantBitsNormal); - } - if (options.draco.quantBitsColor != -1) { - encoder.SetAttributeQuantization(draco::GeometryAttribute::COLOR, options.draco.quantBitsColor); - } - if (options.draco.quantBitsGeneric != -1) { - encoder.SetAttributeQuantization(draco::GeometryAttribute::GENERIC, options.draco.quantBitsGeneric); - } - - draco::EncoderBuffer dracoBuffer; - draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer); - assert(status.code() == draco::Status::OK); - - auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), dracoBuffer.size()); - primitive->NoteDracoBuffer(*view); - } - mesh->AddPrimitive(primitive); + if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) { + RawMetRoughMatProps* props = (RawMetRoughMatProps*)material.info.get(); + diffuseFactor = props->diffuseFactor; + baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO); + } else { + RawTraditionalMatProps* props = ((RawTraditionalMatProps*)material.info.get()); + diffuseFactor = props->diffuseFactor; + baseColorTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE); } - // - // Assign meshes to node - // + pbrMetRough.reset( + new PBRMetallicRoughness(baseColorTex.get(), nullptr, diffuseFactor, 0.0f, 1.0f)); - for (int i = 0; i < raw.GetNodeCount(); i++) { - const RawNode &node = raw.GetNode(i); - auto nodeData = gltf->nodes.ptrs[i]; + khrCmnUnlitMat.reset(new KHRCmnUnlitMaterial()); + } + if (!occlusionTexture) { + occlusionTexture = simpleTex(RAW_TEXTURE_USAGE_OCCLUSION).get(); + } - // - // Assign mesh to node - // - if (node.surfaceId > 0) - { - int surfaceIndex = raw.GetSurfaceById(node.surfaceId); - const RawSurface &rawSurface = raw.GetSurface(surfaceIndex); + std::shared_ptr mData = gltf->materials.hold(new MaterialData( + material.name, + isTransparent, + material.info->shadingModel, + normalTexture, + occlusionTexture, + emissiveTexture, + emissiveFactor * emissiveIntensity, + khrCmnUnlitMat, + pbrMetRough)); + materialsByName[materialHash(material)] = mData; - MeshData &meshData = require(meshBySurfaceId, rawSurface.id); - nodeData->SetMesh(meshData.ix); - - // - // surface skin - // - if (!rawSurface.jointIds.empty()) { - if (nodeData->skin == -1) { - // glTF uses column-major matrices - std::vector inverseBindMatrices; - for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) { - inverseBindMatrices.push_back(inverseBindMatrice.Transpose()); - } - - std::vector jointIndexes; - for (const auto &jointId : rawSurface.jointIds) { - jointIndexes.push_back(require(nodesById, jointId).ix); - } - - // Write out inverseBindMatrices - auto accIBM = gltf->AddAccessorAndView(buffer, GLT_MAT4F, inverseBindMatrices); - - auto skeletonRoot = require(nodesById, rawSurface.skeletonRootId); - auto skin = *gltf->skins.hold(new SkinData(jointIndexes, *accIBM, skeletonRoot)); - nodeData->SetSkin(skin.ix); - } - } - } - } - - // - // cameras - // - - for (int i = 0; i < raw.GetCameraCount(); i++) { - const RawCamera &cam = raw.GetCamera(i); - CameraData &camera = *gltf->cameras.hold(new CameraData()); - camera.name = cam.name; - - if (cam.mode == RawCamera::CAMERA_MODE_PERSPECTIVE) { - camera.type = "perspective"; - camera.aspectRatio = cam.perspective.aspectRatio; - camera.yfov = cam.perspective.fovDegreesY * ((float) M_PI / 180.0f); - camera.znear = cam.perspective.nearZ; - camera.zfar = cam.perspective.farZ; - } else { - camera.type = "orthographic"; - camera.xmag = cam.orthographic.magX; - camera.ymag = cam.orthographic.magY; - camera.znear = cam.orthographic.nearZ; - camera.zfar = cam.orthographic.farZ; - } - // Add the camera to the node hierarchy. - - auto iter = nodesById.find(cam.nodeId); - if (iter == nodesById.end()) { - fmt::printf("Warning: Camera node id %lu does not exist.\n", cam.nodeId); - continue; - } - iter->second->SetCamera(camera.ix); - } - - // - // lights - // - std::vector khrPunctualLights; - if (options.useKHRLightsPunctual) { - for (int i = 0; i < raw.GetLightCount(); i ++) { - const RawLight &light = raw.GetLight(i); - LightData::Type type; - switch(light.type) { - case RAW_LIGHT_TYPE_DIRECTIONAL: - type = LightData::Type::Directional; - break; - case RAW_LIGHT_TYPE_POINT: - type = LightData::Type::Point; - break; - case RAW_LIGHT_TYPE_SPOT: - type = LightData::Type::Spot; - break; - } - gltf->lights.hold(new LightData( - light.name, - type, - light.color, - // FBX intensity defaults to 100, so let's call that 1.0; - // but caveat: I find nothing in the documentation to suggest - // what unit the FBX value is meant to be measured in... - light.intensity / 100, - light.innerConeAngle, - light.outerConeAngle)); - } - } - for (int i = 0; i < raw.GetNodeCount(); i++) { - const RawNode &node = raw.GetNode(i); - const auto nodeData = gltf->nodes.ptrs[i]; - - if (node.lightIx >= 0) { - // we lean on the fact that in this simple case, raw and gltf indexing are aligned - nodeData->SetLight(node.lightIx); - } - } + if (options.enableUserProperties) { + mData->userProperties = material.userProperties; + } } - NodeData &rootNode = require(nodesById, raw.GetRootNode()); - const SceneData &rootScene = *gltf->scenes.hold(new SceneData(DEFAULT_SCENE_NAME, rootNode)); + for (const auto& surfaceModel : materialModels) { + assert(surfaceModel.GetSurfaceCount() == 1); + const RawSurface& rawSurface = surfaceModel.GetSurface(0); + const long surfaceId = rawSurface.id; - if (options.outputBinary) { - // note: glTF binary is little-endian - const char glbHeader[] = { - 'g', 'l', 'T', 'F', // magic - 0x02, 0x00, 0x00, 0x00, // version - 0x00, 0x00, 0x00, 0x00, // total length: written in later - }; - gltfOutStream.write(glbHeader, 12); + const RawMaterial& rawMaterial = + surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex); + const MaterialData& mData = require(materialsByName, materialHash(rawMaterial)); - // binary glTF 2.0 has a sub-header for each of the JSON and BIN chunks - const char glb2JsonHeader[] = { - 0x00, 0x00, 0x00, 0x00, // chunk length: written in later - 'J', 'S', 'O', 'N', // chunk type: 0x4E4F534A aka JSON - }; - gltfOutStream.write(glb2JsonHeader, 8); + MeshData* mesh = nullptr; + auto meshIter = meshBySurfaceId.find(surfaceId); + if (meshIter != meshBySurfaceId.end()) { + mesh = meshIter->second.get(); + + } else { + std::vector defaultDeforms; + for (const auto& channel : rawSurface.blendChannels) { + defaultDeforms.push_back(channel.defaultDeform); + } + auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms)); + meshBySurfaceId[surfaceId] = meshPtr; + mesh = meshPtr.get(); + } + + bool useLongIndices = (options.useLongIndices == UseLongIndicesOptions::ALWAYS) || + (options.useLongIndices == UseLongIndicesOptions::AUTO && + surfaceModel.GetVertexCount() > 65535); + + std::shared_ptr primitive; + if (options.draco.enabled) { + int triangleCount = surfaceModel.GetTriangleCount(); + + // initialize Draco mesh with vertex index information + auto dracoMesh(std::make_shared()); + dracoMesh->SetNumFaces(static_cast(triangleCount)); + + for (uint32_t ii = 0; ii < triangleCount; ii++) { + draco::Mesh::Face face; + face[0] = surfaceModel.GetTriangle(ii).verts[0]; + face[1] = surfaceModel.GetTriangle(ii).verts[1]; + face[2] = surfaceModel.GetTriangle(ii).verts[2]; + dracoMesh->SetFace(draco::FaceIndex(ii), face); + } + + AccessorData& indexes = + *gltf->accessors.hold(new AccessorData(useLongIndices ? GLT_UINT : GLT_USHORT)); + indexes.count = 3 * triangleCount; + primitive.reset(new PrimitiveData(indexes, mData, dracoMesh)); + } else { + const AccessorData& indexes = *gltf->AddAccessorWithView( + *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ELEMENT_ARRAY_BUFFER), + useLongIndices ? GLT_UINT : GLT_USHORT, + getIndexArray(surfaceModel), + std::string("")); + primitive.reset(new PrimitiveData(indexes, mData)); + }; + + // + // surface vertices + // + { + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { + const AttributeDefinition ATTR_POSITION( + "POSITION", + &RawVertex::position, + GLT_VEC3F, + draco::GeometryAttribute::POSITION, + draco::DT_FLOAT32); + auto accessor = + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_POSITION); + + accessor->min = toStdVec(rawSurface.bounds.min); + accessor->max = toStdVec(rawSurface.bounds.max); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { + const AttributeDefinition ATTR_NORMAL( + "NORMAL", + &RawVertex::normal, + GLT_VEC3F, + draco::GeometryAttribute::NORMAL, + draco::DT_FLOAT32); + 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); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_COLOR) != 0) { + const AttributeDefinition ATTR_COLOR( + "COLOR_0", + &RawVertex::color, + GLT_VEC4F, + draco::GeometryAttribute::COLOR, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_COLOR); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { + const AttributeDefinition ATTR_TEXCOORD_0( + "TEXCOORD_0", + &RawVertex::uv0, + GLT_VEC2F, + draco::GeometryAttribute::TEX_COORD, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_0); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { + const AttributeDefinition ATTR_TEXCOORD_1( + "TEXCOORD_1", + &RawVertex::uv1, + GLT_VEC2F, + draco::GeometryAttribute::TEX_COORD, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_TEXCOORD_1); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) != 0) { + const AttributeDefinition ATTR_JOINTS( + "JOINTS_0", + &RawVertex::jointIndices, + GLT_VEC4I, + draco::GeometryAttribute::GENERIC, + draco::DT_UINT16); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_JOINTS); + } + if ((surfaceModel.GetVertexAttributes() & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) != 0) { + const AttributeDefinition ATTR_WEIGHTS( + "WEIGHTS_0", + &RawVertex::jointWeights, + GLT_VEC4F, + draco::GeometryAttribute::GENERIC, + draco::DT_FLOAT32); + gltf->AddAttributeToPrimitive(buffer, surfaceModel, *primitive, ATTR_WEIGHTS); + } + + // each channel present in the mesh always ends up a target in the primitive + for (int channelIx = 0; channelIx < rawSurface.blendChannels.size(); channelIx++) { + const auto& channel = rawSurface.blendChannels[channelIx]; + + // track the bounds of each shape channel + Bounds shapeBounds; + + std::vector positions, normals; + std::vector tangents; + for (int jj = 0; jj < surfaceModel.GetVertexCount(); jj++) { + auto blendVertex = surfaceModel.GetVertex(jj).blends[channelIx]; + shapeBounds.AddPoint(blendVertex.position); + positions.push_back(blendVertex.position); + if (options.useBlendShapeTangents && channel.hasNormals) { + normals.push_back(blendVertex.normal); + } + if (options.useBlendShapeTangents && channel.hasTangents) { + tangents.push_back(blendVertex.tangent); + } + } + std::shared_ptr pAcc = gltf->AddAccessorWithView( + *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), + GLT_VEC3F, + positions, + channel.name); + pAcc->min = toStdVec(shapeBounds.min); + pAcc->max = toStdVec(shapeBounds.max); + + std::shared_ptr nAcc; + if (!normals.empty()) { + nAcc = gltf->AddAccessorWithView( + *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), + GLT_VEC3F, + normals, + channel.name); + } + + std::shared_ptr tAcc; + if (!tangents.empty()) { + nAcc = gltf->AddAccessorWithView( + *gltf->GetAlignedBufferView(buffer, BufferViewData::GL_ARRAY_BUFFER), + GLT_VEC4F, + tangents, + channel.name); + } + + primitive->AddTarget(pAcc.get(), nAcc.get(), tAcc.get()); + } + } + if (options.draco.enabled) { + // Set up the encoder. + draco::Encoder encoder; + + if (options.draco.compressionLevel != -1) { + int dracoSpeed = 10 - options.draco.compressionLevel; + encoder.SetSpeedOptions(dracoSpeed, dracoSpeed); + } + if (options.draco.quantBitsPosition != -1) { + encoder.SetAttributeQuantization( + draco::GeometryAttribute::POSITION, options.draco.quantBitsPosition); + } + if (options.draco.quantBitsTexCoord != -1) { + encoder.SetAttributeQuantization( + draco::GeometryAttribute::TEX_COORD, options.draco.quantBitsTexCoord); + } + if (options.draco.quantBitsNormal != -1) { + encoder.SetAttributeQuantization( + draco::GeometryAttribute::NORMAL, options.draco.quantBitsNormal); + } + if (options.draco.quantBitsColor != -1) { + encoder.SetAttributeQuantization( + draco::GeometryAttribute::COLOR, options.draco.quantBitsColor); + } + if (options.draco.quantBitsGeneric != -1) { + encoder.SetAttributeQuantization( + draco::GeometryAttribute::GENERIC, options.draco.quantBitsGeneric); + } + + draco::EncoderBuffer dracoBuffer; + draco::Status status = encoder.EncodeMeshToBuffer(*primitive->dracoMesh, &dracoBuffer); + assert(status.code() == draco::Status::OK); + + auto view = gltf->AddRawBufferView(buffer, dracoBuffer.data(), dracoBuffer.size()); + primitive->NoteDracoBuffer(*view); + } + mesh->AddPrimitive(primitive); } - { - std::vector extensionsUsed, extensionsRequired; - if (options.useKHRMatUnlit) { - extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT); + // + // Assign meshes to node + // + + for (int i = 0; i < raw.GetNodeCount(); i++) { + const RawNode& node = raw.GetNode(i); + auto nodeData = gltf->nodes.ptrs[i]; + + // + // Assign mesh to node + // + if (node.surfaceId > 0) { + int surfaceIndex = raw.GetSurfaceById(node.surfaceId); + const RawSurface& rawSurface = raw.GetSurface(surfaceIndex); + + MeshData& meshData = require(meshBySurfaceId, rawSurface.id); + nodeData->SetMesh(meshData.ix); + + // + // surface skin + // + if (!rawSurface.jointIds.empty()) { + if (nodeData->skin == -1) { + // glTF uses column-major matrices + std::vector inverseBindMatrices; + for (const auto& inverseBindMatrice : rawSurface.inverseBindMatrices) { + inverseBindMatrices.push_back(inverseBindMatrice.Transpose()); + } + + std::vector jointIndexes; + for (const auto& jointId : rawSurface.jointIds) { + jointIndexes.push_back(require(nodesById, jointId).ix); + } + + // Write out inverseBindMatrices + auto accIBM = gltf->AddAccessorAndView(buffer, GLT_MAT4F, inverseBindMatrices); + + auto skeletonRoot = require(nodesById, rawSurface.skeletonRootId); + auto skin = *gltf->skins.hold(new SkinData(jointIndexes, *accIBM, skeletonRoot)); + nodeData->SetSkin(skin.ix); + } } - if (!gltf->lights.ptrs.empty()) { - extensionsUsed.push_back(KHR_LIGHTS_PUNCTUAL); - } - if (options.draco.enabled) { - extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION); - extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION); - } - - json glTFJson { - { "asset", { - { "generator", "FBX2glTF v" + FBX2GLTF_VERSION }, - { "version", "2.0" }}}, - { "scene", rootScene.ix } - }; - if (!extensionsUsed.empty()) { - glTFJson["extensionsUsed"] = extensionsUsed; - } - if (!extensionsRequired.empty()) { - glTFJson["extensionsRequired"] = extensionsRequired; - } - - gltf->serializeHolders(glTFJson); - - gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4); - } - if (options.outputBinary) { - uint32_t jsonLength = (uint32_t) gltfOutStream.tellp() - 20; - // the binary body must begin on a 4-aligned address, so pad json with spaces if necessary - while ((jsonLength % 4) != 0) { - gltfOutStream.put(' '); - jsonLength++; - } - - uint32_t binHeader = (uint32_t) gltfOutStream.tellp(); - // binary glTF 2.0 has a sub-header for each of the JSON and BIN chunks - const char glb2BinaryHeader[] = { - 0x00, 0x00, 0x00, 0x00, // chunk length: written in later - 'B', 'I', 'N', 0x00, // chunk type: 0x004E4942 aka BIN - }; - gltfOutStream.write(glb2BinaryHeader, 8); - - // append binary buffer directly to .glb file - uint32_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(); - - // seek back to sub-header for json chunk - gltfOutStream.seekp(8); - - // write total length, little-endian - gltfOutStream.put((totalLength >> 0) & 0xFF); - gltfOutStream.put((totalLength >> 8) & 0xFF); - gltfOutStream.put((totalLength >> 16) & 0xFF); - gltfOutStream.put((totalLength >> 24) & 0xFF); - - // write JSON length, little-endian - gltfOutStream.put((jsonLength >> 0) & 0xFF); - gltfOutStream.put((jsonLength >> 8) & 0xFF); - gltfOutStream.put((jsonLength >> 16) & 0xFF); - gltfOutStream.put((jsonLength >> 24) & 0xFF); - - // seek back to the gltf 2.0 binary chunk header - gltfOutStream.seekp(binHeader); - - // write total length, little-endian - gltfOutStream.put((binaryLength >> 0) & 0xFF); - gltfOutStream.put((binaryLength >> 8) & 0xFF); - gltfOutStream.put((binaryLength >> 16) & 0xFF); - gltfOutStream.put((binaryLength >> 24) & 0xFF); - - // be tidy and return write pointer to end-of-file - gltfOutStream.seekp(0, std::ios::end); + } } - return new ModelData(gltf->binary); + // + // cameras + // + + for (int i = 0; i < raw.GetCameraCount(); i++) { + const RawCamera& cam = raw.GetCamera(i); + CameraData& camera = *gltf->cameras.hold(new CameraData()); + camera.name = cam.name; + + if (cam.mode == RawCamera::CAMERA_MODE_PERSPECTIVE) { + camera.type = "perspective"; + camera.aspectRatio = cam.perspective.aspectRatio; + camera.yfov = cam.perspective.fovDegreesY * ((float)M_PI / 180.0f); + camera.znear = cam.perspective.nearZ; + camera.zfar = cam.perspective.farZ; + } else { + camera.type = "orthographic"; + camera.xmag = cam.orthographic.magX; + camera.ymag = cam.orthographic.magY; + camera.znear = cam.orthographic.nearZ; + camera.zfar = cam.orthographic.farZ; + } + // Add the camera to the node hierarchy. + + auto iter = nodesById.find(cam.nodeId); + if (iter == nodesById.end()) { + fmt::printf("Warning: Camera node id %lu does not exist.\n", cam.nodeId); + continue; + } + iter->second->SetCamera(camera.ix); + } + + // + // lights + // + std::vector khrPunctualLights; + if (options.useKHRLightsPunctual) { + for (int i = 0; i < raw.GetLightCount(); i++) { + const RawLight& light = raw.GetLight(i); + LightData::Type type; + switch (light.type) { + case RAW_LIGHT_TYPE_DIRECTIONAL: + type = LightData::Type::Directional; + break; + case RAW_LIGHT_TYPE_POINT: + type = LightData::Type::Point; + break; + case RAW_LIGHT_TYPE_SPOT: + type = LightData::Type::Spot; + break; + } + gltf->lights.hold(new LightData( + light.name, + type, + light.color, + // FBX intensity defaults to 100, so let's call that 1.0; + // but caveat: I find nothing in the documentation to suggest + // what unit the FBX value is meant to be measured in... + light.intensity / 100, + light.innerConeAngle, + light.outerConeAngle)); + } + } + for (int i = 0; i < raw.GetNodeCount(); i++) { + const RawNode& node = raw.GetNode(i); + const auto nodeData = gltf->nodes.ptrs[i]; + + if (node.lightIx >= 0) { + // we lean on the fact that in this simple case, raw and gltf indexing are aligned + nodeData->SetLight(node.lightIx); + } + } + } + + NodeData& rootNode = require(nodesById, raw.GetRootNode()); + const SceneData& rootScene = *gltf->scenes.hold(new SceneData(DEFAULT_SCENE_NAME, rootNode)); + + if (options.outputBinary) { + // note: glTF binary is little-endian + const char glbHeader[] = { + 'g', + 'l', + 'T', + 'F', // magic + 0x02, + 0x00, + 0x00, + 0x00, // version + 0x00, + 0x00, + 0x00, + 0x00, // total length: written in later + }; + gltfOutStream.write(glbHeader, 12); + + // binary glTF 2.0 has a sub-header for each of the JSON and BIN chunks + const char glb2JsonHeader[] = { + 0x00, + 0x00, + 0x00, + 0x00, // chunk length: written in later + 'J', + 'S', + 'O', + 'N', // chunk type: 0x4E4F534A aka JSON + }; + gltfOutStream.write(glb2JsonHeader, 8); + } + + { + std::vector extensionsUsed, extensionsRequired; + if (options.useKHRMatUnlit) { + extensionsUsed.push_back(KHR_MATERIALS_CMN_UNLIT); + } + if (!gltf->lights.ptrs.empty()) { + extensionsUsed.push_back(KHR_LIGHTS_PUNCTUAL); + } + if (options.draco.enabled) { + extensionsUsed.push_back(KHR_DRACO_MESH_COMPRESSION); + extensionsRequired.push_back(KHR_DRACO_MESH_COMPRESSION); + } + + json glTFJson{{"asset", {{"generator", "FBX2glTF v" + FBX2GLTF_VERSION}, {"version", "2.0"}}}, + {"scene", rootScene.ix}}; + if (!extensionsUsed.empty()) { + glTFJson["extensionsUsed"] = extensionsUsed; + } + if (!extensionsRequired.empty()) { + glTFJson["extensionsRequired"] = extensionsRequired; + } + + gltf->serializeHolders(glTFJson); + + gltfOutStream << glTFJson.dump(options.outputBinary ? 0 : 4); + } + if (options.outputBinary) { + uint32_t jsonLength = (uint32_t)gltfOutStream.tellp() - 20; + // the binary body must begin on a 4-aligned address, so pad json with spaces if necessary + while ((jsonLength % 4) != 0) { + gltfOutStream.put(' '); + jsonLength++; + } + + uint32_t binHeader = (uint32_t)gltfOutStream.tellp(); + // binary glTF 2.0 has a sub-header for each of the JSON and BIN chunks + const char glb2BinaryHeader[] = { + 0x00, + 0x00, + 0x00, + 0x00, // chunk length: written in later + 'B', + 'I', + 'N', + 0x00, // chunk type: 0x004E4942 aka BIN + }; + gltfOutStream.write(glb2BinaryHeader, 8); + + // append binary buffer directly to .glb file + uint32_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(); + + // seek back to sub-header for json chunk + gltfOutStream.seekp(8); + + // write total length, little-endian + gltfOutStream.put((totalLength >> 0) & 0xFF); + gltfOutStream.put((totalLength >> 8) & 0xFF); + gltfOutStream.put((totalLength >> 16) & 0xFF); + gltfOutStream.put((totalLength >> 24) & 0xFF); + + // write JSON length, little-endian + gltfOutStream.put((jsonLength >> 0) & 0xFF); + gltfOutStream.put((jsonLength >> 8) & 0xFF); + gltfOutStream.put((jsonLength >> 16) & 0xFF); + gltfOutStream.put((jsonLength >> 24) & 0xFF); + + // seek back to the gltf 2.0 binary chunk header + gltfOutStream.seekp(binHeader); + + // write total length, little-endian + gltfOutStream.put((binaryLength >> 0) & 0xFF); + gltfOutStream.put((binaryLength >> 8) & 0xFF); + gltfOutStream.put((binaryLength >> 16) & 0xFF); + gltfOutStream.put((binaryLength >> 24) & 0xFF); + + // be tidy and return write pointer to end-of-file + gltfOutStream.seekp(0, std::ios::end); + } + + return new ModelData(gltf->binary); } diff --git a/src/gltf/Raw2Gltf.hpp b/src/gltf/Raw2Gltf.hpp index 60e738c..0dbdc6e 100644 --- a/src/gltf/Raw2Gltf.hpp +++ b/src/gltf/Raw2Gltf.hpp @@ -19,138 +19,141 @@ #include "FBX2glTF.h" #include "raw/RawModel.hpp" -const std::string KHR_DRACO_MESH_COMPRESSION = "KHR_draco_mesh_compression"; -const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit"; -const std::string KHR_LIGHTS_PUNCTUAL = "KHR_lights_punctual"; +const std::string KHR_DRACO_MESH_COMPRESSION = "KHR_draco_mesh_compression"; +const std::string KHR_MATERIALS_CMN_UNLIT = "KHR_materials_unlit"; +const std::string KHR_LIGHTS_PUNCTUAL = "KHR_lights_punctual"; const std::string extBufferFilename = "buffer.bin"; struct ComponentType { - // OpenGL Datatype enums - enum GL_DataType - { - GL_BYTE = 5120, - GL_UNSIGNED_BYTE, - GL_SHORT, - GL_UNSIGNED_SHORT, - GL_INT, - GL_UNSIGNED_INT, - GL_FLOAT - }; + // OpenGL Datatype enums + enum GL_DataType { + GL_BYTE = 5120, + GL_UNSIGNED_BYTE, + GL_SHORT, + GL_UNSIGNED_SHORT, + GL_INT, + GL_UNSIGNED_INT, + GL_FLOAT + }; - const GL_DataType glType; - const unsigned int size; + const GL_DataType glType; + const unsigned int size; }; const ComponentType CT_USHORT = {ComponentType::GL_UNSIGNED_SHORT, 2}; -const ComponentType CT_UINT = {ComponentType::GL_UNSIGNED_INT, 4}; -const ComponentType CT_FLOAT = {ComponentType::GL_FLOAT, 4}; +const ComponentType CT_UINT = {ComponentType::GL_UNSIGNED_INT, 4}; +const ComponentType CT_FLOAT = {ComponentType::GL_FLOAT, 4}; // Map our low-level data types for glTF output struct GLType { - GLType(const ComponentType &componentType, unsigned int count, const std::string dataType) - : componentType(componentType), - count(count), - dataType(dataType) - {} + GLType(const ComponentType& componentType, unsigned int count, const std::string dataType) + : componentType(componentType), count(count), dataType(dataType) {} - unsigned int byteStride() const { return componentType.size * count; } + unsigned int byteStride() const { + return componentType.size * count; + } - void write(uint8_t *buf, const float scalar) const { *((float *) buf) = scalar; } - void write(uint8_t *buf, const uint32_t scalar) const { - switch(componentType.size) { - case 1: - *buf = (uint8_t)scalar; - break; - case 2: - *((uint16_t *) buf) = (uint16_t)scalar; - break; - case 4: - *((uint32_t *) buf) = scalar; - break; - } + void write(uint8_t* buf, const float scalar) const { + *((float*)buf) = scalar; + } + void write(uint8_t* buf, const uint32_t scalar) const { + switch (componentType.size) { + case 1: + *buf = (uint8_t)scalar; + break; + case 2: + *((uint16_t*)buf) = (uint16_t)scalar; + break; + case 4: + *((uint32_t*)buf) = scalar; + break; } + } - template - void write(uint8_t *buf, const mathfu::Vector &vector) const { - for (int ii = 0; ii < d; ii ++) { - ((T *)buf)[ii] = vector(ii); - } + template + void write(uint8_t* buf, const mathfu::Vector& vector) const { + for (int ii = 0; ii < d; ii++) { + ((T*)buf)[ii] = vector(ii); } - template - void write(uint8_t *buf, const mathfu::Matrix &matrix) const { - // three matrix types require special alignment considerations that we don't handle - // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment - assert(!(sizeof(T) == 1 && d == 2)); - assert(!(sizeof(T) == 1 && d == 3)); - assert(!(sizeof(T) == 2 && d == 2)); - for (int col = 0; col < d; col ++) { - for (int row = 0; row < d; row ++) { - // glTF matrices are column-major - ((T *)buf)[col * d + row] = matrix(row, col); - } - } + } + template + void write(uint8_t* buf, const mathfu::Matrix& matrix) const { + // three matrix types require special alignment considerations that we don't handle + // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment + assert(!(sizeof(T) == 1 && d == 2)); + assert(!(sizeof(T) == 1 && d == 3)); + assert(!(sizeof(T) == 2 && d == 2)); + for (int col = 0; col < d; col++) { + for (int row = 0; row < d; row++) { + // glTF matrices are column-major + ((T*)buf)[col * d + row] = matrix(row, col); + } } - template - void write(uint8_t *buf, const mathfu::Quaternion &quaternion) const { - for (int ii = 0; ii < 3; ii++) { - ((T *)buf)[ii] = quaternion.vector()(ii); - } - ((T *)buf)[3] = quaternion.scalar(); + } + template + void write(uint8_t* buf, const mathfu::Quaternion& quaternion) const { + for (int ii = 0; ii < 3; ii++) { + ((T*)buf)[ii] = quaternion.vector()(ii); } + ((T*)buf)[3] = quaternion.scalar(); + } - const ComponentType componentType; - const uint8_t count; - const std::string dataType; + const ComponentType componentType; + const uint8_t count; + const std::string dataType; }; -const GLType GLT_FLOAT = {CT_FLOAT, 1, "SCALAR"}; +const GLType GLT_FLOAT = {CT_FLOAT, 1, "SCALAR"}; const GLType GLT_USHORT = {CT_USHORT, 1, "SCALAR"}; -const GLType GLT_UINT = {CT_UINT, 1, "SCALAR"}; -const GLType GLT_VEC2F = {CT_FLOAT, 2, "VEC2"}; -const GLType GLT_VEC3F = {CT_FLOAT, 3, "VEC3"}; -const GLType GLT_VEC4F = {CT_FLOAT, 4, "VEC4"}; -const GLType GLT_VEC4I = {CT_USHORT, 4, "VEC4"}; -const GLType GLT_MAT2F = {CT_USHORT, 4, "MAT2"}; -const GLType GLT_MAT3F = {CT_USHORT, 9, "MAT3"}; -const GLType GLT_MAT4F = {CT_FLOAT, 16, "MAT4"}; -const GLType GLT_QUATF = {CT_FLOAT, 4, "VEC4"}; +const GLType GLT_UINT = {CT_UINT, 1, "SCALAR"}; +const GLType GLT_VEC2F = {CT_FLOAT, 2, "VEC2"}; +const GLType GLT_VEC3F = {CT_FLOAT, 3, "VEC3"}; +const GLType GLT_VEC4F = {CT_FLOAT, 4, "VEC4"}; +const GLType GLT_VEC4I = {CT_USHORT, 4, "VEC4"}; +const GLType GLT_MAT2F = {CT_USHORT, 4, "MAT2"}; +const GLType GLT_MAT3F = {CT_USHORT, 9, "MAT3"}; +const GLType GLT_MAT4F = {CT_FLOAT, 16, "MAT4"}; +const GLType GLT_QUATF = {CT_FLOAT, 4, "VEC4"}; /** * The base of any indexed glTF entity. */ -struct Holdable -{ - uint32_t ix; +struct Holdable { + uint32_t ix; - virtual json serialize() const = 0; + virtual json serialize() const = 0; }; -template -struct AttributeDefinition -{ - const std::string gltfName; - const T RawVertex::* rawAttributeIx; - const GLType glType; - const draco::GeometryAttribute::Type dracoAttribute; - const draco::DataType dracoComponentType; +template +struct AttributeDefinition { + const std::string gltfName; + const T RawVertex::*rawAttributeIx; + const GLType glType; + const draco::GeometryAttribute::Type dracoAttribute; + const draco::DataType dracoComponentType; - AttributeDefinition( - const std::string gltfName, const T RawVertex::*rawAttributeIx, const GLType &_glType, - const draco::GeometryAttribute::Type dracoAttribute, const draco::DataType dracoComponentType) - : gltfName(gltfName), - rawAttributeIx(rawAttributeIx), - glType(_glType), - dracoAttribute(dracoAttribute), - dracoComponentType(dracoComponentType) {} + AttributeDefinition( + const std::string gltfName, + const T RawVertex::*rawAttributeIx, + const GLType& _glType, + const draco::GeometryAttribute::Type dracoAttribute, + const draco::DataType dracoComponentType) + : gltfName(gltfName), + rawAttributeIx(rawAttributeIx), + glType(_glType), + dracoAttribute(dracoAttribute), + dracoComponentType(dracoComponentType) {} - AttributeDefinition( - const std::string gltfName, const T RawVertex::*rawAttributeIx, const GLType &_glType) - : gltfName(gltfName), - rawAttributeIx(rawAttributeIx), - glType(_glType), - dracoAttribute(draco::GeometryAttribute::INVALID), - dracoComponentType(draco::DataType::DT_INVALID) {} + AttributeDefinition( + const std::string gltfName, + const T RawVertex::*rawAttributeIx, + const GLType& _glType) + : gltfName(gltfName), + rawAttributeIx(rawAttributeIx), + glType(_glType), + dracoAttribute(draco::GeometryAttribute::INVALID), + dracoComponentType(draco::DataType::DT_INVALID) {} }; struct AccessorData; @@ -169,19 +172,15 @@ struct SceneData; struct SkinData; struct TextureData; -struct ModelData -{ - explicit ModelData(std::shared_ptr > const &_binary) - : binary(_binary) - { - } +struct ModelData { + explicit ModelData(std::shared_ptr> const& _binary) + : binary(_binary) {} - std::shared_ptr > const binary; + std::shared_ptr> const binary; }; -ModelData *Raw2Gltf( - std::ofstream &gltfOutStream, - const std::string &outputFolder, - const RawModel &raw, - const GltfOptions &options -); +ModelData* Raw2Gltf( + std::ofstream& gltfOutStream, + const std::string& outputFolder, + const RawModel& raw, + const GltfOptions& options); diff --git a/src/gltf/TextureBuilder.cpp b/src/gltf/TextureBuilder.cpp index db5a952..32873ff 100644 --- a/src/gltf/TextureBuilder.cpp +++ b/src/gltf/TextureBuilder.cpp @@ -1,212 +1,219 @@ /** -* Copyright (c) 2014-present, Facebook, Inc. -* All rights reserved. -* -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -*/ + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #include "TextureBuilder.hpp" #include #include +#include #include #include -#include #include #include // keep track of some texture data as we load them struct TexInfo { - explicit TexInfo(int rawTexIx) : rawTexIx(rawTexIx) {} + explicit TexInfo(int rawTexIx) : rawTexIx(rawTexIx) {} - const int rawTexIx; - int width {}; - int height {}; - int channels {}; - uint8_t *pixels {}; + const int rawTexIx; + int width{}; + int height{}; + int channels{}; + uint8_t* pixels{}; }; std::shared_ptr TextureBuilder::combine( - const std::vector &ixVec, - const std::string &tag, - const pixel_merger &computePixel, - bool includeAlphaChannel) -{ - const std::string key = texIndicesKey(ixVec, tag); - auto iter = textureByIndicesKey.find(key); - if (iter != textureByIndicesKey.end()) { - return iter->second; - } + const std::vector& ixVec, + const std::string& tag, + const pixel_merger& computePixel, + bool includeAlphaChannel) { + const std::string key = texIndicesKey(ixVec, tag); + auto iter = textureByIndicesKey.find(key); + if (iter != textureByIndicesKey.end()) { + return iter->second; + } - int width = -1, height = -1; - std::string mergedFilename = tag; - std::vector texes { }; - for (const int rawTexIx : ixVec) { - TexInfo info(rawTexIx); - if (rawTexIx >= 0) { - const RawTexture &rawTex = raw.GetTexture(rawTexIx); - const std::string &fileLoc = rawTex.fileLocation; - 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, - 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", - name, - info.width, info.height, width, height); - // this is bad enough that we abort the whole merge - return nullptr; - } - 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 - return nullptr; - } - // TODO: which channel combinations make sense in input files? - - // write 3 or 4 channels depending on whether or not we need transparency - int channels = includeAlphaChannel ? 4 : 3; - - std::vector mergedPixels(static_cast(channels * width * height)); - std::vector pixels(texes.size()); - std::vector pixelPointers(texes.size()); - for (int xx = 0; xx < width; xx ++) { - for (int yy = 0; yy < height; yy ++) { - pixels.clear(); - for (int jj = 0; jj < texes.size(); jj ++) { - const TexInfo &tex = texes[jj]; - // each texture's structure will depend on its channel count - int ii = tex.channels * (xx + yy*width); - int kk = 0; - if (tex.pixels != nullptr) { - for (; kk < tex.channels; kk ++) { - pixels[jj][kk] = tex.pixels[ii++] / 255.0f; - } - } - for (; kk < pixels[jj].size(); kk ++) { - pixels[jj][kk] = 1.0f; - } - pixelPointers[jj] = &pixels[jj]; - } - const pixel merged = computePixel(pixelPointers); - int ii = channels * (xx + yy*width); - for (int jj = 0; jj < channels; jj ++) { - mergedPixels[ii + jj] = static_cast(fmax(0, fmin(255.0f, merged[jj] * 255.0f))); - } - } - } - - // write a .png iff we need transparency in the destination texture - bool png = includeAlphaChannel; - - std::vector imgBuffer; - int res; - if (png) { - res = stbi_write_png_to_func(WriteToVectorContext, &imgBuffer, - width, height, channels, mergedPixels.data(), width * channels); - } else { - res = stbi_write_jpg_to_func(WriteToVectorContext, &imgBuffer, - width, height, channels, mergedPixels.data(), 80); - } - if (!res) { - fmt::printf("Warning: failed to generate merge texture '%s'.\n", mergedFilename); - return nullptr; - } - - ImageData *image; - if (options.outputBinary) { - const auto bufferView = gltf.AddRawBufferView(*gltf.defaultBuffer, imgBuffer.data(), imgBuffer.size()); - image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg"); - } else { - const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg"); - const std::string imagePath = outputFolder + imageFilename; - FILE *fp = fopen(imagePath.c_str(), "wb"); - if (fp == nullptr) { - fmt::printf("Warning:: Couldn't write file '%s' for writing.\n", imagePath); + int width = -1, height = -1; + std::string mergedFilename = tag; + std::vector texes{}; + for (const int rawTexIx : ixVec) { + TexInfo info(rawTexIx); + if (rawTexIx >= 0) { + const RawTexture& rawTex = raw.GetTexture(rawTexIx); + const std::string& fileLoc = rawTex.fileLocation; + 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, 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", + name, + info.width, + info.height, + width, + height); + // this is bad enough that we abort the whole merge return nullptr; + } + mergedFilename += "_" + name; } - - if (fwrite(imgBuffer.data(), imgBuffer.size(), 1, fp) != 1) { - fmt::printf("Warning: Failed to write %lu bytes to file '%s'.\n", imgBuffer.size(), imagePath); - fclose(fp); - return nullptr; - } - fclose(fp); - if (verboseOutput) { - fmt::printf("Wrote %lu bytes to texture '%s'.\n", imgBuffer.size(), imagePath); - } - image = new ImageData(mergedName, imageFilename); + } } - std::shared_ptr texDat = gltf.textures.hold( - new TextureData(mergedName, *gltf.defaultSampler, *gltf.images.hold(image))); - textureByIndicesKey.insert(std::make_pair(key, texDat)); - return texDat; + 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 + return nullptr; + } + // TODO: which channel combinations make sense in input files? + + // write 3 or 4 channels depending on whether or not we need transparency + int channels = includeAlphaChannel ? 4 : 3; + + std::vector mergedPixels(static_cast(channels * width * height)); + std::vector pixels(texes.size()); + std::vector pixelPointers(texes.size()); + for (int xx = 0; xx < width; xx++) { + for (int yy = 0; yy < height; yy++) { + pixels.clear(); + for (int jj = 0; jj < texes.size(); jj++) { + const TexInfo& tex = texes[jj]; + // each texture's structure will depend on its channel count + int ii = tex.channels * (xx + yy * width); + int kk = 0; + if (tex.pixels != nullptr) { + for (; kk < tex.channels; kk++) { + pixels[jj][kk] = tex.pixels[ii++] / 255.0f; + } + } + for (; kk < pixels[jj].size(); kk++) { + pixels[jj][kk] = 1.0f; + } + pixelPointers[jj] = &pixels[jj]; + } + const pixel merged = computePixel(pixelPointers); + int ii = channels * (xx + yy * width); + for (int jj = 0; jj < channels; jj++) { + mergedPixels[ii + jj] = static_cast(fmax(0, fmin(255.0f, merged[jj] * 255.0f))); + } + } + } + + // write a .png iff we need transparency in the destination texture + bool png = includeAlphaChannel; + + std::vector imgBuffer; + int res; + if (png) { + res = stbi_write_png_to_func( + WriteToVectorContext, + &imgBuffer, + width, + height, + channels, + mergedPixels.data(), + width * channels); + } else { + res = stbi_write_jpg_to_func( + WriteToVectorContext, &imgBuffer, width, height, channels, mergedPixels.data(), 80); + } + if (!res) { + fmt::printf("Warning: failed to generate merge texture '%s'.\n", mergedFilename); + return nullptr; + } + + ImageData* image; + if (options.outputBinary) { + const auto bufferView = + gltf.AddRawBufferView(*gltf.defaultBuffer, imgBuffer.data(), imgBuffer.size()); + image = new ImageData(mergedName, *bufferView, png ? "image/png" : "image/jpeg"); + } else { + const std::string imageFilename = mergedFilename + (png ? ".png" : ".jpg"); + const std::string imagePath = outputFolder + imageFilename; + FILE* fp = fopen(imagePath.c_str(), "wb"); + if (fp == nullptr) { + fmt::printf("Warning:: Couldn't write file '%s' for writing.\n", imagePath); + return nullptr; + } + + if (fwrite(imgBuffer.data(), imgBuffer.size(), 1, fp) != 1) { + fmt::printf( + "Warning: Failed to write %lu bytes to file '%s'.\n", imgBuffer.size(), imagePath); + fclose(fp); + return nullptr; + } + fclose(fp); + if (verboseOutput) { + fmt::printf("Wrote %lu bytes to texture '%s'.\n", imgBuffer.size(), imagePath); + } + image = new ImageData(mergedName, imageFilename); + } + std::shared_ptr texDat = gltf.textures.hold( + new TextureData(mergedName, *gltf.defaultSampler, *gltf.images.hold(image))); + textureByIndicesKey.insert(std::make_pair(key, texDat)); + return texDat; } /** Create a new TextureData for the given RawTexture index, or return a previously created one. */ -std::shared_ptr TextureBuilder::simple(int rawTexIndex, const std::string &tag) { - const std::string key = texIndicesKey({ rawTexIndex }, tag); - auto iter = textureByIndicesKey.find(key); - if (iter != textureByIndicesKey.end()) { - return iter->second; +std::shared_ptr TextureBuilder::simple(int rawTexIndex, const std::string& tag) { + const std::string key = texIndicesKey({rawTexIndex}, tag); + auto iter = textureByIndicesKey.find(key); + if (iter != textureByIndicesKey.end()) { + return iter->second; + } + + const RawTexture& rawTexture = raw.GetTexture(rawTexIndex); + 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(*gltf.defaultBuffer, rawTexture.fileLocation); + if (bufferView) { + std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); + image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix)); } - const RawTexture &rawTexture = raw.GetTexture(rawTexIndex); - 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(*gltf.defaultBuffer, rawTexture.fileLocation); - if (bufferView) { - std::string suffix = StringUtils::GetFileSuffixString(rawTexture.fileLocation); - image = new ImageData(relativeFilename, *bufferView, ImageUtils::suffixToMimeType(suffix)); - } - - } else if (!relativeFilename.empty()) { - image = new ImageData(relativeFilename, relativeFilename); - std::string outputPath = outputFolder + StringUtils::NormalizePath(relativeFilename); - if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true)) - { - if (verboseOutput) { - fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath); - } - } else { - // no point commenting further on read/write error; CopyFile() does enough of that, and we - // certainly want to to add an image struct to the glTF JSON, with the correct relative path - // reference, even if the copy failed. - } - } - if (!image) { - // fallback is tiny transparent PNG - image = new ImageData( - textureName, - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==" - ); + } else if (!relativeFilename.empty()) { + image = new ImageData(relativeFilename, relativeFilename); + std::string outputPath = outputFolder + StringUtils::NormalizePath(relativeFilename); + if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath, true)) { + if (verboseOutput) { + fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath); + } + } else { + // no point commenting further on read/write error; CopyFile() does enough of that, and we + // certainly want to to add an image struct to the glTF JSON, with the correct relative path + // reference, even if the copy failed. } + } + if (!image) { + // fallback is tiny transparent PNG + image = new ImageData( + textureName, + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg=="); + } - std::shared_ptr texDat = gltf.textures.hold( - new TextureData(textureName, *gltf.defaultSampler, *gltf.images.hold(image))); - textureByIndicesKey.insert(std::make_pair(key, texDat)); - return texDat; - + std::shared_ptr texDat = gltf.textures.hold( + new TextureData(textureName, *gltf.defaultSampler, *gltf.images.hold(image))); + textureByIndicesKey.insert(std::make_pair(key, texDat)); + return texDat; } \ No newline at end of file diff --git a/src/gltf/TextureBuilder.hpp b/src/gltf/TextureBuilder.hpp index a60efe9..bc5c854 100644 --- a/src/gltf/TextureBuilder.hpp +++ b/src/gltf/TextureBuilder.hpp @@ -1,11 +1,11 @@ /** -* Copyright (c) 2014-present, Facebook, Inc. -* All rights reserved. -* -* This source code is licensed under the BSD-style license found in the -* LICENSE file in the root directory of this source tree. An additional grant -* of patent rights can be found in the PATENTS file in the same directory. -*/ + * Copyright (c) 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ #pragma once @@ -17,60 +17,62 @@ #include "GltfModel.hpp" -class TextureBuilder -{ -public: - using pixel = std::array; // pixel components are floats in [0, 1] - using pixel_merger = std::function)>; +class TextureBuilder { + public: + using pixel = std::array; // pixel components are floats in [0, 1] + using pixel_merger = std::function)>; - TextureBuilder(const RawModel &raw, const GltfOptions &options, const std::string &outputFolder, GltfModel &gltf) - : raw(raw) - , options(options) - , outputFolder(outputFolder) - , gltf(gltf) - {} - ~TextureBuilder() {} + TextureBuilder( + const RawModel& raw, + const GltfOptions& options, + const std::string& outputFolder, + GltfModel& gltf) + : raw(raw), options(options), outputFolder(outputFolder), gltf(gltf) {} + ~TextureBuilder() {} - std::shared_ptr combine( - const std::vector &ixVec, - const std::string &tag, - const pixel_merger &mergeFunction, - bool transparency - ); + std::shared_ptr combine( + const std::vector& ixVec, + const std::string& tag, + const pixel_merger& mergeFunction, + bool transparency); - std::shared_ptr simple(int rawTexIndex, const std::string &tag); + std::shared_ptr simple(int rawTexIndex, const std::string& tag); - static std::string texIndicesKey(const std::vector &ixVec, const std::string &tag) { - std::string result = tag; - for (int ix : ixVec) { - result += "_" + std::to_string(ix); - } - return result; - }; - - static std::string describeChannel(int channels) { - switch(channels) { - case 1: return "G"; - case 2: return "GA"; - case 3: return "RGB"; - case 4: return "RGBA"; - default: - return fmt::format("?%d?", channels); - } - }; - - static void WriteToVectorContext(void *context, void *data, int size) { - auto *vec = static_cast *>(context); - for (int ii = 0; ii < size; ii ++) { - vec->push_back(((char *) data)[ii]); - } + static std::string texIndicesKey(const std::vector& ixVec, const std::string& tag) { + std::string result = tag; + for (int ix : ixVec) { + result += "_" + std::to_string(ix); } + return result; + }; -private: - const RawModel &raw; - const GltfOptions &options; - const std::string outputFolder; - GltfModel &gltf; + static std::string describeChannel(int channels) { + switch (channels) { + case 1: + return "G"; + case 2: + return "GA"; + case 3: + return "RGB"; + case 4: + return "RGBA"; + default: + return fmt::format("?%d?", channels); + } + }; - std::map> textureByIndicesKey; + static void WriteToVectorContext(void* context, void* data, int size) { + auto* vec = static_cast*>(context); + for (int ii = 0; ii < size; ii++) { + vec->push_back(((char*)data)[ii]); + } + } + + private: + const RawModel& raw; + const GltfOptions& options; + const std::string outputFolder; + GltfModel& gltf; + + std::map> textureByIndicesKey; }; diff --git a/src/gltf/properties/AccessorData.cpp b/src/gltf/properties/AccessorData.cpp index 64ea1f1..564260d 100644 --- a/src/gltf/properties/AccessorData.cpp +++ b/src/gltf/properties/AccessorData.cpp @@ -10,44 +10,32 @@ #include "AccessorData.hpp" #include "BufferViewData.hpp" -AccessorData::AccessorData(const BufferViewData &bufferView, GLType type, std::string name) +AccessorData::AccessorData(const BufferViewData& bufferView, GLType type, std::string name) : Holdable(), bufferView(bufferView.ix), type(std::move(type)), byteOffset(0), count(0), - name(name) -{ -} + name(name) {} AccessorData::AccessorData(GLType type) - : Holdable(), - bufferView(-1), - type(std::move(type)), - byteOffset(0), - count(0) -{ -} + : Holdable(), bufferView(-1), type(std::move(type)), byteOffset(0), count(0) {} -json AccessorData::serialize() const -{ - json result { - { "componentType", type.componentType.glType }, - { "type", type.dataType }, - { "count", count } - }; - if (bufferView >= 0) { - result["bufferView"] = bufferView; - result["byteOffset"] = byteOffset; - } - if (!min.empty()) { - result["min"] = min; - } - if (!max.empty()) { - result["max"] = max; - } - if (name.length() > 0) { - result["name"] = name; - } - return result; +json AccessorData::serialize() const { + json result{ + {"componentType", type.componentType.glType}, {"type", type.dataType}, {"count", count}}; + if (bufferView >= 0) { + result["bufferView"] = bufferView; + result["byteOffset"] = byteOffset; + } + if (!min.empty()) { + result["min"] = min; + } + if (!max.empty()) { + result["max"] = max; + } + if (name.length() > 0) { + result["name"] = name; + } + return result; } diff --git a/src/gltf/properties/AccessorData.hpp b/src/gltf/properties/AccessorData.hpp index 685bc11..06c93e6 100644 --- a/src/gltf/properties/AccessorData.hpp +++ b/src/gltf/properties/AccessorData.hpp @@ -11,36 +11,36 @@ #include "gltf/Raw2Gltf.hpp" -struct AccessorData : Holdable -{ - AccessorData(const BufferViewData &bufferView, GLType type, std::string name); - explicit AccessorData(GLType type); +struct AccessorData : Holdable { + AccessorData(const BufferViewData& bufferView, GLType type, std::string name); + explicit AccessorData(GLType type); - json serialize() const override; + json serialize() const override; - template - void appendAsBinaryArray(const std::vector &in, std::vector &out) - { - const unsigned int stride = type.byteStride(); - const size_t offset = out.size(); - const size_t count = in.size(); + template + void appendAsBinaryArray(const std::vector& in, std::vector& out) { + const unsigned int stride = type.byteStride(); + const size_t offset = out.size(); + const size_t count = in.size(); - this->count = (unsigned int) count; + this->count = (unsigned int)count; - out.resize(offset + count * stride); - for (int ii = 0; ii < count; ii ++) { - type.write(&out[offset + ii * stride], in[ii]); - } + out.resize(offset + count * stride); + for (int ii = 0; ii < count; ii++) { + type.write(&out[offset + ii * stride], in[ii]); } + } - unsigned int byteLength() const { return type.byteStride() * count; } + unsigned int byteLength() const { + return type.byteStride() * count; + } - const int bufferView; - const GLType type; + const int bufferView; + const GLType type; - unsigned int byteOffset; - unsigned int count; - std::vector min; - std::vector max; - std::string name; + unsigned int byteOffset; + unsigned int count; + std::vector min; + std::vector max; + std::string name; }; diff --git a/src/gltf/properties/AnimationData.cpp b/src/gltf/properties/AnimationData.cpp index 9d97946..a1dd09b 100644 --- a/src/gltf/properties/AnimationData.cpp +++ b/src/gltf/properties/AnimationData.cpp @@ -14,57 +14,42 @@ #include "AccessorData.hpp" #include "NodeData.hpp" -AnimationData::AnimationData(std::string name, const AccessorData &timeAccessor) - : Holdable(), - name(std::move(name)), - timeAccessor(timeAccessor.ix) {} +AnimationData::AnimationData(std::string name, const AccessorData& timeAccessor) + : Holdable(), name(std::move(name)), timeAccessor(timeAccessor.ix) {} // assumption: 1-to-1 relationship between channels and samplers; this is a simplification on what // glTF can express, but it means we can rely on samplerIx == channelIx throughout an animation -void AnimationData::AddNodeChannel(const NodeData &node, const AccessorData &accessor, std::string path) -{ - assert(channels.size() == samplers.size()); - uint32_t ix = channels.size(); - channels.emplace_back(channel_t(ix, node, std::move(path))); - samplers.emplace_back(sampler_t(timeAccessor, accessor.ix)); +void AnimationData::AddNodeChannel( + const NodeData& node, + const AccessorData& accessor, + std::string path) { + assert(channels.size() == samplers.size()); + uint32_t ix = channels.size(); + channels.emplace_back(channel_t(ix, node, std::move(path))); + samplers.emplace_back(sampler_t(timeAccessor, accessor.ix)); } -json AnimationData::serialize() const -{ - return { - { "name", name }, - { "channels", channels }, - { "samplers", samplers } - }; +json AnimationData::serialize() const { + return {{"name", name}, {"channels", channels}, {"samplers", samplers}}; } -AnimationData::channel_t::channel_t(uint32_t ix, const NodeData &node, std::string path) - : ix(ix), - node(node.ix), - path(std::move(path)) -{ +AnimationData::channel_t::channel_t(uint32_t ix, const NodeData& node, std::string path) + : ix(ix), node(node.ix), path(std::move(path)) {} + +AnimationData::sampler_t::sampler_t(uint32_t time, uint32_t output) : time(time), output(output) {} + +void to_json(json& j, const AnimationData::channel_t& data) { + j = json{{"sampler", data.ix}, + { + "target", + {{"node", data.node}, {"path", data.path}}, + }}; } -AnimationData::sampler_t::sampler_t(uint32_t time, uint32_t output) - : time(time), - output(output) -{ -} - -void to_json(json &j, const AnimationData::channel_t &data) { - j = json { - { "sampler", data.ix }, - { "target", { - { "node", data.node }, - { "path", data.path }}, - } - }; -} - -void to_json(json &j, const AnimationData::sampler_t &data) { - j = json { - { "input", data.time }, - { "interpolation", "LINEAR" }, - { "output", data.output }, - }; +void to_json(json& j, const AnimationData::sampler_t& data) { + j = json{ + {"input", data.time}, + {"interpolation", "LINEAR"}, + {"output", data.output}, + }; } diff --git a/src/gltf/properties/AnimationData.hpp b/src/gltf/properties/AnimationData.hpp index 90c7d0a..5703f25 100644 --- a/src/gltf/properties/AnimationData.hpp +++ b/src/gltf/properties/AnimationData.hpp @@ -11,38 +11,35 @@ #include "gltf/Raw2Gltf.hpp" -struct AnimationData : Holdable -{ - AnimationData(std::string name, const AccessorData &timeAccessor); +struct AnimationData : Holdable { + AnimationData(std::string name, const AccessorData& timeAccessor); - // assumption: 1-to-1 relationship between channels and samplers; this is a simplification on what - // glTF can express, but it means we can rely on samplerIx == channelIx throughout an animation - void AddNodeChannel(const NodeData &node, const AccessorData &accessor, std::string path); + // assumption: 1-to-1 relationship between channels and samplers; this is a simplification on what + // glTF can express, but it means we can rely on samplerIx == channelIx throughout an animation + void AddNodeChannel(const NodeData& node, const AccessorData& accessor, std::string path); - json serialize() const override; + json serialize() const override; - struct channel_t - { - channel_t(uint32_t _ix, const NodeData &node, std::string path); + struct channel_t { + channel_t(uint32_t _ix, const NodeData& node, std::string path); - const uint32_t ix; - const uint32_t node; - const std::string path; - }; + const uint32_t ix; + const uint32_t node; + const std::string path; + }; - struct sampler_t - { - sampler_t(uint32_t time, uint32_t output); + struct sampler_t { + sampler_t(uint32_t time, uint32_t output); - const uint32_t time; - const uint32_t output; - }; + const uint32_t time; + const uint32_t output; + }; - const std::string name; - const uint32_t timeAccessor; - std::vector channels; - std::vector samplers; + const std::string name; + const uint32_t timeAccessor; + std::vector channels; + std::vector samplers; }; -void to_json(json &j, const AnimationData::channel_t &data); -void to_json(json &j, const AnimationData::sampler_t &data); +void to_json(json& j, const AnimationData::channel_t& data); +void to_json(json& j, const AnimationData::sampler_t& data); diff --git a/src/gltf/properties/BufferData.cpp b/src/gltf/properties/BufferData.cpp index eac3d31..4fc8171 100644 --- a/src/gltf/properties/BufferData.cpp +++ b/src/gltf/properties/BufferData.cpp @@ -11,33 +11,24 @@ #include "BufferData.hpp" -BufferData::BufferData(const std::shared_ptr > &binData) - : Holdable(), - isGlb(true), - binData(binData) -{ -} +BufferData::BufferData(const std::shared_ptr>& binData) + : Holdable(), isGlb(true), binData(binData) {} -BufferData::BufferData(std::string uri, const std::shared_ptr > &binData, bool isEmbedded) - : Holdable(), - isGlb(false), - uri(isEmbedded ? "" : std::move(uri)), - binData(binData) -{ -} +BufferData::BufferData( + std::string uri, + const std::shared_ptr>& binData, + bool isEmbedded) + : Holdable(), isGlb(false), uri(isEmbedded ? "" : std::move(uri)), binData(binData) {} -json BufferData::serialize() const -{ - json result{ - {"byteLength", binData->size()} - }; - if (!isGlb) { - if (!uri.empty()) { - result["uri"] = uri; - } else { - std::string encoded = base64::encode(*binData); - result["uri"] = "data:application/octet-stream;base64," + encoded; - } +json BufferData::serialize() const { + json result{{"byteLength", binData->size()}}; + if (!isGlb) { + if (!uri.empty()) { + result["uri"] = uri; + } else { + std::string encoded = base64::encode(*binData); + result["uri"] = "data:application/octet-stream;base64," + encoded; } - return result; + } + return result; } diff --git a/src/gltf/properties/BufferData.hpp b/src/gltf/properties/BufferData.hpp index 0021184..58d1474 100644 --- a/src/gltf/properties/BufferData.hpp +++ b/src/gltf/properties/BufferData.hpp @@ -11,15 +11,17 @@ #include "gltf/Raw2Gltf.hpp" -struct BufferData : Holdable -{ - explicit BufferData(const std::shared_ptr > &binData); +struct BufferData : Holdable { + explicit BufferData(const std::shared_ptr>& binData); - BufferData(std::string uri, const std::shared_ptr > &binData, bool isEmbedded = false); + BufferData( + std::string uri, + const std::shared_ptr>& binData, + bool isEmbedded = false); - json serialize() const override; + json serialize() const override; - const bool isGlb; - const std::string uri; - const std::shared_ptr > binData; // TODO this is just weird + const bool isGlb; + const std::string uri; + const std::shared_ptr> binData; // TODO this is just weird }; diff --git a/src/gltf/properties/BufferViewData.cpp b/src/gltf/properties/BufferViewData.cpp index 8d2346a..34f23ce 100644 --- a/src/gltf/properties/BufferViewData.cpp +++ b/src/gltf/properties/BufferViewData.cpp @@ -10,23 +10,16 @@ #include "BufferViewData.hpp" #include "BufferData.hpp" -BufferViewData::BufferViewData(const BufferData &_buffer, const size_t _byteOffset, const GL_ArrayType _target) - : Holdable(), - buffer(_buffer.ix), - byteOffset((unsigned int) _byteOffset), - target(_target) -{ -} +BufferViewData::BufferViewData( + const BufferData& _buffer, + const size_t _byteOffset, + const GL_ArrayType _target) + : Holdable(), buffer(_buffer.ix), byteOffset((unsigned int)_byteOffset), target(_target) {} -json BufferViewData::serialize() const -{ - json result { - { "buffer", buffer }, - { "byteLength", byteLength }, - { "byteOffset", byteOffset } - }; - if (target != GL_ARRAY_NONE) { - result["target"] = target; - } - return result; +json BufferViewData::serialize() const { + json result{{"buffer", buffer}, {"byteLength", byteLength}, {"byteOffset", byteOffset}}; + if (target != GL_ARRAY_NONE) { + result["target"] = target; + } + return result; } diff --git a/src/gltf/properties/BufferViewData.hpp b/src/gltf/properties/BufferViewData.hpp index dc46457..a65c6bf 100644 --- a/src/gltf/properties/BufferViewData.hpp +++ b/src/gltf/properties/BufferViewData.hpp @@ -11,22 +11,20 @@ #include "gltf/Raw2Gltf.hpp" -struct BufferViewData : Holdable -{ - enum GL_ArrayType - { - GL_ARRAY_NONE = 0, // no GL buffer is being set - GL_ARRAY_BUFFER = 34962, - GL_ELEMENT_ARRAY_BUFFER = 34963 - }; +struct BufferViewData : Holdable { + enum GL_ArrayType { + GL_ARRAY_NONE = 0, // no GL buffer is being set + GL_ARRAY_BUFFER = 34962, + GL_ELEMENT_ARRAY_BUFFER = 34963 + }; - BufferViewData(const BufferData &_buffer, const size_t _byteOffset, const GL_ArrayType _target); + BufferViewData(const BufferData& _buffer, const size_t _byteOffset, const GL_ArrayType _target); - json serialize() const override; + json serialize() const override; - const unsigned int buffer; - const unsigned int byteOffset; - const GL_ArrayType target; + const unsigned int buffer; + const unsigned int byteOffset; + const GL_ArrayType target; - unsigned int byteLength = 0; + unsigned int byteLength = 0; }; diff --git a/src/gltf/properties/CameraData.cpp b/src/gltf/properties/CameraData.cpp index d10febe..78dcdc7 100644 --- a/src/gltf/properties/CameraData.cpp +++ b/src/gltf/properties/CameraData.cpp @@ -10,33 +10,21 @@ #include "CameraData.hpp" CameraData::CameraData() - : Holdable(), - aspectRatio(0.0f), - yfov(0.0f), - xmag(0.0f), - ymag(0.0f), - znear(0.0f), - zfar(0.0f) -{ -} + : Holdable(), aspectRatio(0.0f), yfov(0.0f), xmag(0.0f), ymag(0.0f), znear(0.0f), zfar(0.0f) {} -json CameraData::serialize() const -{ - json result { - { "name", name }, - { "type", type }, - }; - json subResult { - { "znear", znear }, - { "zfar", zfar } - }; - if (type == "perspective") { - subResult["aspectRatio"] = aspectRatio; - subResult["yfov"] = yfov; - } else { - subResult["xmag"] = xmag; - subResult["ymag"] = ymag; - } - result[type] = subResult; - return result; +json CameraData::serialize() const { + json result{ + {"name", name}, + {"type", type}, + }; + json subResult{{"znear", znear}, {"zfar", zfar}}; + if (type == "perspective") { + subResult["aspectRatio"] = aspectRatio; + subResult["yfov"] = yfov; + } else { + subResult["xmag"] = xmag; + subResult["ymag"] = ymag; + } + result[type] = subResult; + return result; } diff --git a/src/gltf/properties/CameraData.hpp b/src/gltf/properties/CameraData.hpp index f3c70a8..78fe500 100644 --- a/src/gltf/properties/CameraData.hpp +++ b/src/gltf/properties/CameraData.hpp @@ -12,17 +12,16 @@ #include "gltf/Raw2Gltf.hpp" // TODO: this class needs some work -struct CameraData : Holdable -{ - CameraData(); - json serialize() const override; +struct CameraData : Holdable { + CameraData(); + json serialize() const override; - std::string name; - std::string type; - float aspectRatio; - float yfov; - float xmag; - float ymag; - float znear; - float zfar; + std::string name; + std::string type; + float aspectRatio; + float yfov; + float xmag; + float ymag; + float znear; + float zfar; }; diff --git a/src/gltf/properties/ImageData.cpp b/src/gltf/properties/ImageData.cpp index 210ddd2..b816c7e 100644 --- a/src/gltf/properties/ImageData.cpp +++ b/src/gltf/properties/ImageData.cpp @@ -14,32 +14,14 @@ #include "BufferViewData.hpp" ImageData::ImageData(std::string name, std::string uri) - : Holdable(), - name(std::move(name)), - uri(std::move(uri)), - bufferView(-1) -{ -} + : Holdable(), name(std::move(name)), uri(std::move(uri)), bufferView(-1) {} -ImageData::ImageData(std::string name, const BufferViewData &bufferView, std::string mimeType) - : Holdable(), - name(std::move(name)), - bufferView(bufferView.ix), - mimeType(std::move(mimeType)) -{ -} +ImageData::ImageData(std::string name, const BufferViewData& bufferView, std::string mimeType) + : Holdable(), name(std::move(name)), bufferView(bufferView.ix), mimeType(std::move(mimeType)) {} -json ImageData::serialize() const -{ - if (bufferView < 0) { - return { - { "name", name }, - { "uri", uri } - }; - } - return { - { "name", name }, - { "bufferView", bufferView }, - { "mimeType", mimeType } - }; +json ImageData::serialize() const { + if (bufferView < 0) { + return {{"name", name}, {"uri", uri}}; + } + return {{"name", name}, {"bufferView", bufferView}, {"mimeType", mimeType}}; } diff --git a/src/gltf/properties/ImageData.hpp b/src/gltf/properties/ImageData.hpp index 5e62317..ece5aef 100644 --- a/src/gltf/properties/ImageData.hpp +++ b/src/gltf/properties/ImageData.hpp @@ -11,15 +11,14 @@ #include "gltf/Raw2Gltf.hpp" -struct ImageData : Holdable -{ - ImageData(std::string name, std::string uri); - ImageData(std::string name, const BufferViewData &bufferView, std::string mimeType); +struct ImageData : Holdable { + ImageData(std::string name, std::string uri); + ImageData(std::string name, const BufferViewData& bufferView, std::string mimeType); - json serialize() const override; + json serialize() const override; - const std::string name; - const std::string uri; // non-empty in gltf mode - const int32_t bufferView; // non-negative in glb mode - const std::string mimeType; + const std::string name; + const std::string uri; // non-empty in gltf mode + const int32_t bufferView; // non-negative in glb mode + const std::string mimeType; }; diff --git a/src/gltf/properties/LightData.cpp b/src/gltf/properties/LightData.cpp index d981f88..d090f9e 100644 --- a/src/gltf/properties/LightData.cpp +++ b/src/gltf/properties/LightData.cpp @@ -10,34 +10,31 @@ #include "LightData.hpp" LightData::LightData( - std::string name, Type type, Vec3f color, float intensity, - float innerConeAngle, float outerConeAngle) + std::string name, + Type type, + Vec3f color, + float intensity, + float innerConeAngle, + float outerConeAngle) : Holdable(), type(type), color(color), intensity(intensity), innerConeAngle(innerConeAngle), - outerConeAngle(outerConeAngle) -{ -} + outerConeAngle(outerConeAngle) {} -json LightData::serialize() const -{ - json result { - { "name", name }, - { "color", toStdVec(color) }, - { "intensity", intensity } - }; - switch(type) { - case Directional: - result["type"] = "directional"; - break; - case Point: - result["type"] = "point"; - break; - case Spot: - result["type"] = "spot"; - break; - } - return result; +json LightData::serialize() const { + json result{{"name", name}, {"color", toStdVec(color)}, {"intensity", intensity}}; + switch (type) { + case Directional: + result["type"] = "directional"; + break; + case Point: + result["type"] = "point"; + break; + case Spot: + result["type"] = "spot"; + break; + } + return result; } diff --git a/src/gltf/properties/LightData.hpp b/src/gltf/properties/LightData.hpp index 737e309..8445cd5 100644 --- a/src/gltf/properties/LightData.hpp +++ b/src/gltf/properties/LightData.hpp @@ -11,23 +11,27 @@ #include "gltf/Raw2Gltf.hpp" -struct LightData : Holdable -{ - enum Type { - Directional, - Point, - Spot, - }; +struct LightData : Holdable { + enum Type { + Directional, + Point, + Spot, + }; - LightData(std::string name, Type type, Vec3f color, float intensity, - float innerConeAngle, float outerConeAngle); + LightData( + std::string name, + Type type, + Vec3f color, + float intensity, + float innerConeAngle, + float outerConeAngle); - json serialize() const override; + json serialize() const override; - const std::string name; - const Type type; - const Vec3f color; - const float intensity; - const float innerConeAngle; - const float outerConeAngle; + const std::string name; + const Type type; + const Vec3f color; + const float intensity; + const float innerConeAngle; + const float outerConeAngle; }; diff --git a/src/gltf/properties/MaterialData.cpp b/src/gltf/properties/MaterialData.cpp index 2269928..c1697b3 100644 --- a/src/gltf/properties/MaterialData.cpp +++ b/src/gltf/properties/MaterialData.cpp @@ -11,77 +11,74 @@ #include "TextureData.hpp" // TODO: retrieve & pass in correct UV set from FBX -std::unique_ptr Tex::ref(const TextureData *tex, uint32_t texCoord) -{ - return std::unique_ptr { (tex != nullptr) ? new Tex(tex->ix, texCoord) : nullptr }; +std::unique_ptr Tex::ref(const TextureData* tex, uint32_t texCoord) { + return std::unique_ptr{(tex != nullptr) ? new Tex(tex->ix, texCoord) : nullptr}; } -Tex::Tex(uint32_t texRef, uint32_t texCoord) - : texRef(texRef), - texCoord(texCoord) {} +Tex::Tex(uint32_t texRef, uint32_t texCoord) : texRef(texRef), texCoord(texCoord) {} -void to_json(json &j, const Tex &data) { - j = json { - { "index", data.texRef }, - { "texCoord", data.texCoord } - }; +void to_json(json& j, const Tex& data) { + j = json{{"index", data.texRef}, {"texCoord", data.texCoord}}; } -KHRCmnUnlitMaterial::KHRCmnUnlitMaterial() -{ -} +KHRCmnUnlitMaterial::KHRCmnUnlitMaterial() {} -void to_json(json &j, const KHRCmnUnlitMaterial &d) -{ - j = json({}); +void to_json(json& j, const KHRCmnUnlitMaterial& d) { + j = json({}); } inline float clamp(float d, float bottom = 0, float top = 1) { - return std::max(bottom, std::min(top, d)); + return std::max(bottom, std::min(top, d)); } -inline Vec3f clamp(const Vec3f &vec, const Vec3f &bottom = VEC3F_ZERO, const Vec3f &top = VEC3F_ONE) { - return Vec3f::Max(bottom, Vec3f::Min(top, vec)); +inline Vec3f +clamp(const Vec3f& vec, const Vec3f& bottom = VEC3F_ZERO, const Vec3f& top = VEC3F_ONE) { + return Vec3f::Max(bottom, Vec3f::Min(top, vec)); } -inline Vec4f clamp(const Vec4f &vec, const Vec4f &bottom = VEC4F_ZERO, const Vec4f &top = VEC4F_ONE) { - return Vec4f::Max(bottom, Vec4f::Min(top, vec)); +inline Vec4f +clamp(const Vec4f& vec, const Vec4f& bottom = VEC4F_ZERO, const Vec4f& top = VEC4F_ONE) { + return Vec4f::Max(bottom, Vec4f::Min(top, vec)); } PBRMetallicRoughness::PBRMetallicRoughness( - const TextureData *baseColorTexture, const TextureData *metRoughTexture, - const Vec4f &baseColorFactor, float metallic, float roughness) + const TextureData* baseColorTexture, + const TextureData* metRoughTexture, + const Vec4f& baseColorFactor, + float metallic, + float roughness) : baseColorTexture(Tex::ref(baseColorTexture)), metRoughTexture(Tex::ref(metRoughTexture)), baseColorFactor(clamp(baseColorFactor)), metallic(clamp(metallic)), - roughness(clamp(roughness)) -{ -} + roughness(clamp(roughness)) {} -void to_json(json &j, const PBRMetallicRoughness &d) -{ - j = { }; - if (d.baseColorTexture != nullptr) { - j["baseColorTexture"] = *d.baseColorTexture; - } - if (d.baseColorFactor.LengthSquared() > 0) { - j["baseColorFactor"] = toStdVec(d.baseColorFactor); - } - 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; - } +void to_json(json& j, const PBRMetallicRoughness& d) { + j = {}; + if (d.baseColorTexture != nullptr) { + j["baseColorTexture"] = *d.baseColorTexture; + } + if (d.baseColorFactor.LengthSquared() > 0) { + j["baseColorFactor"] = toStdVec(d.baseColorFactor); + } + 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; + } } MaterialData::MaterialData( - std::string name, bool isTransparent, const RawShadingModel shadingModel, - const TextureData *normalTexture, const TextureData *occlusionTexture, - const TextureData *emissiveTexture, const Vec3f & emissiveFactor, + std::string name, + bool isTransparent, + const RawShadingModel shadingModel, + const TextureData* normalTexture, + const TextureData* occlusionTexture, + const TextureData* emissiveTexture, + const Vec3f& emissiveFactor, std::shared_ptr const khrCmnConstantMaterial, std::shared_ptr const pbrMetallicRoughness) : Holdable(), @@ -95,50 +92,43 @@ MaterialData::MaterialData( khrCmnConstantMaterial(khrCmnConstantMaterial), pbrMetallicRoughness(pbrMetallicRoughness) {} -json MaterialData::serialize() const -{ - json result = { - { "name", name }, - { "alphaMode", isTransparent ? "BLEND" : "OPAQUE" }, - { "extras", { - { "fromFBX", { - { "shadingModel", Describe(shadingModel) }, - { "isTruePBR", shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH } - }} - }} - }; +json MaterialData::serialize() const { + json result = {{"name", name}, + {"alphaMode", isTransparent ? "BLEND" : "OPAQUE"}, + {"extras", + {{"fromFBX", + {{"shadingModel", Describe(shadingModel)}, + {"isTruePBR", shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH}}}}}}; - if (normalTexture != nullptr) { - result["normalTexture"] = *normalTexture; - } - if (occlusionTexture != nullptr) { - result["occlusionTexture"] = *occlusionTexture; - } - if (emissiveTexture != nullptr) { - result["emissiveTexture"] = *emissiveTexture; - } - if (emissiveFactor.LengthSquared() > 0) { - result["emissiveFactor"] = toStdVec(emissiveFactor); - } - if (pbrMetallicRoughness != nullptr) { - result["pbrMetallicRoughness"] = *pbrMetallicRoughness; - } - if (khrCmnConstantMaterial != nullptr) { - json extensions = { }; - extensions[KHR_MATERIALS_CMN_UNLIT] = *khrCmnConstantMaterial; - result["extensions"] = extensions; - } + if (normalTexture != nullptr) { + result["normalTexture"] = *normalTexture; + } + if (occlusionTexture != nullptr) { + result["occlusionTexture"] = *occlusionTexture; + } + if (emissiveTexture != nullptr) { + result["emissiveTexture"] = *emissiveTexture; + } + if (emissiveFactor.LengthSquared() > 0) { + result["emissiveFactor"] = toStdVec(emissiveFactor); + } + if (pbrMetallicRoughness != nullptr) { + result["pbrMetallicRoughness"] = *pbrMetallicRoughness; + } + if (khrCmnConstantMaterial != nullptr) { + json extensions = {}; + extensions[KHR_MATERIALS_CMN_UNLIT] = *khrCmnConstantMaterial; + result["extensions"] = extensions; + } - for (const auto& i : userProperties) - { - auto& prop_map = result["extras"]["fromFBX"]["userProperties"]; + for (const auto& i : userProperties) { + auto& prop_map = result["extras"]["fromFBX"]["userProperties"]; - json j = json::parse(i); - for (const auto& k : json::iterator_wrapper(j)) - { - prop_map[k.key()] = k.value(); - } + json j = json::parse(i); + for (const auto& k : json::iterator_wrapper(j)) { + prop_map[k.key()] = k.value(); } + } - return result; + return result; } diff --git a/src/gltf/properties/MaterialData.hpp b/src/gltf/properties/MaterialData.hpp index 407f3ad..4a203df 100644 --- a/src/gltf/properties/MaterialData.hpp +++ b/src/gltf/properties/MaterialData.hpp @@ -13,58 +13,61 @@ #include "gltf/Raw2Gltf.hpp" -struct Tex -{ - static std::unique_ptr ref(const TextureData *tex, uint32_t texCoord = 0); - explicit Tex(uint32_t texRef, uint32_t texCoord); +struct Tex { + static std::unique_ptr ref(const TextureData* tex, uint32_t texCoord = 0); + explicit Tex(uint32_t texRef, uint32_t texCoord); - const uint32_t texRef; - const uint32_t texCoord; + const uint32_t texRef; + const uint32_t texCoord; }; -struct KHRCmnUnlitMaterial -{ - KHRCmnUnlitMaterial(); +struct KHRCmnUnlitMaterial { + KHRCmnUnlitMaterial(); }; -struct PBRMetallicRoughness -{ - PBRMetallicRoughness( - const TextureData *baseColorTexture, const TextureData *metRoughTexture, - const Vec4f &baseColorFactor, float metallic = 0.1f, float roughness = 0.6f); +struct PBRMetallicRoughness { + PBRMetallicRoughness( + const TextureData* baseColorTexture, + const TextureData* metRoughTexture, + const Vec4f& baseColorFactor, + float metallic = 0.1f, + float roughness = 0.6f); - std::unique_ptr baseColorTexture; - std::unique_ptr metRoughTexture; - const Vec4f baseColorFactor; - const float metallic; - const float roughness; + std::unique_ptr baseColorTexture; + std::unique_ptr metRoughTexture; + const Vec4f baseColorFactor; + const float metallic; + const float roughness; }; -struct MaterialData : Holdable -{ - MaterialData( - std::string name, bool isTransparent, RawShadingModel shadingModel, - const TextureData *normalTexture, const TextureData *occlusionTexture, - const TextureData *emissiveTexture, const Vec3f &emissiveFactor, - std::shared_ptr const khrCmnConstantMaterial, - std::shared_ptr const pbrMetallicRoughness); +struct MaterialData : Holdable { + MaterialData( + std::string name, + bool isTransparent, + RawShadingModel shadingModel, + const TextureData* normalTexture, + const TextureData* occlusionTexture, + const TextureData* emissiveTexture, + const Vec3f& emissiveFactor, + std::shared_ptr const khrCmnConstantMaterial, + std::shared_ptr const pbrMetallicRoughness); - json serialize() const override; + json serialize() const override; - const std::string name; - const RawShadingModel shadingModel; - const bool isTransparent; - const std::unique_ptr normalTexture; - const std::unique_ptr occlusionTexture; - const std::unique_ptr emissiveTexture; - const Vec3f emissiveFactor; + const std::string name; + const RawShadingModel shadingModel; + const bool isTransparent; + const std::unique_ptr normalTexture; + const std::unique_ptr occlusionTexture; + const std::unique_ptr emissiveTexture; + const Vec3f emissiveFactor; - const std::shared_ptr khrCmnConstantMaterial; - const std::shared_ptr pbrMetallicRoughness; + const std::shared_ptr khrCmnConstantMaterial; + const std::shared_ptr pbrMetallicRoughness; - std::vector userProperties; + std::vector userProperties; }; -void to_json(json &j, const Tex &data); -void to_json(json &j, const KHRCmnUnlitMaterial &d); -void to_json(json &j, const PBRMetallicRoughness &d); +void to_json(json& j, const Tex& data); +void to_json(json& j, const KHRCmnUnlitMaterial& d); +void to_json(json& j, const PBRMetallicRoughness& d); diff --git a/src/gltf/properties/MeshData.cpp b/src/gltf/properties/MeshData.cpp index cf31e89..c9e952a 100644 --- a/src/gltf/properties/MeshData.cpp +++ b/src/gltf/properties/MeshData.cpp @@ -10,25 +10,17 @@ #include "MeshData.hpp" #include "PrimitiveData.hpp" -MeshData::MeshData(const std::string &name, const std::vector &weights) - : Holdable(), - name(name), - weights(weights) -{ -} +MeshData::MeshData(const std::string& name, const std::vector& weights) + : Holdable(), name(name), weights(weights) {} -json MeshData::serialize() const -{ - json jsonPrimitivesArray = json::array(); - for (const auto &primitive : primitives) { - jsonPrimitivesArray.push_back(*primitive); - } - json result = { - { "name", name }, - { "primitives", jsonPrimitivesArray } - }; - if (!weights.empty()) { - result["weights"] = weights; - } - return result; +json MeshData::serialize() const { + json jsonPrimitivesArray = json::array(); + for (const auto& primitive : primitives) { + jsonPrimitivesArray.push_back(*primitive); + } + json result = {{"name", name}, {"primitives", jsonPrimitivesArray}}; + if (!weights.empty()) { + result["weights"] = weights; + } + return result; } diff --git a/src/gltf/properties/MeshData.hpp b/src/gltf/properties/MeshData.hpp index 2f3658a..786ea9a 100644 --- a/src/gltf/properties/MeshData.hpp +++ b/src/gltf/properties/MeshData.hpp @@ -17,18 +17,16 @@ #include "PrimitiveData.hpp" -struct MeshData : Holdable -{ - MeshData(const std::string &name, const std::vector &weights); +struct MeshData : Holdable { + MeshData(const std::string& name, const std::vector& weights); - void AddPrimitive(std::shared_ptr primitive) - { - primitives.push_back(std::move(primitive)); - } + void AddPrimitive(std::shared_ptr primitive) { + primitives.push_back(std::move(primitive)); + } - json serialize() const override; + json serialize() const override; - const std::string name; - const std::vector weights; - std::vector> primitives; + const std::string name; + const std::vector weights; + std::vector> primitives; }; diff --git a/src/gltf/properties/NodeData.cpp b/src/gltf/properties/NodeData.cpp index 4714fec..2093421 100644 --- a/src/gltf/properties/NodeData.cpp +++ b/src/gltf/properties/NodeData.cpp @@ -10,8 +10,11 @@ #include "NodeData.hpp" NodeData::NodeData( - std::string name, const Vec3f &translation, - const Quatf &rotation, const Vec3f &scale, bool isJoint) + std::string name, + const Vec3f& translation, + const Quatf& rotation, + const Vec3f& scale, + bool isJoint) : Holdable(), name(std::move(name)), isJoint(isJoint), @@ -22,90 +25,80 @@ NodeData::NodeData( mesh(-1), camera(-1), light(-1), - skin(-1) -{ + skin(-1) {} + +void NodeData::AddChildNode(uint32_t childIx) { + children.push_back(childIx); } -void NodeData::AddChildNode(uint32_t childIx) -{ - children.push_back(childIx); +void NodeData::SetMesh(uint32_t meshIx) { + assert(mesh < 0); + assert(!isJoint); + mesh = meshIx; } -void NodeData::SetMesh(uint32_t meshIx) -{ - assert(mesh < 0); - assert(!isJoint); - mesh = meshIx; +void NodeData::SetSkin(uint32_t skinIx) { + assert(skin < 0); + assert(!isJoint); + skin = skinIx; } -void NodeData::SetSkin(uint32_t skinIx) -{ - assert(skin < 0); - assert(!isJoint); - skin = skinIx; +void NodeData::SetCamera(uint32_t cameraIndex) { + assert(!isJoint); + camera = cameraIndex; } -void NodeData::SetCamera(uint32_t cameraIndex) -{ - assert(!isJoint); - camera = cameraIndex; +void NodeData::SetLight(uint32_t lightIndex) { + assert(!isJoint); + light = lightIndex; } -void NodeData::SetLight(uint32_t lightIndex) -{ - assert(!isJoint); - light = lightIndex; -} +json NodeData::serialize() const { + json result = {{"name", name}}; -json NodeData::serialize() const -{ - json result = { { "name", name } }; - - // if any of the T/R/S have NaN components, just leave them out of the glTF - auto maybeAdd = [&](std::string key, std::vector vec) -> void { - if (std::none_of(vec.begin(), vec.end(), [&](float n) { return std::isnan(n); })) { - result[key] = vec; - } - }; - maybeAdd("translation", toStdVec(translation)); - maybeAdd("rotation", toStdVec(rotation)); - maybeAdd("scale", toStdVec(scale)); - - if (!children.empty()) { - result["children"] = children; + // if any of the T/R/S have NaN components, just leave them out of the glTF + auto maybeAdd = [&](std::string key, std::vector vec) -> void { + if (std::none_of(vec.begin(), vec.end(), [&](float n) { return std::isnan(n); })) { + result[key] = vec; } - if (isJoint) { - // sanity-check joint node - assert(mesh < 0 && skin < 0); - } else { - // non-joint node - if (mesh >= 0) { - result["mesh"] = mesh; - } - if (!skeletons.empty()) { - result["skeletons"] = skeletons; - } - if (skin >= 0) { - result["skin"] = skin; - } - if (camera >= 0) { - result["camera"] = camera; - } - if (light >= 0) { - result["extensions"][KHR_LIGHTS_PUNCTUAL]["light"] = light; - } + }; + maybeAdd("translation", toStdVec(translation)); + maybeAdd("rotation", toStdVec(rotation)); + maybeAdd("scale", toStdVec(scale)); + + if (!children.empty()) { + result["children"] = children; + } + if (isJoint) { + // sanity-check joint node + assert(mesh < 0 && skin < 0); + } else { + // non-joint node + if (mesh >= 0) { + result["mesh"] = mesh; } - - for (const auto& i : userProperties) - { - auto& prop_map = result["extras"]["fromFBX"]["userProperties"]; - - json j = json::parse(i); - for (const auto& k : json::iterator_wrapper(j)) - { - prop_map[k.key()] = k.value(); - } + if (!skeletons.empty()) { + result["skeletons"] = skeletons; } + if (skin >= 0) { + result["skin"] = skin; + } + if (camera >= 0) { + result["camera"] = camera; + } + if (light >= 0) { + result["extensions"][KHR_LIGHTS_PUNCTUAL]["light"] = light; + } + } - return result; + for (const auto& i : userProperties) { + auto& prop_map = result["extras"]["fromFBX"]["userProperties"]; + + json j = json::parse(i); + for (const auto& k : json::iterator_wrapper(j)) { + prop_map[k.key()] = k.value(); + } + } + + return result; } diff --git a/src/gltf/properties/NodeData.hpp b/src/gltf/properties/NodeData.hpp index dbc0b86..6ee8315 100644 --- a/src/gltf/properties/NodeData.hpp +++ b/src/gltf/properties/NodeData.hpp @@ -11,28 +11,32 @@ #include "gltf/Raw2Gltf.hpp" -struct NodeData : Holdable -{ - NodeData(std::string name, const Vec3f &translation, const Quatf &rotation, const Vec3f &scale, bool isJoint); +struct NodeData : Holdable { + NodeData( + std::string name, + const Vec3f& translation, + const Quatf& rotation, + const Vec3f& scale, + bool isJoint); - void AddChildNode(uint32_t childIx); - void SetMesh(uint32_t meshIx); - void SetSkin(uint32_t skinIx); - void SetCamera(uint32_t camera); - void SetLight(uint32_t light); + void AddChildNode(uint32_t childIx); + void SetMesh(uint32_t meshIx); + void SetSkin(uint32_t skinIx); + void SetCamera(uint32_t camera); + void SetLight(uint32_t light); - json serialize() const override; + json serialize() const override; - const std::string name; - const bool isJoint; - Vec3f translation; - Quatf rotation; - Vec3f scale; - std::vector children; - int32_t mesh; - int32_t camera; - int32_t light; - int32_t skin; - std::vector skeletons; - std::vector userProperties; + const std::string name; + const bool isJoint; + Vec3f translation; + Quatf rotation; + Vec3f scale; + std::vector children; + int32_t mesh; + int32_t camera; + int32_t light; + int32_t skin; + std::vector skeletons; + std::vector userProperties; }; diff --git a/src/gltf/properties/PrimitiveData.cpp b/src/gltf/properties/PrimitiveData.cpp index b0b8d82..fe6e7c0 100644 --- a/src/gltf/properties/PrimitiveData.cpp +++ b/src/gltf/properties/PrimitiveData.cpp @@ -9,73 +9,71 @@ #include "PrimitiveData.hpp" -#include "MaterialData.hpp" #include "AccessorData.hpp" #include "BufferViewData.hpp" +#include "MaterialData.hpp" -PrimitiveData::PrimitiveData(const AccessorData &indices, const MaterialData &material, std::shared_ptr dracoMesh) +PrimitiveData::PrimitiveData( + const AccessorData& indices, + const MaterialData& material, + std::shared_ptr dracoMesh) : indices(indices.ix), material(material.ix), mode(TRIANGLES), dracoMesh(dracoMesh), dracoBufferView(-1) {} -PrimitiveData::PrimitiveData(const AccessorData &indices, const MaterialData &material) +PrimitiveData::PrimitiveData(const AccessorData& indices, const MaterialData& material) : indices(indices.ix), material(material.ix), mode(TRIANGLES), dracoMesh(nullptr), - dracoBufferView(-1) -{ + dracoBufferView(-1) {} + +void PrimitiveData::AddAttrib(std::string name, const AccessorData& accessor) { + attributes[name] = accessor.ix; } -void PrimitiveData::AddAttrib(std::string name, const AccessorData &accessor) -{ - attributes[name] = accessor.ix; +void PrimitiveData::NoteDracoBuffer(const BufferViewData& data) { + dracoBufferView = data.ix; } -void PrimitiveData::NoteDracoBuffer(const BufferViewData &data) -{ - dracoBufferView = data.ix; +void PrimitiveData::AddTarget( + const AccessorData* positions, + const AccessorData* normals, + const AccessorData* tangents) { + targetAccessors.push_back(std::make_tuple( + positions->ix, + normals != nullptr ? normals->ix : -1, + tangents != nullptr ? tangents->ix : -1)); } -void PrimitiveData::AddTarget(const AccessorData *positions, const AccessorData *normals, const AccessorData *tangents) -{ - targetAccessors.push_back(std::make_tuple( - positions->ix, - normals != nullptr ? normals->ix : -1, - tangents != nullptr ? tangents ->ix : -1 - )); -} - -void to_json(json &j, const PrimitiveData &d) { - j = { - { "material", d.material }, - { "mode", d.mode }, - { "attributes", d.attributes } - }; - if (d.indices >= 0) { - j["indices"] = d.indices; - } - if (!d.targetAccessors.empty()) { - json targets {}; - int pIx, nIx, tIx; - for (auto accessor : d.targetAccessors) { - std::tie(pIx, nIx, tIx) = accessor; - json target {}; - if (pIx >= 0) { target["POSITION"] = pIx; } - if (nIx >= 0) { target["NORMAL"] = nIx; } - if (tIx >= 0) { target["TANGENT"] = tIx; } - targets.push_back(target); - } - j["targets"] = targets; - } - if (!d.dracoAttributes.empty()) { - j["extensions"] = { - { KHR_DRACO_MESH_COMPRESSION, { - { "bufferView", d.dracoBufferView }, - { "attributes", d.dracoAttributes } - }} - }; +void to_json(json& j, const PrimitiveData& d) { + j = {{"material", d.material}, {"mode", d.mode}, {"attributes", d.attributes}}; + if (d.indices >= 0) { + j["indices"] = d.indices; + } + if (!d.targetAccessors.empty()) { + json targets{}; + int pIx, nIx, tIx; + for (auto accessor : d.targetAccessors) { + std::tie(pIx, nIx, tIx) = accessor; + json target{}; + if (pIx >= 0) { + target["POSITION"] = pIx; + } + if (nIx >= 0) { + target["NORMAL"] = nIx; + } + if (tIx >= 0) { + target["TANGENT"] = tIx; + } + targets.push_back(target); } + j["targets"] = targets; + } + if (!d.dracoAttributes.empty()) { + j["extensions"] = {{KHR_DRACO_MESH_COMPRESSION, + {{"bufferView", d.dracoBufferView}, {"attributes", d.dracoAttributes}}}}; + } } diff --git a/src/gltf/properties/PrimitiveData.hpp b/src/gltf/properties/PrimitiveData.hpp index 64fc2a3..16bf7e9 100644 --- a/src/gltf/properties/PrimitiveData.hpp +++ b/src/gltf/properties/PrimitiveData.hpp @@ -11,62 +11,70 @@ #include "gltf/Raw2Gltf.hpp" -struct PrimitiveData -{ - enum MeshMode - { - POINTS = 0, - LINES, - LINE_LOOP, - LINE_STRIP, - TRIANGLES, - TRIANGLE_STRIP, - TRIANGLE_FAN - }; +struct PrimitiveData { + enum MeshMode { + POINTS = 0, + LINES, + LINE_LOOP, + LINE_STRIP, + TRIANGLES, + TRIANGLE_STRIP, + TRIANGLE_FAN + }; - PrimitiveData(const AccessorData &indices, const MaterialData &material, std::shared_ptr dracoMesh); + PrimitiveData( + const AccessorData& indices, + const MaterialData& material, + std::shared_ptr dracoMesh); - PrimitiveData(const AccessorData &indices, const MaterialData &material); + PrimitiveData(const AccessorData& indices, const MaterialData& material); - void AddAttrib(std::string name, const AccessorData &accessor); + void AddAttrib(std::string name, const AccessorData& accessor); - void AddTarget(const AccessorData *positions, const AccessorData *normals, const AccessorData *tangents); + void AddTarget( + const AccessorData* positions, + const AccessorData* normals, + const AccessorData* tangents); - template - void AddDracoAttrib(const AttributeDefinition attribute, const std::vector &attribArr) - { - draco::PointAttribute att; - int8_t componentCount = attribute.glType.count; - att.Init( - attribute.dracoAttribute, nullptr, componentCount, attribute.dracoComponentType, - false, componentCount * draco::DataTypeLength(attribute.dracoComponentType), 0); + template + void AddDracoAttrib(const AttributeDefinition attribute, const std::vector& attribArr) { + draco::PointAttribute att; + int8_t componentCount = attribute.glType.count; + att.Init( + attribute.dracoAttribute, + nullptr, + componentCount, + attribute.dracoComponentType, + false, + componentCount * draco::DataTypeLength(attribute.dracoComponentType), + 0); - const int dracoAttId = dracoMesh->AddAttribute(att, true, attribArr.size()); - draco::PointAttribute *attPtr = dracoMesh->attribute(dracoAttId); + const int dracoAttId = dracoMesh->AddAttribute(att, true, attribArr.size()); + draco::PointAttribute* attPtr = dracoMesh->attribute(dracoAttId); - std::vector buf(sizeof(T)); - for (uint32_t ii = 0; ii < attribArr.size(); ii++) { - uint8_t *ptr = &buf[0]; - attribute.glType.write(ptr, attribArr[ii]); - attPtr->SetAttributeValue(attPtr->mapped_index(draco::PointIndex(ii)), ptr); - } - - dracoAttributes[attribute.gltfName] = dracoAttId; + std::vector buf(sizeof(T)); + for (uint32_t ii = 0; ii < attribArr.size(); ii++) { + uint8_t* ptr = &buf[0]; + attribute.glType.write(ptr, attribArr[ii]); + attPtr->SetAttributeValue(attPtr->mapped_index(draco::PointIndex(ii)), ptr); } - void NoteDracoBuffer(const BufferViewData &data); + dracoAttributes[attribute.gltfName] = dracoAttId; + } - const int indices; - const unsigned int material; - const MeshMode mode; + void NoteDracoBuffer(const BufferViewData& data); - std::vector> targetAccessors {}; + const int indices; + const unsigned int material; + const MeshMode mode; - std::map attributes; - std::map dracoAttributes; + std::vector> targetAccessors{}; - std::shared_ptr dracoMesh; - int dracoBufferView; + std::map attributes; + std::map dracoAttributes; + + std::shared_ptr dracoMesh; + int dracoBufferView; }; -void to_json(json &j, const PrimitiveData &d); +void to_json(json& j, const PrimitiveData& d); diff --git a/src/gltf/properties/SamplerData.hpp b/src/gltf/properties/SamplerData.hpp index 43978b5..4361bba 100644 --- a/src/gltf/properties/SamplerData.hpp +++ b/src/gltf/properties/SamplerData.hpp @@ -11,15 +11,11 @@ #include "gltf/Raw2Gltf.hpp" -struct SamplerData : Holdable -{ - // this is where magFilter, minFilter, wrapS and wrapT would go, should we want it - SamplerData() - : Holdable() - { - } +struct SamplerData : Holdable { + // this is where magFilter, minFilter, wrapS and wrapT would go, should we want it + SamplerData() : Holdable() {} - json serialize() const override { - return json::object(); - } + json serialize() const override { + return json::object(); + } }; diff --git a/src/gltf/properties/SceneData.cpp b/src/gltf/properties/SceneData.cpp index 8512250..dd68497 100644 --- a/src/gltf/properties/SceneData.cpp +++ b/src/gltf/properties/SceneData.cpp @@ -11,18 +11,10 @@ #include "NodeData.hpp" -SceneData::SceneData(std::string name, const NodeData &rootNode) - : Holdable(), - name(std::move(name)), - nodes({rootNode.ix}) -{ -} +SceneData::SceneData(std::string name, const NodeData& rootNode) + : Holdable(), name(std::move(name)), nodes({rootNode.ix}) {} -json SceneData::serialize() const -{ - assert(nodes.size() <= 1); - return { - { "name", name }, - { "nodes", nodes } - }; +json SceneData::serialize() const { + assert(nodes.size() <= 1); + return {{"name", name}, {"nodes", nodes}}; } diff --git a/src/gltf/properties/SceneData.hpp b/src/gltf/properties/SceneData.hpp index 3fbb73f..cfef6ab 100644 --- a/src/gltf/properties/SceneData.hpp +++ b/src/gltf/properties/SceneData.hpp @@ -11,12 +11,11 @@ #include "gltf/Raw2Gltf.hpp" -struct SceneData : Holdable -{ - SceneData(std::string name, const NodeData &rootNode); +struct SceneData : Holdable { + SceneData(std::string name, const NodeData& rootNode); - json serialize() const override; + json serialize() const override; - const std::string name; - std::vector nodes; + const std::string name; + std::vector nodes; }; diff --git a/src/gltf/properties/SkinData.cpp b/src/gltf/properties/SkinData.cpp index db66e40..1c808bf 100644 --- a/src/gltf/properties/SkinData.cpp +++ b/src/gltf/properties/SkinData.cpp @@ -13,20 +13,16 @@ #include "NodeData.hpp" SkinData::SkinData( - const std::vector joints, const AccessorData &inverseBindMatricesAccessor, - const NodeData &skeletonRootNode) + const std::vector joints, + const AccessorData& inverseBindMatricesAccessor, + const NodeData& skeletonRootNode) : Holdable(), joints(joints), inverseBindMatrices(inverseBindMatricesAccessor.ix), - skeletonRootNode(skeletonRootNode.ix) -{ -} + skeletonRootNode(skeletonRootNode.ix) {} -json SkinData::serialize() const -{ - return { - { "joints", joints }, - { "inverseBindMatrices", inverseBindMatrices }, - { "skeleton", skeletonRootNode } - }; +json SkinData::serialize() const { + return {{"joints", joints}, + {"inverseBindMatrices", inverseBindMatrices}, + {"skeleton", skeletonRootNode}}; } diff --git a/src/gltf/properties/SkinData.hpp b/src/gltf/properties/SkinData.hpp index 8a3b22f..1028ebd 100644 --- a/src/gltf/properties/SkinData.hpp +++ b/src/gltf/properties/SkinData.hpp @@ -11,15 +11,15 @@ #include "gltf/Raw2Gltf.hpp" -struct SkinData : Holdable -{ - SkinData( - const std::vector joints, const AccessorData &inverseBindMatricesAccessor, - const NodeData &skeletonRootNode); +struct SkinData : Holdable { + SkinData( + const std::vector joints, + const AccessorData& inverseBindMatricesAccessor, + const NodeData& skeletonRootNode); - json serialize() const override; + json serialize() const override; - const std::vector joints; - const uint32_t skeletonRootNode; - const uint32_t inverseBindMatrices; + const std::vector joints; + const uint32_t skeletonRootNode; + const uint32_t inverseBindMatrices; }; diff --git a/src/gltf/properties/TextureData.cpp b/src/gltf/properties/TextureData.cpp index f645fcf..79080a9 100644 --- a/src/gltf/properties/TextureData.cpp +++ b/src/gltf/properties/TextureData.cpp @@ -12,19 +12,9 @@ #include "ImageData.hpp" #include "SamplerData.hpp" -TextureData::TextureData(std::string name, const SamplerData &sampler, const ImageData &source) - : Holdable(), - name(std::move(name)), - sampler(sampler.ix), - source(source.ix) -{ -} +TextureData::TextureData(std::string name, const SamplerData& sampler, const ImageData& source) + : Holdable(), name(std::move(name)), sampler(sampler.ix), source(source.ix) {} -json TextureData::serialize() const -{ - return { - { "name", name }, - { "sampler", sampler }, - { "source", source } - }; +json TextureData::serialize() const { + return {{"name", name}, {"sampler", sampler}, {"source", source}}; } diff --git a/src/gltf/properties/TextureData.hpp b/src/gltf/properties/TextureData.hpp index 4ff74d9..8c69f40 100644 --- a/src/gltf/properties/TextureData.hpp +++ b/src/gltf/properties/TextureData.hpp @@ -11,13 +11,12 @@ #include "gltf/Raw2Gltf.hpp" -struct TextureData : Holdable -{ - TextureData(std::string name, const SamplerData &sampler, const ImageData &source); +struct TextureData : Holdable { + TextureData(std::string name, const SamplerData& sampler, const ImageData& source); - json serialize() const override; + json serialize() const override; - const std::string name; - const uint32_t sampler; - const uint32_t source; + const std::string name; + const uint32_t sampler; + const uint32_t source; }; diff --git a/src/mathfu.hpp b/src/mathfu.hpp index 75cd7c2..aaed7dd 100644 --- a/src/mathfu.hpp +++ b/src/mathfu.hpp @@ -11,93 +11,93 @@ #include -#include #include #include #include +#include /** * All the mathfu:: implementations of our core data types. */ -template -struct Bounds -{ - mathfu::Vector min; - mathfu::Vector max; - bool initialized = false; +template +struct Bounds { + mathfu::Vector min; + mathfu::Vector max; + bool initialized = false; - void Clear() { - min = mathfu::Vector(); - max = mathfu::Vector(); - initialized = false; - } + void Clear() { + min = mathfu::Vector(); + max = mathfu::Vector(); + initialized = false; + } - void AddPoint(const mathfu::Vector &p) { - if (initialized) { - for (int ii = 0; ii < d; ii ++) { - min(ii) = std::min(min(ii), p(ii)); - max(ii) = std::max(max(ii), p(ii)); - } - } else { - min = p; - max = p; - initialized = true; - } + void AddPoint(const mathfu::Vector& p) { + if (initialized) { + for (int ii = 0; ii < d; ii++) { + min(ii) = std::min(min(ii), p(ii)); + max(ii) = std::max(max(ii), p(ii)); + } + } else { + min = p; + max = p; + initialized = true; } + } }; typedef mathfu::Vector Vec4i; typedef mathfu::Matrix Mat4i; -typedef mathfu::Vector Vec2f; -typedef mathfu::Vector Vec3f; -typedef mathfu::Vector Vec4f; -typedef mathfu::Matrix Mat2f; -typedef mathfu::Matrix Mat3f; -typedef mathfu::Matrix Mat4f; -typedef mathfu::Quaternion Quatf; -typedef Bounds Boundsf; +typedef mathfu::Vector Vec2f; +typedef mathfu::Vector Vec3f; +typedef mathfu::Vector Vec4f; +typedef mathfu::Matrix Mat2f; +typedef mathfu::Matrix Mat3f; +typedef mathfu::Matrix Mat4f; +typedef mathfu::Quaternion Quatf; +typedef Bounds Boundsf; -#define VEC3F_ONE (Vec3f {1.0f}) -#define VEC3F_ZERO (Vec3f {0.0f}) -#define VEC4F_ONE (Vec4f {1.0f}) -#define VEC4F_ZERO (Vec4f {0.0f}) +#define VEC3F_ONE (Vec3f{1.0f}) +#define VEC3F_ZERO (Vec3f{0.0f}) +#define VEC4F_ONE (Vec4f{1.0f}) +#define VEC4F_ZERO (Vec4f{0.0f}) -template inline std::vector toStdVec(const mathfu::Vector &vec) -{ - std::vector result(d); - for (int ii = 0; ii < d; ii ++) { - result[ii] = vec[ii]; +template +inline std::vector toStdVec(const mathfu::Vector& vec) { + std::vector result(d); + for (int ii = 0; ii < d; ii++) { + result[ii] = vec[ii]; + } + return result; +} + +template +std::vector toStdVec(const mathfu::Quaternion& quat) { + return std::vector{quat.vector()[0], quat.vector()[1], quat.vector()[2], quat.scalar()}; +} + +inline Vec3f toVec3f(const FbxDouble3& v) { + return Vec3f((float)v[0], (float)v[1], (float)v[2]); +} + +inline Vec3f toVec3f(const FbxVector4& v) { + return Vec3f((float)v[0], (float)v[1], (float)v[2]); +} + +inline Vec4f toVec4f(const FbxVector4& v) { + return Vec4f((float)v[0], (float)v[1], (float)v[2], (float)v[3]); +} + +inline Mat4f toMat4f(const FbxAMatrix& m) { + auto result = Mat4f(); + for (int row = 0; row < 4; row++) { + for (int col = 0; col < 4; col++) { + result(row, col) = (float)m[row][col]; } - return result; + } + return result; } -template std::vector toStdVec(const mathfu::Quaternion &quat) { - return std::vector { quat.vector()[0], quat.vector()[1], quat.vector()[2], quat.scalar() }; -} - -inline Vec3f toVec3f(const FbxDouble3 &v) { - return Vec3f((float) v[0], (float) v[1], (float) v[2]); -} - -inline Vec3f toVec3f(const FbxVector4 &v) { - return Vec3f((float) v[0], (float) v[1], (float) v[2]); -} - -inline Vec4f toVec4f(const FbxVector4 &v) { - return Vec4f((float) v[0], (float) v[1], (float) v[2], (float) v[3]); -} - -inline Mat4f toMat4f(const FbxAMatrix &m) { - auto result = Mat4f(); - for (int row = 0; row < 4; row ++) { - for (int col = 0; col < 4; col ++) { - result(row, col) = (float) m[row][col]; - } - } - return result; -} - -inline Quatf toQuatf(const FbxQuaternion &q) { - return Quatf((float) q[3], (float) q[0], (float) q[1], (float) q[2]); +inline Quatf toQuatf(const FbxQuaternion& q) { + return Quatf((float)q[3], (float)q[0], (float)q[1], (float)q[2]); } diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index 57f9382..2a7fe12 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -9,684 +9,707 @@ #include "RawModel.hpp" -#include -#include -#include #include #include #include +#include +#include +#include -#if defined( __unix__ ) +#if defined(__unix__) #include #endif -#include "utils/String_Utils.hpp" #include "utils/Image_Utils.hpp" +#include "utils/String_Utils.hpp" -bool RawVertex::operator==(const RawVertex &other) const -{ - return (position == other.position) && - (normal == other.normal) && - (tangent == other.tangent) && - (binormal == other.binormal) && - (color == other.color) && - (uv0 == other.uv0) && - (uv1 == other.uv1) && - (jointIndices == other.jointIndices) && - (jointWeights == other.jointWeights) && - (polarityUv0 == other.polarityUv0) && - (blendSurfaceIx == other.blendSurfaceIx) && - (blends == other.blends); +bool RawVertex::operator==(const RawVertex& other) const { + return (position == other.position) && (normal == other.normal) && (tangent == other.tangent) && + (binormal == other.binormal) && (color == other.color) && (uv0 == other.uv0) && + (uv1 == other.uv1) && (jointIndices == other.jointIndices) && + (jointWeights == other.jointWeights) && (polarityUv0 == other.polarityUv0) && + (blendSurfaceIx == other.blendSurfaceIx) && (blends == other.blends); } -size_t RawVertex::Difference(const RawVertex &other) const -{ - size_t attributes = 0; - if (position != other.position) { attributes |= RAW_VERTEX_ATTRIBUTE_POSITION; } - if (normal != other.normal) { attributes |= RAW_VERTEX_ATTRIBUTE_NORMAL; } - if (tangent != other.tangent) { attributes |= RAW_VERTEX_ATTRIBUTE_TANGENT; } - if (binormal != other.binormal) { attributes |= RAW_VERTEX_ATTRIBUTE_BINORMAL; } - if (color != other.color) { attributes |= RAW_VERTEX_ATTRIBUTE_COLOR; } - if (uv0 != other.uv0) { attributes |= RAW_VERTEX_ATTRIBUTE_UV0; } - if (uv1 != other.uv1) { attributes |= RAW_VERTEX_ATTRIBUTE_UV1; } - // Always need both or neither. - if (jointIndices != other.jointIndices) { attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; } - if (jointWeights != other.jointWeights) { attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; } - return attributes; +size_t RawVertex::Difference(const RawVertex& other) const { + size_t attributes = 0; + if (position != other.position) { + attributes |= RAW_VERTEX_ATTRIBUTE_POSITION; + } + if (normal != other.normal) { + attributes |= RAW_VERTEX_ATTRIBUTE_NORMAL; + } + if (tangent != other.tangent) { + attributes |= RAW_VERTEX_ATTRIBUTE_TANGENT; + } + if (binormal != other.binormal) { + attributes |= RAW_VERTEX_ATTRIBUTE_BINORMAL; + } + if (color != other.color) { + attributes |= RAW_VERTEX_ATTRIBUTE_COLOR; + } + if (uv0 != other.uv0) { + attributes |= RAW_VERTEX_ATTRIBUTE_UV0; + } + if (uv1 != other.uv1) { + attributes |= RAW_VERTEX_ATTRIBUTE_UV1; + } + // Always need both or neither. + if (jointIndices != other.jointIndices) { + attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + } + if (jointWeights != other.jointWeights) { + attributes |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + } + return attributes; } -RawModel::RawModel() - : vertexAttributes(0) -{ +RawModel::RawModel() : vertexAttributes(0) {} + +void RawModel::AddVertexAttribute(const RawVertexAttribute attrib) { + vertexAttributes |= attrib; } -void RawModel::AddVertexAttribute(const RawVertexAttribute attrib) -{ - vertexAttributes |= attrib; +int RawModel::AddVertex(const RawVertex& vertex) { + auto it = vertexHash.find(vertex); + if (it != vertexHash.end()) { + return it->second; + } + vertexHash.emplace(vertex, (int)vertices.size()); + vertices.push_back(vertex); + return (int)vertices.size() - 1; } -int RawModel::AddVertex(const RawVertex &vertex) -{ - auto it = vertexHash.find(vertex); - if (it != vertexHash.end()) { - return it->second; +int RawModel::AddTriangle( + const int v0, + const int v1, + const int v2, + const int materialIndex, + const int surfaceIndex) { + const RawTriangle triangle = {{v0, v1, v2}, materialIndex, surfaceIndex}; + triangles.push_back(triangle); + return (int)triangles.size() - 1; +} + +int RawModel::AddTexture( + const std::string& name, + const std::string& fileName, + const std::string& fileLocation, + RawTextureUsage usage) { + if (name.empty()) { + return -1; + } + for (size_t i = 0; i < textures.size(); i++) { + if (StringUtils::CompareNoCase(textures[i].name, name) == 0 && textures[i].usage == usage) { + return (int)i; } - vertexHash.emplace(vertex, (int) vertices.size()); - vertices.push_back(vertex); - return (int) vertices.size() - 1; + } + + const ImageUtils::ImageProperties properties = ImageUtils::GetImageProperties( + !fileLocation.empty() ? fileLocation.c_str() : fileName.c_str()); + + RawTexture texture; + texture.name = name; + texture.width = properties.width; + texture.height = properties.height; + texture.mipLevels = + (int)ceilf(log2f(std::max((float)properties.width, (float)properties.height))); + texture.usage = usage; + texture.occlusion = (properties.occlusion == ImageUtils::IMAGE_TRANSPARENT) + ? RAW_TEXTURE_OCCLUSION_TRANSPARENT + : RAW_TEXTURE_OCCLUSION_OPAQUE; + texture.fileName = fileName; + texture.fileLocation = fileLocation; + textures.emplace_back(texture); + return (int)textures.size() - 1; } -int RawModel::AddTriangle(const int v0, const int v1, const int v2, const int materialIndex, const int surfaceIndex) -{ - const RawTriangle triangle = {{v0, v1, v2}, materialIndex, surfaceIndex}; - triangles.push_back(triangle); - return (int) triangles.size() - 1; -} - -int RawModel::AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage) -{ - if (name.empty()) { - return -1; - } - for (size_t i = 0; i < textures.size(); i++) { - if (StringUtils::CompareNoCase(textures[i].name, name) == 0 && textures[i].usage == usage) { - return (int) i; - } - } - - const ImageUtils::ImageProperties - properties = ImageUtils::GetImageProperties(!fileLocation.empty() ? fileLocation.c_str() : fileName.c_str()); - - RawTexture texture; - texture.name = name; - texture.width = properties.width; - texture.height = properties.height; - texture.mipLevels = (int) ceilf(log2f(std::max((float) properties.width, (float) properties.height))); - texture.usage = usage; - texture.occlusion = (properties.occlusion == ImageUtils::IMAGE_TRANSPARENT) ? - RAW_TEXTURE_OCCLUSION_TRANSPARENT : RAW_TEXTURE_OCCLUSION_OPAQUE; - texture.fileName = fileName; - texture.fileLocation = fileLocation; - textures.emplace_back(texture); - return (int) textures.size() - 1; -} - -int RawModel::AddMaterial(const RawMaterial &material) -{ - return AddMaterial(material.name.c_str(), material.type, material.textures, material.info, material.userProperties); +int RawModel::AddMaterial(const RawMaterial& material) { + return AddMaterial( + material.name.c_str(), + material.type, + material.textures, + material.info, + material.userProperties); } int RawModel::AddMaterial( - const char *name, + const char* name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX], std::shared_ptr materialInfo, - const std::vector& userProperties) -{ - for (size_t i = 0; i < materials.size(); i++) { - if (materials[i].name != name) { - continue; - } - if (materials[i].type != materialType) { - continue; - } - if (*(materials[i].info) != *materialInfo) { - continue; - } - bool match = true; - for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) { - match = match && (materials[i].textures[j] == textures[j]); - } - if (materials[i].userProperties.size() != userProperties.size()) { - match = false; - } - else { - for (int j = 0; match && j < userProperties.size(); j++) { - match = match && (materials[i].userProperties[j] == userProperties[j]); - } - } - if (match) { - return (int) i; - } + const std::vector& userProperties) { + for (size_t i = 0; i < materials.size(); i++) { + if (materials[i].name != name) { + continue; } - - RawMaterial material; - material.name = name; - material.type = materialType; - material.info = materialInfo; - material.userProperties = userProperties; - - for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) { - material.textures[i] = textures[i]; + if (materials[i].type != materialType) { + continue; } + if (*(materials[i].info) != *materialInfo) { + continue; + } + bool match = true; + for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) { + match = match && (materials[i].textures[j] == textures[j]); + } + if (materials[i].userProperties.size() != userProperties.size()) { + match = false; + } else { + for (int j = 0; match && j < userProperties.size(); j++) { + match = match && (materials[i].userProperties[j] == userProperties[j]); + } + } + if (match) { + return (int)i; + } + } - materials.emplace_back(material); + RawMaterial material; + material.name = name; + material.type = materialType; + material.info = materialInfo; + material.userProperties = userProperties; - return (int) materials.size() - 1; + for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) { + material.textures[i] = textures[i]; + } + + materials.emplace_back(material); + + return (int)materials.size() - 1; } int RawModel::AddLight( - const char *name, + const char* name, const RawLightType lightType, const Vec3f color, const float intensity, const float innerConeAngle, - const float outerConeAngle) -{ - for (size_t i = 0; i < lights.size(); i ++) { - if (lights[i].name != name || lights[i].type != lightType) { - continue; - } - // only care about cone angles for spot - if (lights[i].type == RAW_LIGHT_TYPE_SPOT) { - if (lights[i].innerConeAngle != innerConeAngle || - lights[i].outerConeAngle != outerConeAngle) { - continue; - } - } - return (int) i; + const float outerConeAngle) { + for (size_t i = 0; i < lights.size(); i++) { + if (lights[i].name != name || lights[i].type != lightType) { + continue; } - RawLight light { - name, - lightType, - color, - intensity, - innerConeAngle, - outerConeAngle, - }; - lights.push_back(light); - return (int) lights.size() - 1; + // only care about cone angles for spot + if (lights[i].type == RAW_LIGHT_TYPE_SPOT) { + if (lights[i].innerConeAngle != innerConeAngle || + lights[i].outerConeAngle != outerConeAngle) { + continue; + } + } + return (int)i; + } + RawLight light{ + name, + lightType, + color, + intensity, + innerConeAngle, + outerConeAngle, + }; + lights.push_back(light); + return (int)lights.size() - 1; } - -int RawModel::AddSurface(const RawSurface &surface) -{ - for (size_t i = 0; i < surfaces.size(); i++) { - if (StringUtils::CompareNoCase(surfaces[i].name, surface.name) == 0) { - return (int) i; - } +int RawModel::AddSurface(const RawSurface& surface) { + for (size_t i = 0; i < surfaces.size(); i++) { + if (StringUtils::CompareNoCase(surfaces[i].name, surface.name) == 0) { + return (int)i; } + } - surfaces.emplace_back(surface); - return (int) (surfaces.size() - 1); + surfaces.emplace_back(surface); + return (int)(surfaces.size() - 1); } -int RawModel::AddSurface(const char *name, const long surfaceId) -{ - assert(name[0] != '\0'); +int RawModel::AddSurface(const char* name, const long surfaceId) { + assert(name[0] != '\0'); - for (size_t i = 0; i < surfaces.size(); i++) { - if (surfaces[i].id == surfaceId) { - return (int) i; - } + for (size_t i = 0; i < surfaces.size(); i++) { + if (surfaces[i].id == surfaceId) { + return (int)i; } - RawSurface surface; - surface.id = surfaceId; - surface.name = name; - surface.bounds.Clear(); - surface.discrete = false; + } + RawSurface surface; + surface.id = surfaceId; + surface.name = name; + surface.bounds.Clear(); + surface.discrete = false; - surfaces.emplace_back(surface); - return (int) (surfaces.size() - 1); + surfaces.emplace_back(surface); + return (int)(surfaces.size() - 1); } -int RawModel::AddAnimation(const RawAnimation &animation) -{ - animations.emplace_back(animation); - return (int) (animations.size() - 1); +int RawModel::AddAnimation(const RawAnimation& animation) { + animations.emplace_back(animation); + return (int)(animations.size() - 1); } -int RawModel::AddNode(const RawNode &node) -{ - for (size_t i = 0; i < nodes.size(); i++) { - if (nodes[i].id == node.id) { - return (int)i; - } +int RawModel::AddNode(const RawNode& node) { + for (size_t i = 0; i < nodes.size(); i++) { + if (nodes[i].id == node.id) { + return (int)i; } + } - nodes.emplace_back(node); - return (int) nodes.size() - 1; + nodes.emplace_back(node); + return (int)nodes.size() - 1; } int RawModel::AddCameraPerspective( - const char *name, const long nodeId, const float aspectRatio, const float fovDegreesX, const float fovDegreesY, const float nearZ, - const float farZ) -{ - RawCamera camera; - camera.name = name; - camera.nodeId = nodeId; - camera.mode = RawCamera::CAMERA_MODE_PERSPECTIVE; - camera.perspective.aspectRatio = aspectRatio; - camera.perspective.fovDegreesX = fovDegreesX; - camera.perspective.fovDegreesY = fovDegreesY; - camera.perspective.nearZ = nearZ; - camera.perspective.farZ = farZ; - cameras.emplace_back(camera); - return (int) cameras.size() - 1; + const char* name, + const long nodeId, + const float aspectRatio, + const float fovDegreesX, + const float fovDegreesY, + const float nearZ, + const float farZ) { + RawCamera camera; + camera.name = name; + camera.nodeId = nodeId; + camera.mode = RawCamera::CAMERA_MODE_PERSPECTIVE; + camera.perspective.aspectRatio = aspectRatio; + camera.perspective.fovDegreesX = fovDegreesX; + camera.perspective.fovDegreesY = fovDegreesY; + camera.perspective.nearZ = nearZ; + camera.perspective.farZ = farZ; + cameras.emplace_back(camera); + return (int)cameras.size() - 1; } int RawModel::AddCameraOrthographic( - const char *name, const long nodeId, const float magX, const float magY, const float nearZ, const float farZ) -{ - RawCamera camera; - camera.name = name; - camera.nodeId = nodeId; - camera.mode = RawCamera::CAMERA_MODE_ORTHOGRAPHIC; - camera.orthographic.magX = magX; - camera.orthographic.magY = magY; - camera.orthographic.nearZ = nearZ; - camera.orthographic.farZ = farZ; - cameras.emplace_back(camera); - return (int) cameras.size() - 1; + const char* name, + const long nodeId, + const float magX, + const float magY, + const float nearZ, + const float farZ) { + RawCamera camera; + camera.name = name; + camera.nodeId = nodeId; + camera.mode = RawCamera::CAMERA_MODE_ORTHOGRAPHIC; + camera.orthographic.magX = magX; + camera.orthographic.magY = magY; + camera.orthographic.nearZ = nearZ; + camera.orthographic.farZ = farZ; + cameras.emplace_back(camera); + return (int)cameras.size() - 1; } -int RawModel::AddNode(const long id, const char *name, const long parentId) -{ - assert(name[0] != '\0'); +int RawModel::AddNode(const long id, const char* name, const long parentId) { + assert(name[0] != '\0'); - for (size_t i = 0; i < nodes.size(); i++) { - if (nodes[i].id == id ) { - return (int) i; - } + for (size_t i = 0; i < nodes.size(); i++) { + if (nodes[i].id == id) { + return (int)i; } + } - RawNode joint; - joint.isJoint = false; - joint.id = id; - joint.name = name; - joint.parentId = parentId; - joint.surfaceId = 0; - joint.lightIx = -1; - joint.translation = Vec3f(0, 0, 0); - joint.rotation = Quatf(0, 0, 0, 1); - joint.scale = Vec3f(1, 1, 1); + RawNode joint; + joint.isJoint = false; + joint.id = id; + joint.name = name; + joint.parentId = parentId; + joint.surfaceId = 0; + joint.lightIx = -1; + joint.translation = Vec3f(0, 0, 0); + joint.rotation = Quatf(0, 0, 0, 1); + joint.scale = Vec3f(1, 1, 1); - nodes.emplace_back(joint); - return (int) nodes.size() - 1; + nodes.emplace_back(joint); + return (int)nodes.size() - 1; } -void RawModel::Condense() -{ - // Only keep surfaces that are referenced by one or more triangles. - { - std::vector oldSurfaces = surfaces; +void RawModel::Condense() { + // Only keep surfaces that are referenced by one or more triangles. + { + std::vector oldSurfaces = surfaces; - surfaces.clear(); + surfaces.clear(); - std::set survivingSurfaceIds; - for (auto &triangle : triangles) { - const RawSurface &surface = oldSurfaces[triangle.surfaceIndex]; - const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id); - surfaces[surfaceIndex] = surface; - triangle.surfaceIndex = surfaceIndex; - survivingSurfaceIds.emplace(surface.id); - } - // clear out references to meshes that no longer exist - for (auto &node : nodes) { - if (node.surfaceId != 0 && survivingSurfaceIds.find(node.surfaceId) == survivingSurfaceIds.end()) { - node.surfaceId = 0; - } - } + std::set survivingSurfaceIds; + for (auto& triangle : triangles) { + const RawSurface& surface = oldSurfaces[triangle.surfaceIndex]; + const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id); + surfaces[surfaceIndex] = surface; + triangle.surfaceIndex = surfaceIndex; + survivingSurfaceIds.emplace(surface.id); } - - // Only keep materials that are referenced by one or more triangles. - { - std::vector oldMaterials = materials; - - materials.clear(); - - for (auto &triangle : triangles) { - const RawMaterial &material = oldMaterials[triangle.materialIndex]; - const int materialIndex = AddMaterial(material); - materials[materialIndex] = material; - triangle.materialIndex = materialIndex; - } + // clear out references to meshes that no longer exist + for (auto& node : nodes) { + if (node.surfaceId != 0 && + survivingSurfaceIds.find(node.surfaceId) == survivingSurfaceIds.end()) { + node.surfaceId = 0; + } } + } - // Only keep textures that are referenced by one or more materials. - { - std::vector oldTextures = textures; + // Only keep materials that are referenced by one or more triangles. + { + std::vector oldMaterials = materials; - textures.clear(); + materials.clear(); - for (auto &material : materials) { - for (int j = 0; j < RAW_TEXTURE_USAGE_MAX; j++) { - if (material.textures[j] >= 0) { - const RawTexture &texture = oldTextures[material.textures[j]]; - const int textureIndex = AddTexture(texture.name, texture.fileName, texture.fileLocation, texture.usage); - textures[textureIndex] = texture; - material.textures[j] = textureIndex; - } - } - } + for (auto& triangle : triangles) { + const RawMaterial& material = oldMaterials[triangle.materialIndex]; + const int materialIndex = AddMaterial(material); + materials[materialIndex] = material; + triangle.materialIndex = materialIndex; } + } - // Only keep vertices that are referenced by one or more triangles. - { - std::vector oldVertices = vertices; + // Only keep textures that are referenced by one or more materials. + { + std::vector oldTextures = textures; - vertexHash.clear(); - vertices.clear(); + textures.clear(); - for (auto &triangle : triangles) { - for (int j = 0; j < 3; j++) { - triangle.verts[j] = AddVertex(oldVertices[triangle.verts[j]]); - } + for (auto& material : materials) { + for (int j = 0; j < RAW_TEXTURE_USAGE_MAX; j++) { + if (material.textures[j] >= 0) { + const RawTexture& texture = oldTextures[material.textures[j]]; + const int textureIndex = + AddTexture(texture.name, texture.fileName, texture.fileLocation, texture.usage); + textures[textureIndex] = texture; + material.textures[j] = textureIndex; } + } } + } + + // Only keep vertices that are referenced by one or more triangles. + { + std::vector oldVertices = vertices; + + vertexHash.clear(); + vertices.clear(); + + for (auto& triangle : triangles) { + for (int j = 0; j < 3; j++) { + triangle.verts[j] = AddVertex(oldVertices[triangle.verts[j]]); + } + } + } } -void RawModel::TransformGeometry(ComputeNormalsOption normals) -{ - switch(normals) { - case ComputeNormalsOption::NEVER: - break; - case ComputeNormalsOption::MISSING: - if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { - break; - } - // otherwise fall through - case ComputeNormalsOption::BROKEN: - case ComputeNormalsOption::ALWAYS: - size_t computedNormalsCount = this->CalculateNormals(normals == ComputeNormalsOption::BROKEN); - vertexAttributes |= RAW_VERTEX_ATTRIBUTE_NORMAL; +void RawModel::TransformGeometry(ComputeNormalsOption normals) { + switch (normals) { + case ComputeNormalsOption::NEVER: + break; + case ComputeNormalsOption::MISSING: + if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_NORMAL) != 0) { + break; + } + // otherwise fall through + case ComputeNormalsOption::BROKEN: + case ComputeNormalsOption::ALWAYS: + size_t computedNormalsCount = this->CalculateNormals(normals == ComputeNormalsOption::BROKEN); + vertexAttributes |= RAW_VERTEX_ATTRIBUTE_NORMAL; - if (verboseOutput) { - if (normals == ComputeNormalsOption::BROKEN) { - if (computedNormalsCount > 0) { - fmt::printf("Repaired %lu empty normals.\n", computedNormalsCount); - } - } else { - fmt::printf("Computed %lu normals.\n", computedNormalsCount); - } - } - break; - } + if (verboseOutput) { + if (normals == ComputeNormalsOption::BROKEN) { + if (computedNormalsCount > 0) { + fmt::printf("Repaired %lu empty normals.\n", computedNormalsCount); + } + } else { + fmt::printf("Computed %lu normals.\n", computedNormalsCount); + } + } + break; + } } -void RawModel::TransformTextures(const std::vector> &transforms) -{ - for (auto &vertice : vertices) { - if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { - for (const auto &fun : transforms) { - vertice.uv0 = fun(vertice.uv0); - } - } - if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { - for (const auto &fun : transforms) { - vertice.uv1 = fun(vertice.uv1); - } - } +void RawModel::TransformTextures(const std::vector>& transforms) { + for (auto& vertice : vertices) { + if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_UV0) != 0) { + for (const auto& fun : transforms) { + vertice.uv0 = fun(vertice.uv0); + } } + if ((vertexAttributes & RAW_VERTEX_ATTRIBUTE_UV1) != 0) { + for (const auto& fun : transforms) { + vertice.uv1 = fun(vertice.uv1); + } + } + } } -struct TriangleModelSortPos -{ - static bool Compare(const RawTriangle &a, const RawTriangle &b) - { - if (a.materialIndex != b.materialIndex) { - return a.materialIndex < b.materialIndex; - } - if (a.surfaceIndex != b.surfaceIndex) { - return a.surfaceIndex < b.surfaceIndex; - } - return a.verts[0] < b.verts[0]; +struct TriangleModelSortPos { + static bool Compare(const RawTriangle& a, const RawTriangle& b) { + if (a.materialIndex != b.materialIndex) { + return a.materialIndex < b.materialIndex; } + if (a.surfaceIndex != b.surfaceIndex) { + return a.surfaceIndex < b.surfaceIndex; + } + return a.verts[0] < b.verts[0]; + } }; -struct TriangleModelSortNeg -{ - static bool Compare(const RawTriangle &a, const RawTriangle &b) - { - if (a.materialIndex != b.materialIndex) { - return a.materialIndex < b.materialIndex; - } - if (a.surfaceIndex != b.surfaceIndex) { - return a.surfaceIndex < b.surfaceIndex; - } - return a.verts[0] > b.verts[0]; +struct TriangleModelSortNeg { + static bool Compare(const RawTriangle& a, const RawTriangle& b) { + if (a.materialIndex != b.materialIndex) { + return a.materialIndex < b.materialIndex; } + if (a.surfaceIndex != b.surfaceIndex) { + return a.surfaceIndex < b.surfaceIndex; + } + return a.verts[0] > b.verts[0]; + } }; void RawModel::CreateMaterialModels( - std::vector &materialModels, bool shortIndices, const int keepAttribs, const bool forceDiscrete) const -{ - // Sort all triangles based on material first, then surface, then first vertex index. - std::vector sortedTriangles; + std::vector& materialModels, + bool shortIndices, + const int keepAttribs, + const bool forceDiscrete) const { + // Sort all triangles based on material first, then surface, then first vertex index. + std::vector sortedTriangles; - bool invertedTransparencySort = true; - if (invertedTransparencySort) { - // Split the triangles into opaque and transparent triangles. - std::vector opaqueTriangles; - std::vector transparentTriangles; - for (const auto &triangle : triangles) { - const int materialIndex = triangle.materialIndex; - if (materialIndex < 0) { - opaqueTriangles.push_back(triangle); - continue; - } - const int textureIndex = materials[materialIndex].textures[RAW_TEXTURE_USAGE_DIFFUSE]; - if (textureIndex < 0) { - if (vertices[triangle.verts[0]].color.w < 1.0f || - vertices[triangle.verts[1]].color.w < 1.0f || - vertices[triangle.verts[2]].color.w < 1.0f) { - transparentTriangles.push_back(triangle); - continue; - } - opaqueTriangles.push_back(triangle); - continue; - } - if (textures[textureIndex].occlusion == RAW_TEXTURE_OCCLUSION_TRANSPARENT) { - transparentTriangles.push_back(triangle); - } else { - opaqueTriangles.push_back(triangle); - } + bool invertedTransparencySort = true; + if (invertedTransparencySort) { + // Split the triangles into opaque and transparent triangles. + std::vector opaqueTriangles; + std::vector transparentTriangles; + for (const auto& triangle : triangles) { + const int materialIndex = triangle.materialIndex; + if (materialIndex < 0) { + opaqueTriangles.push_back(triangle); + continue; + } + const int textureIndex = materials[materialIndex].textures[RAW_TEXTURE_USAGE_DIFFUSE]; + if (textureIndex < 0) { + if (vertices[triangle.verts[0]].color.w < 1.0f || + vertices[triangle.verts[1]].color.w < 1.0f || + vertices[triangle.verts[2]].color.w < 1.0f) { + transparentTriangles.push_back(triangle); + continue; } - - // Sort the opaque triangles. - std::sort(opaqueTriangles.begin(), opaqueTriangles.end(), TriangleModelSortPos::Compare); - - // Sort the transparent triangles in the reverse direction. - std::sort(transparentTriangles.begin(), transparentTriangles.end(), TriangleModelSortNeg::Compare); - - // Add the triangles to the sorted list. - for (const auto &opaqueTriangle : opaqueTriangles) { - sortedTriangles.push_back(opaqueTriangle); - } - for (const auto &transparentTriangle : transparentTriangles) { - sortedTriangles.push_back(transparentTriangle); - } - } else { - sortedTriangles = triangles; - std::sort(sortedTriangles.begin(), sortedTriangles.end(), TriangleModelSortPos::Compare); + opaqueTriangles.push_back(triangle); + continue; + } + if (textures[textureIndex].occlusion == RAW_TEXTURE_OCCLUSION_TRANSPARENT) { + transparentTriangles.push_back(triangle); + } else { + opaqueTriangles.push_back(triangle); + } } - // Overestimate the number of models that will be created to avoid massive reallocation. - int discreteCount = 0; - for (const auto &surface : surfaces) { - discreteCount += surface.discrete ? 1 : 0; + // Sort the opaque triangles. + std::sort(opaqueTriangles.begin(), opaqueTriangles.end(), TriangleModelSortPos::Compare); + + // Sort the transparent triangles in the reverse direction. + std::sort( + transparentTriangles.begin(), transparentTriangles.end(), TriangleModelSortNeg::Compare); + + // Add the triangles to the sorted list. + for (const auto& opaqueTriangle : opaqueTriangles) { + sortedTriangles.push_back(opaqueTriangle); + } + for (const auto& transparentTriangle : transparentTriangles) { + sortedTriangles.push_back(transparentTriangle); + } + } else { + sortedTriangles = triangles; + std::sort(sortedTriangles.begin(), sortedTriangles.end(), TriangleModelSortPos::Compare); + } + + // Overestimate the number of models that will be created to avoid massive reallocation. + int discreteCount = 0; + for (const auto& surface : surfaces) { + discreteCount += surface.discrete ? 1 : 0; + } + + materialModels.clear(); + materialModels.reserve(materials.size() + discreteCount); + + const RawVertex defaultVertex; + + // Create a separate model for each material. + RawModel* model; + for (size_t i = 0; i < sortedTriangles.size(); i++) { + if (sortedTriangles[i].materialIndex < 0 || sortedTriangles[i].surfaceIndex < 0) { + continue; } - materialModels.clear(); - materialModels.reserve(materials.size() + discreteCount); - - const RawVertex defaultVertex; - - // Create a separate model for each material. - RawModel *model; - for (size_t i = 0; i < sortedTriangles.size(); i++) { - - if (sortedTriangles[i].materialIndex < 0 || sortedTriangles[i].surfaceIndex < 0) { - continue; - } - - if (i == 0 || - (shortIndices && model->GetVertexCount() >= 0xFFFE) || - sortedTriangles[i].materialIndex != sortedTriangles[i - 1].materialIndex || - (sortedTriangles[i].surfaceIndex != sortedTriangles[i - 1].surfaceIndex && - (forceDiscrete || surfaces[sortedTriangles[i].surfaceIndex].discrete || - surfaces[sortedTriangles[i - 1].surfaceIndex].discrete))) { - materialModels.resize(materialModels.size() + 1); - model = &materialModels[materialModels.size() - 1]; - } - - // FIXME: will have to unlink from the nodes, transform both surfaces into a - // common space, and reparent to a new node with appropriate transform. - - const int prevSurfaceCount = model->GetSurfaceCount(); - const int materialIndex = model->AddMaterial(materials[sortedTriangles[i].materialIndex]); - const int surfaceIndex = model->AddSurface(surfaces[sortedTriangles[i].surfaceIndex]); - RawSurface &rawSurface = model->GetSurface(surfaceIndex); - - if (model->GetSurfaceCount() > prevSurfaceCount) { - const std::vector &jointIds = surfaces[sortedTriangles[i].surfaceIndex].jointIds; - for (const auto &jointId : jointIds) { - const int nodeIndex = GetNodeById(jointId); - assert(nodeIndex != -1); - model->AddNode(GetNode(nodeIndex)); - } - rawSurface.bounds.Clear(); - } - - int verts[3]; - for (int j = 0; j < 3; j++) { - RawVertex vertex = vertices[sortedTriangles[i].verts[j]]; - - if (keepAttribs != -1) { - int keep = keepAttribs; - if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { - keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; - } - if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) { - keep |= RAW_VERTEX_ATTRIBUTE_POSITION; - - const RawMaterial &mat = model->GetMaterial(materialIndex); - if (mat.textures[RAW_TEXTURE_USAGE_DIFFUSE] != -1) { - keep |= RAW_VERTEX_ATTRIBUTE_UV0; - } - if (mat.textures[RAW_TEXTURE_USAGE_NORMAL] != -1) { - keep |= RAW_VERTEX_ATTRIBUTE_NORMAL | - RAW_VERTEX_ATTRIBUTE_TANGENT | - RAW_VERTEX_ATTRIBUTE_BINORMAL | - RAW_VERTEX_ATTRIBUTE_UV0; - } - if (mat.textures[RAW_TEXTURE_USAGE_SPECULAR] != -1) { - keep |= RAW_VERTEX_ATTRIBUTE_NORMAL | - RAW_VERTEX_ATTRIBUTE_UV0; - } - if (mat.textures[RAW_TEXTURE_USAGE_EMISSIVE] != -1) { - keep |= RAW_VERTEX_ATTRIBUTE_UV1; - } - } - if ((keep & RAW_VERTEX_ATTRIBUTE_POSITION) == 0) { vertex.position = defaultVertex.position; } - if ((keep & RAW_VERTEX_ATTRIBUTE_NORMAL) == 0) { vertex.normal = defaultVertex.normal; } - if ((keep & RAW_VERTEX_ATTRIBUTE_TANGENT) == 0) { vertex.tangent = defaultVertex.tangent; } - if ((keep & RAW_VERTEX_ATTRIBUTE_BINORMAL) == 0) { vertex.binormal = defaultVertex.binormal; } - if ((keep & RAW_VERTEX_ATTRIBUTE_COLOR) == 0) { vertex.color = defaultVertex.color; } - if ((keep & RAW_VERTEX_ATTRIBUTE_UV0) == 0) { vertex.uv0 = defaultVertex.uv0; } - if ((keep & RAW_VERTEX_ATTRIBUTE_UV1) == 0) { vertex.uv1 = defaultVertex.uv1; } - if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) == 0) { vertex.jointIndices = defaultVertex.jointIndices; } - if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) == 0) { vertex.jointWeights = defaultVertex.jointWeights; } - } - - verts[j] = model->AddVertex(vertex); - model->vertexAttributes |= vertex.Difference(defaultVertex); - - rawSurface.bounds.AddPoint(vertex.position); - } - - model->AddTriangle(verts[0], verts[1], verts[2], materialIndex, surfaceIndex); + if (i == 0 || (shortIndices && model->GetVertexCount() >= 0xFFFE) || + sortedTriangles[i].materialIndex != sortedTriangles[i - 1].materialIndex || + (sortedTriangles[i].surfaceIndex != sortedTriangles[i - 1].surfaceIndex && + (forceDiscrete || surfaces[sortedTriangles[i].surfaceIndex].discrete || + surfaces[sortedTriangles[i - 1].surfaceIndex].discrete))) { + materialModels.resize(materialModels.size() + 1); + model = &materialModels[materialModels.size() - 1]; } + + // FIXME: will have to unlink from the nodes, transform both surfaces into a + // common space, and reparent to a new node with appropriate transform. + + const int prevSurfaceCount = model->GetSurfaceCount(); + const int materialIndex = model->AddMaterial(materials[sortedTriangles[i].materialIndex]); + const int surfaceIndex = model->AddSurface(surfaces[sortedTriangles[i].surfaceIndex]); + RawSurface& rawSurface = model->GetSurface(surfaceIndex); + + if (model->GetSurfaceCount() > prevSurfaceCount) { + const std::vector& jointIds = surfaces[sortedTriangles[i].surfaceIndex].jointIds; + for (const auto& jointId : jointIds) { + const int nodeIndex = GetNodeById(jointId); + assert(nodeIndex != -1); + model->AddNode(GetNode(nodeIndex)); + } + rawSurface.bounds.Clear(); + } + + int verts[3]; + for (int j = 0; j < 3; j++) { + RawVertex vertex = vertices[sortedTriangles[i].verts[j]]; + + if (keepAttribs != -1) { + int keep = keepAttribs; + if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_POSITION) != 0) { + keep |= RAW_VERTEX_ATTRIBUTE_JOINT_INDICES | RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS; + } + if ((keepAttribs & RAW_VERTEX_ATTRIBUTE_AUTO) != 0) { + keep |= RAW_VERTEX_ATTRIBUTE_POSITION; + + const RawMaterial& mat = model->GetMaterial(materialIndex); + if (mat.textures[RAW_TEXTURE_USAGE_DIFFUSE] != -1) { + keep |= RAW_VERTEX_ATTRIBUTE_UV0; + } + if (mat.textures[RAW_TEXTURE_USAGE_NORMAL] != -1) { + keep |= RAW_VERTEX_ATTRIBUTE_NORMAL | RAW_VERTEX_ATTRIBUTE_TANGENT | + RAW_VERTEX_ATTRIBUTE_BINORMAL | RAW_VERTEX_ATTRIBUTE_UV0; + } + if (mat.textures[RAW_TEXTURE_USAGE_SPECULAR] != -1) { + keep |= RAW_VERTEX_ATTRIBUTE_NORMAL | RAW_VERTEX_ATTRIBUTE_UV0; + } + if (mat.textures[RAW_TEXTURE_USAGE_EMISSIVE] != -1) { + keep |= RAW_VERTEX_ATTRIBUTE_UV1; + } + } + if ((keep & RAW_VERTEX_ATTRIBUTE_POSITION) == 0) { + vertex.position = defaultVertex.position; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_NORMAL) == 0) { + vertex.normal = defaultVertex.normal; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_TANGENT) == 0) { + vertex.tangent = defaultVertex.tangent; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_BINORMAL) == 0) { + vertex.binormal = defaultVertex.binormal; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_COLOR) == 0) { + vertex.color = defaultVertex.color; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_UV0) == 0) { + vertex.uv0 = defaultVertex.uv0; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_UV1) == 0) { + vertex.uv1 = defaultVertex.uv1; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_INDICES) == 0) { + vertex.jointIndices = defaultVertex.jointIndices; + } + if ((keep & RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS) == 0) { + vertex.jointWeights = defaultVertex.jointWeights; + } + } + + verts[j] = model->AddVertex(vertex); + model->vertexAttributes |= vertex.Difference(defaultVertex); + + rawSurface.bounds.AddPoint(vertex.position); + } + + model->AddTriangle(verts[0], verts[1], verts[2], materialIndex, surfaceIndex); + } } -int RawModel::GetNodeById(const long nodeId) const -{ - for (size_t i = 0; i < nodes.size(); i++) { - if (nodes[i].id == nodeId) { - return (int) i; - } +int RawModel::GetNodeById(const long nodeId) const { + for (size_t i = 0; i < nodes.size(); i++) { + if (nodes[i].id == nodeId) { + return (int)i; } - return -1; + } + return -1; } -int RawModel::GetSurfaceById(const long surfaceId) const -{ - for (size_t i = 0; i < surfaces.size(); i++) { - if (surfaces[i].id == surfaceId) { - return (int)i; - } +int RawModel::GetSurfaceById(const long surfaceId) const { + for (size_t i = 0; i < surfaces.size(); i++) { + if (surfaces[i].id == surfaceId) { + return (int)i; } - return -1; + } + return -1; } -Vec3f RawModel::getFaceNormal(int verts[3]) const -{ - const float l0 = (vertices[verts[1]].position - vertices[verts[0]].position ).LengthSquared(); - const float l1 = (vertices[verts[2]].position - vertices[verts[1]].position ).LengthSquared(); - const float l2 = (vertices[verts[0]].position - vertices[verts[2]].position ).LengthSquared(); - const int index = ( l0 > l1 ) ? ( l0 > l2 ? 2 : 1 ) : ( l1 > l2 ? 0 : 1 ); +Vec3f RawModel::getFaceNormal(int verts[3]) const { + const float l0 = (vertices[verts[1]].position - vertices[verts[0]].position).LengthSquared(); + const float l1 = (vertices[verts[2]].position - vertices[verts[1]].position).LengthSquared(); + const float l2 = (vertices[verts[0]].position - vertices[verts[2]].position).LengthSquared(); + const int index = (l0 > l1) ? (l0 > l2 ? 2 : 1) : (l1 > l2 ? 0 : 1); - const Vec3f e0 = vertices[verts[(index + 1) % 3]].position - vertices[verts[index]].position; - const Vec3f e1 = vertices[verts[(index + 2) % 3]].position - vertices[verts[index]].position; - if (e0.LengthSquared() < FLT_MIN || e1.LengthSquared() < FLT_MIN) { - return Vec3f { 0.0f }; - } - auto result = Vec3f::CrossProduct(e0, e1); - auto resultLengthSquared = result.LengthSquared(); - if (resultLengthSquared < FLT_MIN) { - return Vec3f { 0.0f }; - } - float edgeDot = std::max(-1.0f, std::min(1.0f, Vec3f::DotProduct(e0, e1))); - float angle = acos(edgeDot); - float area = resultLengthSquared / 2.0f; - return result.Normalized() * angle * area; + const Vec3f e0 = vertices[verts[(index + 1) % 3]].position - vertices[verts[index]].position; + const Vec3f e1 = vertices[verts[(index + 2) % 3]].position - vertices[verts[index]].position; + if (e0.LengthSquared() < FLT_MIN || e1.LengthSquared() < FLT_MIN) { + return Vec3f{0.0f}; + } + auto result = Vec3f::CrossProduct(e0, e1); + auto resultLengthSquared = result.LengthSquared(); + if (resultLengthSquared < FLT_MIN) { + return Vec3f{0.0f}; + } + float edgeDot = std::max(-1.0f, std::min(1.0f, Vec3f::DotProduct(e0, e1))); + float angle = acos(edgeDot); + float area = resultLengthSquared / 2.0f; + return result.Normalized() * angle * area; } -size_t RawModel::CalculateNormals(bool onlyBroken) -{ - Vec3f averagePos = Vec3f { 0.0f }; - std::set brokenVerts; - for (int vertIx = 0; vertIx < vertices.size(); vertIx ++) { - RawVertex &vertex = vertices[vertIx]; - averagePos += (vertex.position / (float)vertices.size()); - if (onlyBroken && (vertex.normal.LengthSquared() >= FLT_MIN)) { - continue; - } - vertex.normal = Vec3f { 0.0f }; - if (onlyBroken) { - brokenVerts.emplace(vertIx); - } +size_t RawModel::CalculateNormals(bool onlyBroken) { + Vec3f averagePos = Vec3f{0.0f}; + std::set brokenVerts; + for (int vertIx = 0; vertIx < vertices.size(); vertIx++) { + RawVertex& vertex = vertices[vertIx]; + averagePos += (vertex.position / (float)vertices.size()); + if (onlyBroken && (vertex.normal.LengthSquared() >= FLT_MIN)) { + continue; } + vertex.normal = Vec3f{0.0f}; + if (onlyBroken) { + brokenVerts.emplace(vertIx); + } + } - for (auto &triangle : triangles) { - bool relevant = false; - for (int vertIx : triangle.verts) { - relevant |= (brokenVerts.count(vertIx) > 0); - } - if (!relevant) { - continue; - } - Vec3f faceNormal = this->getFaceNormal(triangle.verts); - for (int vertIx : triangle.verts) { - if (!onlyBroken || brokenVerts.count(vertIx) > 0) { - vertices[vertIx].normal += faceNormal; - } - } + for (auto& triangle : triangles) { + bool relevant = false; + for (int vertIx : triangle.verts) { + relevant |= (brokenVerts.count(vertIx) > 0); } + if (!relevant) { + continue; + } + Vec3f faceNormal = this->getFaceNormal(triangle.verts); + for (int vertIx : triangle.verts) { + if (!onlyBroken || brokenVerts.count(vertIx) > 0) { + vertices[vertIx].normal += faceNormal; + } + } + } - for (int vertIx = 0; vertIx < vertices.size(); vertIx ++) { - if (onlyBroken && brokenVerts.count(vertIx) == 0) { - continue; - } - RawVertex &vertex = vertices[vertIx]; - if (vertex.normal.LengthSquared() < FLT_MIN) { - vertex.normal = vertex.position - averagePos; - if (vertex.normal.LengthSquared() < FLT_MIN) { - vertex.normal = Vec3f { 0.0f, 1.0f, 0.0f }; - continue; - } - } - vertex.normal.Normalize(); + for (int vertIx = 0; vertIx < vertices.size(); vertIx++) { + if (onlyBroken && brokenVerts.count(vertIx) == 0) { + continue; } - return onlyBroken ? brokenVerts.size() : vertices.size(); + RawVertex& vertex = vertices[vertIx]; + if (vertex.normal.LengthSquared() < FLT_MIN) { + vertex.normal = vertex.position - averagePos; + if (vertex.normal.LengthSquared() < FLT_MIN) { + vertex.normal = Vec3f{0.0f, 1.0f, 0.0f}; + continue; + } + } + vertex.normal.Normalize(); + } + return onlyBroken ? brokenVerts.size() : vertices.size(); } diff --git a/src/raw/RawModel.hpp b/src/raw/RawModel.hpp index 2ef07f1..df134fa 100644 --- a/src/raw/RawModel.hpp +++ b/src/raw/RawModel.hpp @@ -9,481 +9,540 @@ #pragma once -#include #include #include +#include #include "FBX2glTF.h" +enum RawVertexAttribute { + RAW_VERTEX_ATTRIBUTE_POSITION = 1 << 0, + RAW_VERTEX_ATTRIBUTE_NORMAL = 1 << 1, + RAW_VERTEX_ATTRIBUTE_TANGENT = 1 << 2, + RAW_VERTEX_ATTRIBUTE_BINORMAL = 1 << 3, + RAW_VERTEX_ATTRIBUTE_COLOR = 1 << 4, + RAW_VERTEX_ATTRIBUTE_UV0 = 1 << 5, + RAW_VERTEX_ATTRIBUTE_UV1 = 1 << 6, + RAW_VERTEX_ATTRIBUTE_JOINT_INDICES = 1 << 7, + RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS = 1 << 8, -enum RawVertexAttribute -{ - RAW_VERTEX_ATTRIBUTE_POSITION = 1 << 0, - RAW_VERTEX_ATTRIBUTE_NORMAL = 1 << 1, - RAW_VERTEX_ATTRIBUTE_TANGENT = 1 << 2, - RAW_VERTEX_ATTRIBUTE_BINORMAL = 1 << 3, - RAW_VERTEX_ATTRIBUTE_COLOR = 1 << 4, - RAW_VERTEX_ATTRIBUTE_UV0 = 1 << 5, - RAW_VERTEX_ATTRIBUTE_UV1 = 1 << 6, - RAW_VERTEX_ATTRIBUTE_JOINT_INDICES = 1 << 7, - RAW_VERTEX_ATTRIBUTE_JOINT_WEIGHTS = 1 << 8, - - RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31 + RAW_VERTEX_ATTRIBUTE_AUTO = 1 << 31 }; -struct RawBlendVertex -{ - Vec3f position {}; - Vec3f normal {}; - Vec4f tangent {}; +struct RawBlendVertex { + Vec3f position{}; + Vec3f normal{}; + Vec4f tangent{}; - bool operator==(const RawBlendVertex &other) const { - return position == other.position && - normal == other.normal && - tangent == other.tangent; - } + bool operator==(const RawBlendVertex& other) const { + return position == other.position && normal == other.normal && tangent == other.tangent; + } }; -struct RawVertex -{ - RawVertex() : - polarityUv0(false), - pad1(false), - pad2(false), - pad3(false) {} +struct RawVertex { + RawVertex() : polarityUv0(false), pad1(false), pad2(false), pad3(false) {} - Vec3f position { 0.0f }; - Vec3f normal { 0.0f }; - Vec3f binormal { 0.0f }; - Vec4f tangent { 0.0f }; - Vec4f color { 0.0f }; - Vec2f uv0 { 0.0f }; - Vec2f uv1 { 0.0f }; - Vec4i jointIndices { 0, 0, 0, 0 }; - Vec4f jointWeights { 0.0f }; - // end of members that directly correspond to vertex attributes + Vec3f position{0.0f}; + Vec3f normal{0.0f}; + Vec3f binormal{0.0f}; + Vec4f tangent{0.0f}; + Vec4f color{0.0f}; + Vec2f uv0{0.0f}; + Vec2f uv1{0.0f}; + Vec4i jointIndices{0, 0, 0, 0}; + Vec4f jointWeights{0.0f}; + // end of members that directly correspond to vertex attributes - // if this vertex participates in a blend shape setup, the surfaceIx of its dedicated mesh; otherwise, -1 - int blendSurfaceIx = -1; - // the size of this vector is always identical to the size of the corresponding RawSurface.blendChannels - std::vector blends { }; + // if this vertex participates in a blend shape setup, the surfaceIx of its dedicated mesh; + // otherwise, -1 + int blendSurfaceIx = -1; + // the size of this vector is always identical to the size of the corresponding + // RawSurface.blendChannels + std::vector blends{}; - bool polarityUv0; - bool pad1; - bool pad2; - bool pad3; + bool polarityUv0; + bool pad1; + bool pad2; + bool pad3; - bool operator==(const RawVertex &other) const; - size_t Difference(const RawVertex &other) const; + bool operator==(const RawVertex& other) const; + size_t Difference(const RawVertex& other) const; }; -class VertexHasher -{ -public: - size_t operator()(const RawVertex &v) const - { - size_t seed = 5381; - const auto hasher = std::hash{}; - seed ^= hasher(v.position[0]) + 0x9e3779b9 + (seed<<6) + (seed>>2); - seed ^= hasher(v.position[1]) + 0x9e3779b9 + (seed<<6) + (seed>>2); - seed ^= hasher(v.position[2]) + 0x9e3779b9 + (seed<<6) + (seed>>2); - return seed; - } +class VertexHasher { + public: + size_t operator()(const RawVertex& v) const { + size_t seed = 5381; + const auto hasher = std::hash{}; + seed ^= hasher(v.position[0]) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= hasher(v.position[1]) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + seed ^= hasher(v.position[2]) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + return seed; + } }; -struct RawTriangle -{ - int verts[3]; - int materialIndex; - int surfaceIndex; +struct RawTriangle { + int verts[3]; + int materialIndex; + int surfaceIndex; }; -enum RawShadingModel -{ - RAW_SHADING_MODEL_UNKNOWN = -1, - RAW_SHADING_MODEL_CONSTANT, - RAW_SHADING_MODEL_LAMBERT, - RAW_SHADING_MODEL_BLINN, - RAW_SHADING_MODEL_PHONG, - RAW_SHADING_MODEL_PBR_MET_ROUGH, - RAW_SHADING_MODEL_MAX +enum RawShadingModel { + RAW_SHADING_MODEL_UNKNOWN = -1, + RAW_SHADING_MODEL_CONSTANT, + RAW_SHADING_MODEL_LAMBERT, + RAW_SHADING_MODEL_BLINN, + RAW_SHADING_MODEL_PHONG, + RAW_SHADING_MODEL_PBR_MET_ROUGH, + RAW_SHADING_MODEL_MAX }; inline std::string Describe(RawShadingModel model) { - switch(model) { - case RAW_SHADING_MODEL_UNKNOWN: return ""; - case RAW_SHADING_MODEL_CONSTANT: return "Constant"; - case RAW_SHADING_MODEL_LAMBERT: return "Lambert"; - case RAW_SHADING_MODEL_BLINN: return "Blinn"; - case RAW_SHADING_MODEL_PHONG: return "Phong"; - case RAW_SHADING_MODEL_PBR_MET_ROUGH: return "Metallic/Roughness"; - case RAW_SHADING_MODEL_MAX: default: return ""; - } + switch (model) { + case RAW_SHADING_MODEL_UNKNOWN: + return ""; + case RAW_SHADING_MODEL_CONSTANT: + return "Constant"; + case RAW_SHADING_MODEL_LAMBERT: + return "Lambert"; + case RAW_SHADING_MODEL_BLINN: + return "Blinn"; + case RAW_SHADING_MODEL_PHONG: + return "Phong"; + case RAW_SHADING_MODEL_PBR_MET_ROUGH: + return "Metallic/Roughness"; + case RAW_SHADING_MODEL_MAX: + default: + return ""; + } } -enum RawTextureUsage -{ - RAW_TEXTURE_USAGE_NONE = -1, - RAW_TEXTURE_USAGE_AMBIENT, - RAW_TEXTURE_USAGE_DIFFUSE, - RAW_TEXTURE_USAGE_NORMAL, - RAW_TEXTURE_USAGE_SPECULAR, - RAW_TEXTURE_USAGE_SHININESS, - RAW_TEXTURE_USAGE_EMISSIVE, - RAW_TEXTURE_USAGE_REFLECTION, - RAW_TEXTURE_USAGE_ALBEDO, - RAW_TEXTURE_USAGE_OCCLUSION, - RAW_TEXTURE_USAGE_ROUGHNESS, - RAW_TEXTURE_USAGE_METALLIC, - RAW_TEXTURE_USAGE_MAX +enum RawTextureUsage { + RAW_TEXTURE_USAGE_NONE = -1, + RAW_TEXTURE_USAGE_AMBIENT, + RAW_TEXTURE_USAGE_DIFFUSE, + RAW_TEXTURE_USAGE_NORMAL, + RAW_TEXTURE_USAGE_SPECULAR, + RAW_TEXTURE_USAGE_SHININESS, + RAW_TEXTURE_USAGE_EMISSIVE, + RAW_TEXTURE_USAGE_REFLECTION, + RAW_TEXTURE_USAGE_ALBEDO, + RAW_TEXTURE_USAGE_OCCLUSION, + RAW_TEXTURE_USAGE_ROUGHNESS, + RAW_TEXTURE_USAGE_METALLIC, + RAW_TEXTURE_USAGE_MAX }; -inline std::string Describe(RawTextureUsage usage) -{ - switch (usage) { - case RAW_TEXTURE_USAGE_NONE: return ""; - case RAW_TEXTURE_USAGE_AMBIENT: return "ambient"; - case RAW_TEXTURE_USAGE_DIFFUSE: return "diffuse"; - case RAW_TEXTURE_USAGE_NORMAL: return "normal"; - case RAW_TEXTURE_USAGE_SPECULAR: return "specular"; - case RAW_TEXTURE_USAGE_SHININESS: return "shininess"; - case RAW_TEXTURE_USAGE_EMISSIVE: return "emissive"; - case RAW_TEXTURE_USAGE_REFLECTION: return "reflection"; - case RAW_TEXTURE_USAGE_OCCLUSION: return "occlusion"; - case RAW_TEXTURE_USAGE_ROUGHNESS: return "roughness"; - case RAW_TEXTURE_USAGE_METALLIC: return "metallic"; - case RAW_TEXTURE_USAGE_MAX:default: return "unknown"; - } +inline std::string Describe(RawTextureUsage usage) { + switch (usage) { + case RAW_TEXTURE_USAGE_NONE: + return ""; + case RAW_TEXTURE_USAGE_AMBIENT: + return "ambient"; + case RAW_TEXTURE_USAGE_DIFFUSE: + return "diffuse"; + case RAW_TEXTURE_USAGE_NORMAL: + return "normal"; + case RAW_TEXTURE_USAGE_SPECULAR: + return "specular"; + case RAW_TEXTURE_USAGE_SHININESS: + return "shininess"; + case RAW_TEXTURE_USAGE_EMISSIVE: + return "emissive"; + case RAW_TEXTURE_USAGE_REFLECTION: + return "reflection"; + case RAW_TEXTURE_USAGE_OCCLUSION: + return "occlusion"; + case RAW_TEXTURE_USAGE_ROUGHNESS: + return "roughness"; + case RAW_TEXTURE_USAGE_METALLIC: + return "metallic"; + case RAW_TEXTURE_USAGE_MAX: + default: + return "unknown"; + } }; -enum RawTextureOcclusion -{ - RAW_TEXTURE_OCCLUSION_OPAQUE, - RAW_TEXTURE_OCCLUSION_TRANSPARENT +enum RawTextureOcclusion { RAW_TEXTURE_OCCLUSION_OPAQUE, RAW_TEXTURE_OCCLUSION_TRANSPARENT }; + +struct RawTexture { + std::string name; // logical name in FBX file + int width; + int height; + int mipLevels; + RawTextureUsage usage; + RawTextureOcclusion occlusion; + std::string fileName; // original filename in FBX file + std::string fileLocation; // inferred path in local filesystem, or "" }; -struct RawTexture -{ - std::string name; // logical name in FBX file - int width; - int height; - int mipLevels; - RawTextureUsage usage; - RawTextureOcclusion occlusion; - std::string fileName; // original filename in FBX file - std::string fileLocation; // inferred path in local filesystem, or "" -}; - -enum RawMaterialType -{ - RAW_MATERIAL_TYPE_OPAQUE, - RAW_MATERIAL_TYPE_TRANSPARENT, - RAW_MATERIAL_TYPE_SKINNED_OPAQUE, - RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT, +enum RawMaterialType { + RAW_MATERIAL_TYPE_OPAQUE, + RAW_MATERIAL_TYPE_TRANSPARENT, + RAW_MATERIAL_TYPE_SKINNED_OPAQUE, + RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT, }; struct RawMatProps { - explicit RawMatProps(RawShadingModel shadingModel) - : shadingModel(shadingModel) - {} - const RawShadingModel shadingModel; + explicit RawMatProps(RawShadingModel shadingModel) : shadingModel(shadingModel) {} + const RawShadingModel shadingModel; - virtual bool operator!=(const RawMatProps &other) const { return !(*this == other); } - virtual bool operator==(const RawMatProps &other) const { return shadingModel == other.shadingModel; }; + virtual bool operator!=(const RawMatProps& other) const { + return !(*this == other); + } + virtual bool operator==(const RawMatProps& other) const { + return shadingModel == other.shadingModel; + }; }; struct RawTraditionalMatProps : RawMatProps { - RawTraditionalMatProps( - RawShadingModel shadingModel, - const Vec3f &&ambientFactor, - const Vec4f &&diffuseFactor, - const Vec3f &&emissiveFactor, - const Vec3f &&specularFactor, - const float shininess - ) : RawMatProps(shadingModel), - ambientFactor(ambientFactor), - diffuseFactor(diffuseFactor), - emissiveFactor(emissiveFactor), - specularFactor(specularFactor), - shininess(shininess) - {} + RawTraditionalMatProps( + RawShadingModel shadingModel, + const Vec3f&& ambientFactor, + const Vec4f&& diffuseFactor, + const Vec3f&& emissiveFactor, + const Vec3f&& specularFactor, + const float shininess) + : RawMatProps(shadingModel), + ambientFactor(ambientFactor), + diffuseFactor(diffuseFactor), + emissiveFactor(emissiveFactor), + specularFactor(specularFactor), + shininess(shininess) {} - const Vec3f ambientFactor; - const Vec4f diffuseFactor; - const Vec3f emissiveFactor; - const Vec3f specularFactor; - const float shininess; + const Vec3f ambientFactor; + const Vec4f diffuseFactor; + const Vec3f emissiveFactor; + const Vec3f specularFactor; + const float shininess; - bool operator==(const RawMatProps &other) const override { - if (RawMatProps::operator==(other)) { - const auto &typed = (RawTraditionalMatProps &) other; - return ambientFactor == typed.ambientFactor && - diffuseFactor == typed.diffuseFactor && - specularFactor == typed.specularFactor && - emissiveFactor == typed.emissiveFactor && - shininess == typed.shininess; - } - return false; + bool operator==(const RawMatProps& other) const override { + if (RawMatProps::operator==(other)) { + const auto& typed = (RawTraditionalMatProps&)other; + return ambientFactor == typed.ambientFactor && diffuseFactor == typed.diffuseFactor && + specularFactor == typed.specularFactor && emissiveFactor == typed.emissiveFactor && + shininess == typed.shininess; } + return false; + } }; struct RawMetRoughMatProps : RawMatProps { - RawMetRoughMatProps( - RawShadingModel shadingModel, - const Vec4f &&diffuseFactor, - const Vec3f &&emissiveFactor, - float emissiveIntensity, - float metallic, - float roughness - ) : RawMatProps(shadingModel), - diffuseFactor(diffuseFactor), - emissiveFactor(emissiveFactor), - emissiveIntensity(emissiveIntensity), - metallic(metallic), - roughness(roughness) - {} - const Vec4f diffuseFactor; - const Vec3f emissiveFactor; - const float emissiveIntensity; - const float metallic; - const float roughness; + RawMetRoughMatProps( + RawShadingModel shadingModel, + const Vec4f&& diffuseFactor, + const Vec3f&& emissiveFactor, + float emissiveIntensity, + float metallic, + float roughness) + : RawMatProps(shadingModel), + diffuseFactor(diffuseFactor), + emissiveFactor(emissiveFactor), + emissiveIntensity(emissiveIntensity), + metallic(metallic), + roughness(roughness) {} + const Vec4f diffuseFactor; + const Vec3f emissiveFactor; + const float emissiveIntensity; + const float metallic; + const float roughness; - bool operator==(const RawMatProps &other) const override { - if (RawMatProps::operator==(other)) { - const auto &typed = (RawMetRoughMatProps &) other; - return diffuseFactor == typed.diffuseFactor && - emissiveFactor == typed.emissiveFactor && - emissiveIntensity == typed.emissiveIntensity && - metallic == typed.metallic && - roughness == typed.roughness; - } - return false; + bool operator==(const RawMatProps& other) const override { + if (RawMatProps::operator==(other)) { + const auto& typed = (RawMetRoughMatProps&)other; + return diffuseFactor == typed.diffuseFactor && emissiveFactor == typed.emissiveFactor && + emissiveIntensity == typed.emissiveIntensity && metallic == typed.metallic && + roughness == typed.roughness; } + return false; + } }; - -struct RawMaterial -{ - std::string name; - RawMaterialType type; - std::shared_ptr info; - int textures[RAW_TEXTURE_USAGE_MAX]; - std::vector userProperties; +struct RawMaterial { + std::string name; + RawMaterialType type; + std::shared_ptr info; + int textures[RAW_TEXTURE_USAGE_MAX]; + std::vector userProperties; }; -enum RawLightType -{ - RAW_LIGHT_TYPE_DIRECTIONAL, - RAW_LIGHT_TYPE_POINT, - RAW_LIGHT_TYPE_SPOT, +enum RawLightType { + RAW_LIGHT_TYPE_DIRECTIONAL, + RAW_LIGHT_TYPE_POINT, + RAW_LIGHT_TYPE_SPOT, }; -struct RawLight -{ - std::string name; - RawLightType type; - Vec3f color; - float intensity; - float innerConeAngle; // only meaningful for spot - float outerConeAngle; // only meaningful for spot +struct RawLight { + std::string name; + RawLightType type; + Vec3f color; + float intensity; + float innerConeAngle; // only meaningful for spot + float outerConeAngle; // only meaningful for spot }; -struct RawBlendChannel -{ - float defaultDeform; - bool hasNormals; - bool hasTangents; - std::string name; +struct RawBlendChannel { + float defaultDeform; + bool hasNormals; + bool hasTangents; + std::string name; }; -struct RawSurface -{ - long id; - std::string name; // The name of this surface - long skeletonRootId; // The id of the root node of the skeleton. - Bounds bounds; - std::vector jointIds; - std::vector jointGeometryMins; - std::vector jointGeometryMaxs; - std::vector inverseBindMatrices; - std::vector blendChannels; - bool discrete; +struct RawSurface { + long id; + std::string name; // The name of this surface + long skeletonRootId; // The id of the root node of the skeleton. + Bounds bounds; + std::vector jointIds; + std::vector jointGeometryMins; + std::vector jointGeometryMaxs; + std::vector inverseBindMatrices; + std::vector blendChannels; + bool discrete; }; -struct RawChannel -{ - int nodeIndex; - std::vector translations; - std::vector rotations; - std::vector scales; - std::vector weights; +struct RawChannel { + int nodeIndex; + std::vector translations; + std::vector rotations; + std::vector scales; + std::vector weights; }; -struct RawAnimation -{ - std::string name; - std::vector times; - std::vector channels; +struct RawAnimation { + std::string name; + std::vector times; + std::vector channels; }; -struct RawCamera -{ - std::string name; - long nodeId; +struct RawCamera { + std::string name; + long nodeId; - enum - { - CAMERA_MODE_PERSPECTIVE, - CAMERA_MODE_ORTHOGRAPHIC - } mode; + enum { CAMERA_MODE_PERSPECTIVE, CAMERA_MODE_ORTHOGRAPHIC } mode; - struct - { - float aspectRatio; - float fovDegreesX; - float fovDegreesY; - float nearZ; - float farZ; - } perspective; + struct { + float aspectRatio; + float fovDegreesX; + float fovDegreesY; + float nearZ; + float farZ; + } perspective; - struct - { - float magX; - float magY; - float nearZ; - float farZ; - } orthographic; + struct { + float magX; + float magY; + float nearZ; + float farZ; + } orthographic; }; -struct RawNode -{ - bool isJoint; - long id; - std::string name; - long parentId; - std::vector childIds; - Vec3f translation; - Quatf rotation; - Vec3f scale; - long surfaceId; - long lightIx; - std::vector userProperties; +struct RawNode { + bool isJoint; + long id; + std::string name; + long parentId; + std::vector childIds; + Vec3f translation; + Quatf rotation; + Vec3f scale; + long surfaceId; + long lightIx; + std::vector userProperties; }; -class RawModel -{ -public: - RawModel(); +class RawModel { + public: + RawModel(); - // Add geometry. - void AddVertexAttribute(const RawVertexAttribute attrib); - int AddVertex(const RawVertex &vertex); - int AddTriangle(const int v0, const int v1, const int v2, const int materialIndex, const int surfaceIndex); - int AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage); - int AddMaterial(const RawMaterial &material); - int AddMaterial( - const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX], - std::shared_ptr materialInfo, const std::vector& userProperties); - int AddLight(const char *name, RawLightType lightType, Vec3f color, float intensity, - float innerConeAngle, float outerConeAngle); - int AddSurface(const RawSurface &suface); - int AddSurface(const char *name, long surfaceId); - int AddAnimation(const RawAnimation &animation); - int AddCameraPerspective( - const char *name, const long nodeId, const float aspectRatio, const float fovDegreesX, const float fovDegreesY, - const float nearZ, const float farZ); - int - AddCameraOrthographic(const char *name, const long nodeId, const float magX, const float magY, const float nearZ, const float farZ); - int AddNode(const RawNode &node); - int AddNode(const long id, const char *name, const long parentId); - void SetRootNode(const long nodeId) { rootNodeId = nodeId; } - const long GetRootNode() const { return rootNodeId; } + // Add geometry. + void AddVertexAttribute(const RawVertexAttribute attrib); + int AddVertex(const RawVertex& vertex); + int AddTriangle( + const int v0, + const int v1, + const int v2, + const int materialIndex, + const int surfaceIndex); + int AddTexture( + const std::string& name, + const std::string& fileName, + const std::string& fileLocation, + RawTextureUsage usage); + int AddMaterial(const RawMaterial& material); + int AddMaterial( + const char* name, + const RawMaterialType materialType, + const int textures[RAW_TEXTURE_USAGE_MAX], + std::shared_ptr materialInfo, + const std::vector& userProperties); + int AddLight( + const char* name, + RawLightType lightType, + Vec3f color, + float intensity, + float innerConeAngle, + float outerConeAngle); + int AddSurface(const RawSurface& suface); + int AddSurface(const char* name, long surfaceId); + int AddAnimation(const RawAnimation& animation); + int AddCameraPerspective( + const char* name, + const long nodeId, + const float aspectRatio, + const float fovDegreesX, + const float fovDegreesY, + const float nearZ, + const float farZ); + int AddCameraOrthographic( + const char* name, + const long nodeId, + const float magX, + const float magY, + const float nearZ, + const float farZ); + int AddNode(const RawNode& node); + int AddNode(const long id, const char* name, const long parentId); + void SetRootNode(const long nodeId) { + rootNodeId = nodeId; + } + const long GetRootNode() const { + return rootNodeId; + } - // Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces. - void Condense(); + // Remove unused vertices, textures or materials after removing vertex attributes, textures, + // materials or surfaces. + void Condense(); - void TransformGeometry(ComputeNormalsOption); + void TransformGeometry(ComputeNormalsOption); - void TransformTextures(const std::vector> &transforms); + void TransformTextures(const std::vector>& transforms); - size_t CalculateNormals(bool); + size_t CalculateNormals(bool); - // Get the attributes stored per vertex. - int GetVertexAttributes() const { return vertexAttributes; } + // Get the attributes stored per vertex. + int GetVertexAttributes() const { + return vertexAttributes; + } - // Iterate over the vertices. - int GetVertexCount() const { return (int) vertices.size(); } - const RawVertex &GetVertex(const int index) const { return vertices[index]; } + // Iterate over the vertices. + int GetVertexCount() const { + return (int)vertices.size(); + } + const RawVertex& GetVertex(const int index) const { + return vertices[index]; + } - // Iterate over the triangles. - int GetTriangleCount() const { return (int) triangles.size(); } - const RawTriangle &GetTriangle(const int index) const { return triangles[index]; } + // Iterate over the triangles. + int GetTriangleCount() const { + return (int)triangles.size(); + } + const RawTriangle& GetTriangle(const int index) const { + return triangles[index]; + } - // Iterate over the textures. - int GetTextureCount() const { return (int) textures.size(); } - const RawTexture &GetTexture(const int index) const { return textures[index]; } + // Iterate over the textures. + int GetTextureCount() const { + return (int)textures.size(); + } + const RawTexture& GetTexture(const int index) const { + return textures[index]; + } - // Iterate over the materials. - int GetMaterialCount() const { return (int) materials.size(); } - const RawMaterial &GetMaterial(const int index) const { return materials[index]; } + // Iterate over the materials. + int GetMaterialCount() const { + return (int)materials.size(); + } + const RawMaterial& GetMaterial(const int index) const { + return materials[index]; + } - // Iterate over the surfaces. - int GetSurfaceCount() const { return (int) surfaces.size(); } - const RawSurface &GetSurface(const int index) const { return surfaces[index]; } - RawSurface &GetSurface(const int index) { return surfaces[index]; } - int GetSurfaceById(const long id) const; + // Iterate over the surfaces. + int GetSurfaceCount() const { + return (int)surfaces.size(); + } + const RawSurface& GetSurface(const int index) const { + return surfaces[index]; + } + RawSurface& GetSurface(const int index) { + return surfaces[index]; + } + int GetSurfaceById(const long id) const; - // Iterate over the animations. - int GetAnimationCount() const { return (int) animations.size(); } - const RawAnimation &GetAnimation(const int index) const { return animations[index]; } + // Iterate over the animations. + int GetAnimationCount() const { + return (int)animations.size(); + } + const RawAnimation& GetAnimation(const int index) const { + return animations[index]; + } - // Iterate over the cameras. - int GetCameraCount() const { return (int) cameras.size(); } - const RawCamera &GetCamera(const int index) const { return cameras[index]; } + // Iterate over the cameras. + int GetCameraCount() const { + return (int)cameras.size(); + } + const RawCamera& GetCamera(const int index) const { + return cameras[index]; + } - // Iterate over the lights. - int GetLightCount() const { return (int) lights.size(); } - const RawLight &GetLight(const int index) const { return lights[index]; } + // Iterate over the lights. + int GetLightCount() const { + return (int)lights.size(); + } + const RawLight& GetLight(const int index) const { + return lights[index]; + } - // Iterate over the nodes. - int GetNodeCount() const { return (int) nodes.size(); } - const RawNode &GetNode(const int index) const { return nodes[index]; } - RawNode &GetNode(const int index) { return nodes[index]; } - int GetNodeById(const long nodeId) const; + // Iterate over the nodes. + int GetNodeCount() const { + return (int)nodes.size(); + } + const RawNode& GetNode(const int index) const { + return nodes[index]; + } + RawNode& GetNode(const int index) { + return nodes[index]; + } + int GetNodeById(const long nodeId) const; - // Create individual attribute arrays. - // Returns true if the vertices store the particular attribute. - template - void GetAttributeArray(std::vector<_attrib_type_> &out, const _attrib_type_ RawVertex::* ptr) const; + // Create individual attribute arrays. + // Returns true if the vertices store the particular attribute. + template + void GetAttributeArray(std::vector<_attrib_type_>& out, const _attrib_type_ RawVertex::*ptr) + const; - // Create an array with a raw model for each material. - // Multiple surfaces with the same material will turn into a single model. - // However, surfaces that are marked as 'discrete' will turn into separate models. - void CreateMaterialModels( - std::vector &materialModels, bool shortIndices, const int keepAttribs, const bool forceDiscrete) const; + // Create an array with a raw model for each material. + // Multiple surfaces with the same material will turn into a single model. + // However, surfaces that are marked as 'discrete' will turn into separate models. + void CreateMaterialModels( + std::vector& materialModels, + bool shortIndices, + const int keepAttribs, + const bool forceDiscrete) const; -private: - Vec3f getFaceNormal(int verts[3]) const; + private: + Vec3f getFaceNormal(int verts[3]) const; - long rootNodeId; - int vertexAttributes; - std::unordered_map vertexHash; - std::vector vertices; - std::vector triangles; - std::vector textures; - std::vector materials; - std::vector lights; - std::vector surfaces; - std::vector animations; - std::vector cameras; - std::vector nodes; + long rootNodeId; + int vertexAttributes; + std::unordered_map vertexHash; + std::vector vertices; + std::vector triangles; + std::vector textures; + std::vector materials; + std::vector lights; + std::vector surfaces; + std::vector animations; + std::vector cameras; + std::vector nodes; }; -template -void RawModel::GetAttributeArray(std::vector<_attrib_type_> &out, const _attrib_type_ RawVertex::* ptr) const -{ - out.resize(vertices.size()); - for (size_t i = 0; i < vertices.size(); i++) { - out[i] = vertices[i].*ptr; - } +template +void RawModel::GetAttributeArray( + std::vector<_attrib_type_>& out, + const _attrib_type_ RawVertex::*ptr) const { + out.resize(vertices.size()); + for (size_t i = 0; i < vertices.size(); i++) { + out[i] = vertices[i].*ptr; + } } diff --git a/src/utils/File_Utils.cpp b/src/utils/File_Utils.cpp index ad045c6..4c044fe 100644 --- a/src/utils/File_Utils.cpp +++ b/src/utils/File_Utils.cpp @@ -9,24 +9,24 @@ #include "File_Utils.hpp" +#include #include #include -#include #include #include -#if defined( __unix__ ) || defined ( __APPLE__ ) +#if defined(__unix__) || defined(__APPLE__) -#include -#include -#include #include #include +#include +#include +#include #define _getcwd getcwd #define _mkdir(a) mkdir(a, 0777) -#elif defined( _WIN32 ) +#elif defined(_WIN32) #include #include #else @@ -41,176 +41,174 @@ 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 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); - } - - 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]; - } - return true; - } - - bool CopyFile(const std::string &srcFilename, const std::string &dstFilename, bool createPath) { - std::ifstream srcFile(srcFilename, std::ios::binary); - if (!srcFile) { - fmt::printf("Warning: Couldn't open file %s for reading.\n", srcFilename); - return false; - } - // find source file length - srcFile.seekg(0, std::ios::end); - std::streamsize srcSize = srcFile.tellg(); - srcFile.seekg(0, std::ios::beg); - - if (createPath && !CreatePath(dstFilename.c_str())) { - fmt::printf("Warning: Couldn't create directory %s.\n", dstFilename); - return false; - } - - std::ofstream dstFile(dstFilename, std::ios::binary | std::ios::trunc); - if (!dstFile) { - fmt::printf("Warning: Couldn't open file %s for writing.\n", dstFilename); - return false; - } - dstFile << srcFile.rdbuf(); - std::streamsize dstSize = dstFile.tellp(); - if (srcSize == dstSize) { - return true; - } - fmt::printf("Warning: Only copied %lu bytes to %s, when %s is %lu bytes long.\n", dstSize, dstFilename, srcFilename, srcSize); - return false; - } +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 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); + } + + 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]; + } + return true; +} + +bool CopyFile(const std::string& srcFilename, const std::string& dstFilename, bool createPath) { + std::ifstream srcFile(srcFilename, std::ios::binary); + if (!srcFile) { + fmt::printf("Warning: Couldn't open file %s for reading.\n", srcFilename); + return false; + } + // find source file length + srcFile.seekg(0, std::ios::end); + std::streamsize srcSize = srcFile.tellg(); + srcFile.seekg(0, std::ios::beg); + + if (createPath && !CreatePath(dstFilename.c_str())) { + fmt::printf("Warning: Couldn't create directory %s.\n", dstFilename); + return false; + } + + std::ofstream dstFile(dstFilename, std::ios::binary | std::ios::trunc); + if (!dstFile) { + fmt::printf("Warning: Couldn't open file %s for writing.\n", dstFilename); + return false; + } + dstFile << srcFile.rdbuf(); + std::streamsize dstSize = dstFile.tellp(); + if (srcSize == dstSize) { + return true; + } + fmt::printf( + "Warning: Only copied %lu bytes to %s, when %s is %lu bytes long.\n", + dstSize, + dstFilename, + srcFilename, + srcSize); + return false; +} +} // namespace FileUtils diff --git a/src/utils/File_Utils.hpp b/src/utils/File_Utils.hpp index 6a54423..0f9a702 100644 --- a/src/utils/File_Utils.hpp +++ b/src/utils/File_Utils.hpp @@ -14,15 +14,18 @@ namespace FileUtils { - std::string GetCurrentFolder(); +std::string GetCurrentFolder(); - bool FileExists(const std::string &folderPath); - bool FolderExists(const std::string &folderPath); +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); +bool MatchExtension(const char* fileExtension, const char* matchExtensions); +std::vector ListFolderFiles(const char* folder, const char* matchExtensions); - bool CreatePath(const char *path); +bool CreatePath(const char* path); - bool CopyFile(const std::string &srcFilename, const std::string &dstFilename, bool createPath = false); -} +bool CopyFile( + const std::string& srcFilename, + const std::string& dstFilename, + bool createPath = false); +} // namespace FileUtils diff --git a/src/utils/Image_Utils.cpp b/src/utils/Image_Utils.cpp index cac8e6f..23c107e 100644 --- a/src/utils/Image_Utils.cpp +++ b/src/utils/Image_Utils.cpp @@ -9,8 +9,8 @@ #include "Image_Utils.hpp" -#include #include +#include #define STB_IMAGE_IMPLEMENTATION @@ -22,56 +22,53 @@ namespace ImageUtils { - 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 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; + } } - - ImageProperties GetImageProperties(char const *filePath) - { - ImageProperties result = { - 1, - 1, - IMAGE_OPAQUE, - }; - - FILE *f = fopen(filePath, "rb"); - if (f == nullptr) { - return result; - } - - 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 result; - } - - std::string suffixToMimeType(std::string suffix) - { - std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); - - if (suffix == "jpg" || suffix == "jpeg") { - return "image/jpeg"; - } - if (suffix == "png") { - return "image/png"; - } - return "image/unknown"; - } - + } + return false; } + +ImageProperties GetImageProperties(char const* filePath) { + ImageProperties result = { + 1, + 1, + IMAGE_OPAQUE, + }; + + FILE* f = fopen(filePath, "rb"); + if (f == nullptr) { + return result; + } + + 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 result; +} + +std::string suffixToMimeType(std::string suffix) { + std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::tolower); + + if (suffix == "jpg" || suffix == "jpeg") { + return "image/jpeg"; + } + if (suffix == "png") { + return "image/png"; + } + return "image/unknown"; +} + +} // namespace ImageUtils diff --git a/src/utils/Image_Utils.hpp b/src/utils/Image_Utils.hpp index eb4d8e9..a391163 100644 --- a/src/utils/Image_Utils.hpp +++ b/src/utils/Image_Utils.hpp @@ -13,25 +13,20 @@ namespace ImageUtils { - enum ImageOcclusion - { - IMAGE_OPAQUE, - IMAGE_TRANSPARENT - }; +enum ImageOcclusion { IMAGE_OPAQUE, IMAGE_TRANSPARENT }; - struct ImageProperties - { - int width; - int height; - ImageOcclusion occlusion; - }; +struct ImageProperties { + int width; + int height; + ImageOcclusion occlusion; +}; - ImageProperties GetImageProperties(char const *filePath); +ImageProperties GetImageProperties(char const* filePath); - /** - * Very simple method for mapping filename suffix to mime type. The glTF 2.0 spec only accepts values - * "image/jpeg" and "image/png" so we don't need to get too fancy. - */ - std::string suffixToMimeType(std::string suffix); +/** + * Very simple method for mapping filename suffix to mime type. The glTF 2.0 spec only accepts + * values "image/jpeg" and "image/png" so we don't need to get too fancy. + */ +std::string suffixToMimeType(std::string suffix); -} +} // namespace ImageUtils diff --git a/src/utils/String_Utils.cpp b/src/utils/String_Utils.cpp index 3e958ed..8cf8063 100644 --- a/src/utils/String_Utils.cpp +++ b/src/utils/String_Utils.cpp @@ -11,77 +11,70 @@ 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); - } - +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); +} + +} // namespace StringUtils diff --git a/src/utils/String_Utils.hpp b/src/utils/String_Utils.hpp index 1976c0d..f1a3a65 100644 --- a/src/utils/String_Utils.hpp +++ b/src/utils/String_Utils.hpp @@ -9,49 +9,46 @@ #pragma once -#include +#include #include #include -#include +#include -#if defined( _MSC_VER ) +#if defined(_MSC_VER) #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif 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); - PathSeparator GetPathSeparator(); - const std::string NormalizePath(const std::string &path); +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; - } - } - - 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); +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; + } } + +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