diff --git a/README.md b/README.md index 7ec8fab..14992c5 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ a modern runtime asset delivery format. Precompiled binaries releases for Windows, Mac OS X and Linux may be found [here](https://github.com/facebookincubator/FBX2glTF/releases). +Bleeding-edge binaries are periodically built and publicly available [here](https://dev.azure.com/parwinzell/FBX2glTF/): click a build (usually the most recent), then the 'Artefacts' dropdown in the upper right. + ## Running The tool can be invoked like so: diff --git a/npm/tests/test/fbx2gltf.ts b/npm/tests/test/fbx2gltf.ts index ea88cf2..118a2b7 100644 --- a/npm/tests/test/fbx2gltf.ts +++ b/npm/tests/test/fbx2gltf.ts @@ -1,76 +1,80 @@ -import { readFileSync } from 'fs'; -import * as tmp from 'tmp'; -import * as path from 'path'; +import {assert, expect} from 'chai'; import * as fbx2gltf from 'fbx2gltf'; -import { assert, expect } from 'chai'; -import { validateBytes } from 'gltf-validator'; +import {readFileSync} from 'fs'; +import {validateBytes} from 'gltf-validator'; +import * as path from 'path'; +import * as tmp from 'tmp'; interface Model { - path: string; - ignoredIssues?: Array; - args?: Array; + path: string; + ignoredIssues?: Array; + args?: Array; } -const MODELS :Array = [ - { path: 'fromFacebook/Jon/jon_morph' }, - { - path: 'fromFacebook/Jon/troll-final', - ignoredIssues: [ 'ACCESSOR_NON_UNIT' ], - }, - { path: 'fromFacebook/Natalie/GlitchRobot' }, - { path: 'fromFacebook/Ocean/blackvan/blackvan_with_windows' }, - { path: 'fromFacebook/Ocean/zell_van_vertex_color' }, - { path: 'fromFacebook/RAZ/RAZ_ape' }, - { path: 'fromFbxSDK/Box' }, - { - path: 'fromFbxSDK/Humanoid', - ignoredIssues: [ 'UNSUPPORTED_EXTENSION' ], - }, - { - path: 'fromFbxSDK/Camera', - ignoredIssues: [ 'UNSUPPORTED_EXTENSION' ], - }, - { path: 'fromFbxSDK/Normals' }, - { path: 'fromGltfSamples/BoxVertexColors/BoxVertexColors' }, - { path: 'fromGltfSamples/WaterBottle/NewWaterBottle' }, +const MODELS: Array = [ + {path : 'fromFacebook/Jon/jon_morph'}, + { + path : 'fromFacebook/Jon/troll-final', + ignoredIssues : [ 'ACCESSOR_NON_UNIT' ], + }, + {path : 'fromFacebook/Natalie/GlitchRobot'}, + {path : 'fromFacebook/Ocean/blackvan/blackvan_with_windows'}, + { + path : 'fromFacebook/Ocean/zell_van_vertex_color', + args : [ '--draco' ], + ignoredIssues : [ 'UNSUPPORTED_EXTENSION' ], + }, + {path : 'fromFacebook/RAZ/RAZ_ape', args : [ '--long-indices=always' ]}, + {path : 'fromFbxSDK/Box'}, + { + path : 'fromFbxSDK/Humanoid', + ignoredIssues : [ 'UNSUPPORTED_EXTENSION' ], + }, + { + path : 'fromFbxSDK/Camera', + ignoredIssues : [ 'UNSUPPORTED_EXTENSION' ], + }, + {path : 'fromFbxSDK/Normals'}, + {path : 'fromGltfSamples/BoxVertexColors/BoxVertexColors', args : [ '--khr-materials-unlit' ]}, + {path : 'fromGltfSamples/WaterBottle/NewWaterBottle'}, ]; const CONVERSION_TIMEOUT_MS = 50000; describe('FBX2glTF', () => { - const tmpobj = tmp.dirSync(); - for(let model of MODELS) { - const modelName = path.basename(model.path); - describe('Model: ' + modelName, () => { - const fbxPath = path.join('models', model.path + '.fbx'); - let glbBytes; - it('should convert fbx to glb', async () => { - const glbPath = path.join(tmpobj.name, modelName + '.glb'); + const tmpobj = tmp.dirSync(); + for (let model of MODELS) { + const modelName = path.basename(model.path); + describe('Model: ' + modelName, () => { + const fbxPath = path.join('models', model.path + '.fbx'); + let glbBytes; + it('should convert fbx to glb', async () => { + const glbPath = path.join(tmpobj.name, modelName + '.glb'); - try { - const destPath = await fbx2gltf(fbxPath, glbPath, model.args || []); - assert.isNotNull(destPath); - glbBytes = readFileSync(destPath); - } catch (err) { - throw new Error('Conversion failed: ' + err); - } - }).timeout(CONVERSION_TIMEOUT_MS); + try { + const destPath = await fbx2gltf(fbxPath, glbPath, model.args || []); + assert.isNotNull(destPath); + glbBytes = readFileSync(destPath); + } catch (err) { + throw new Error('Conversion failed: ' + err); + } + }).timeout(CONVERSION_TIMEOUT_MS); - it('resulting glb should be valid', async() => { - try { - let options = {}; - if (model.ignoredIssues) { - options.ignoredIssues = model.ignoredIssues; - } - const report = await validateBytes(glbBytes, options); - expect(report.issues.numErrors).to.equal(0); - expect(report.issues.numWarnings).to.equal(0); + it('resulting glb should be valid', async () => { + try { + let options = {}; + if (model.ignoredIssues) { + options.ignoredIssues = model.ignoredIssues; + } + const report = await validateBytes(glbBytes, options); + expect(report.issues.numErrors).to.equal(0); + expect(report.issues.numWarnings).to.equal(0); - } catch (err) { - throw new Error('Validation failed: ' + err); - } - }); - }); - } - console.log('GLB files may be inspected in: ' + tmpobj.name); + } catch (err) { + throw new Error('Validation failed: ' + err); + } + }); + }); + } + console.log('GLB files may be inspected in: ' + tmpobj.name); }); diff --git a/src/fbx/Fbx2Raw.cpp b/src/fbx/Fbx2Raw.cpp index 189828e..9a11bdd 100644 --- a/src/fbx/Fbx2Raw.cpp +++ b/src/fbx/Fbx2Raw.cpp @@ -209,9 +209,11 @@ static void ReadMesh( std::shared_ptr rawMatProps; FbxString materialName; + long materialId; if (fbxMaterial == nullptr) { materialName = "DefaultMaterial"; + materialId = -1; rawMatProps.reset(new RawTraditionalMatProps( RAW_SHADING_MODEL_LAMBERT, Vec3f(0, 0, 0), @@ -222,6 +224,7 @@ static void ReadMesh( } else { materialName = fbxMaterial->name; + materialId = fbxMaterial->id; const auto maybeAddTexture = [&](const FbxFileTexture* tex, RawTextureUsage usage) { if (tex != nullptr) { @@ -436,8 +439,8 @@ static void ReadMesh( const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned()); - const int rawMaterialIndex = - raw.AddMaterial(materialName, materialType, textures, rawMatProps, userProperties); + const int rawMaterialIndex = raw.AddMaterial( + materialId, materialName, materialType, textures, rawMatProps, userProperties); raw.AddTriangle( rawVertexIndices[0], diff --git a/src/fbx/materials/3dsMaxPhysicalMaterial.cpp b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp index aa619cd..362c3a7 100644 --- a/src/fbx/materials/3dsMaxPhysicalMaterial.cpp +++ b/src/fbx/materials/3dsMaxPhysicalMaterial.cpp @@ -149,6 +149,7 @@ std::unique_ptr Fbx3dsMaxPhysicalMaterialResolver::reso } std::unique_ptr res(new FbxRoughMetMaterialInfo( + fbxMaterial->GetUniqueID(), fbxMaterial->GetName(), FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, baseCol, diff --git a/src/fbx/materials/FbxMaterials.hpp b/src/fbx/materials/FbxMaterials.hpp index 9141b78..c48a829 100644 --- a/src/fbx/materials/FbxMaterials.hpp +++ b/src/fbx/materials/FbxMaterials.hpp @@ -16,9 +16,10 @@ class FbxMaterialInfo { public: - FbxMaterialInfo(const FbxString& name, const FbxString& shadingModel) - : name(name), shadingModel(shadingModel) {} + FbxMaterialInfo(const FbxUInt64 id, const FbxString& name, const FbxString& shadingModel) + : id(id), name(name), shadingModel(shadingModel) {} + const FbxUInt64 id; const FbxString name; const FbxString shadingModel; }; diff --git a/src/fbx/materials/RoughnessMetallicMaterials.hpp b/src/fbx/materials/RoughnessMetallicMaterials.hpp index 9123e28..ad430ae 100644 --- a/src/fbx/materials/RoughnessMetallicMaterials.hpp +++ b/src/fbx/materials/RoughnessMetallicMaterials.hpp @@ -21,12 +21,13 @@ struct FbxRoughMetMaterialInfo : FbxMaterialInfo { const std::map& textureLocations); FbxRoughMetMaterialInfo( + const FbxUInt64 id, const FbxString& name, const FbxString& shadingModel, FbxDouble4 baseColor, FbxDouble metallic, FbxDouble roughness) - : FbxMaterialInfo(name, shadingModel), + : FbxMaterialInfo(id, name, shadingModel), baseColor(baseColor), metallic(metallic), roughness(roughness) {} diff --git a/src/fbx/materials/StingrayPBSMaterial.cpp b/src/fbx/materials/StingrayPBSMaterial.cpp index 05dc5e1..29ef74f 100644 --- a/src/fbx/materials/StingrayPBSMaterial.cpp +++ b/src/fbx/materials/StingrayPBSMaterial.cpp @@ -54,6 +54,7 @@ std::unique_ptr FbxStingrayPBSMaterialResolver::resolve FbxDouble3 baseColor = getVec("base_color"); std::unique_ptr res(new FbxRoughMetMaterialInfo( + fbxMaterial->GetUniqueID(), fbxMaterial->GetName(), FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH, FbxDouble4(baseColor[0], baseColor[1], baseColor[2], 1), diff --git a/src/fbx/materials/TraditionalMaterials.cpp b/src/fbx/materials/TraditionalMaterials.cpp index 6b0bc17..919dd99 100644 --- a/src/fbx/materials/TraditionalMaterials.cpp +++ b/src/fbx/materials/TraditionalMaterials.cpp @@ -68,8 +68,8 @@ std::unique_ptr FbxTraditionalMaterialResolver::reso }; std::string name = fbxMaterial->GetName(); - std::unique_ptr res( - new FbxTraditionalMaterialInfo(name.c_str(), fbxMaterial->ShadingModel.Get())); + std::unique_ptr res(new FbxTraditionalMaterialInfo( + fbxMaterial->GetUniqueID(), name.c_str(), fbxMaterial->ShadingModel.Get())); // four properties are on the same structure and follow the same rules auto handleBasicProperty = [&](const char* colName, diff --git a/src/fbx/materials/TraditionalMaterials.hpp b/src/fbx/materials/TraditionalMaterials.hpp index c0fa51d..31a3912 100644 --- a/src/fbx/materials/TraditionalMaterials.hpp +++ b/src/fbx/materials/TraditionalMaterials.hpp @@ -14,8 +14,11 @@ struct FbxTraditionalMaterialInfo : FbxMaterialInfo { 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 FbxUInt64 id, + const FbxString& name, + const FbxString& shadingModel) + : FbxMaterialInfo(id, name, shadingModel) {} FbxFileTexture* texAmbient{}; FbxVector4 colAmbient{}; diff --git a/src/gltf/Raw2Gltf.cpp b/src/gltf/Raw2Gltf.cpp index 01b4afe..9b35618 100644 --- a/src/gltf/Raw2Gltf.cpp +++ b/src/gltf/Raw2Gltf.cpp @@ -77,11 +77,6 @@ static const std::vector getIndexArray(const RawModel& raw) { 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); -} - ModelData* Raw2Gltf( std::ofstream& gltfOutStream, const std::string& outputFolder, @@ -123,7 +118,7 @@ ModelData* Raw2Gltf( std::unique_ptr gltf(new GltfModel(options)); std::map> nodesById; - std::map> materialsByName; + std::map> materialsById; std::map> textureByIndicesKey; std::map> meshBySurfaceId; @@ -394,7 +389,8 @@ ModelData* Raw2Gltf( emissiveFactor * emissiveIntensity, khrCmnUnlitMat, pbrMetRough)); - materialsByName[materialHash(material)] = mData; + fmt::printf("Stashing material of id %ls, name %s...\n", material.id, material.name.c_str()); + materialsById[material.id] = mData; if (options.enableUserProperties) { mData->userProperties = material.userProperties; @@ -408,7 +404,9 @@ ModelData* Raw2Gltf( const RawMaterial& rawMaterial = surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex); - const MaterialData& mData = require(materialsByName, materialHash(rawMaterial)); + fmt::printf( + "Seeking material of id %ls, name %s...\n", rawMaterial.id, rawMaterial.name.c_str()); + const MaterialData& mData = require(materialsById, rawMaterial.id); MeshData* mesh = nullptr; auto meshIter = meshBySurfaceId.find(surfaceId); diff --git a/src/raw/RawModel.cpp b/src/raw/RawModel.cpp index 38b5fc7..cafc076 100644 --- a/src/raw/RawModel.cpp +++ b/src/raw/RawModel.cpp @@ -129,6 +129,7 @@ int RawModel::AddTexture( int RawModel::AddMaterial(const RawMaterial& material) { return AddMaterial( + material.id, material.name.c_str(), material.type, material.textures, @@ -137,6 +138,7 @@ int RawModel::AddMaterial(const RawMaterial& material) { } int RawModel::AddMaterial( + const long id, const char* name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX], @@ -169,6 +171,7 @@ int RawModel::AddMaterial( } RawMaterial material; + material.id = id; material.name = name; material.type = materialType; material.info = materialInfo; diff --git a/src/raw/RawModel.hpp b/src/raw/RawModel.hpp index 0b81903..c21c4bb 100644 --- a/src/raw/RawModel.hpp +++ b/src/raw/RawModel.hpp @@ -262,6 +262,7 @@ struct RawMetRoughMatProps : RawMatProps { }; struct RawMaterial { + long id; std::string name; RawMaterialType type; std::shared_ptr info; @@ -374,6 +375,7 @@ class RawModel { RawTextureUsage usage); int AddMaterial(const RawMaterial& material); int AddMaterial( + const long id, const char* name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],