Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
|
739ee5db94 | |
|
37f992321e | |
|
be627fa228 | |
|
1d735698ba | |
|
5c3229d6cf |
64
README.md
64
README.md
|
@ -1,4 +1,5 @@
|
|||
# FBX2glTF
|
||||
|
||||
[](https://opensource.org/licenses/BSD-3-Clause)
|
||||
|
||||
This is a command line tool for converting 3D model assets on Autodesk's
|
||||
|
@ -17,11 +18,13 @@ Bleeding-edge binaries for Windows may be found [here](https://ci.appveyor.com/p
|
|||
## Running
|
||||
|
||||
The tool can be invoked like so:
|
||||
|
||||
```
|
||||
> FBX2glTF ~/models/butterfly.fbx
|
||||
```
|
||||
|
||||
Or perhaps, as part of a more complex pipeline:
|
||||
|
||||
```
|
||||
> FBX2glTF --binary --draco --verbose \
|
||||
--input ~/models/source/butterfly.fbx \
|
||||
|
@ -33,6 +36,7 @@ There are also some friendly & hands-on instructions available [over at Facebook
|
|||
### CLI Switches
|
||||
|
||||
You can always run the binary with --help to see what options it takes:
|
||||
|
||||
```
|
||||
FBX2glTF 0.9.7: Generate a glTF 2.0 representation of an FBX model.
|
||||
Usage: FBX2glTF [OPTIONS] [FBX Model]
|
||||
|
@ -106,7 +110,7 @@ Some of these switches are not obvious:
|
|||
- `--compute-normals` controls when automatic vertex normals should be computed
|
||||
from the mesh. By default, empty normals (which are forbidden by glTF) are
|
||||
replaced. A choice of 'missing' implies 'broken', but additionally creates
|
||||
normals for models that lack them completely.
|
||||
normals for models that lack them completely.
|
||||
- `--no-flip-v` will actively disable v coordinat flipping. This can be useful
|
||||
if your textures are pre-flipped, or if for some other reason you were already
|
||||
in a glTF-centric texture coordinate system.
|
||||
|
@ -116,7 +120,7 @@ Some of these switches are not obvious:
|
|||
will be chosen by default if you supply none of the others. Material switches
|
||||
are documented further below.
|
||||
- If you supply any `-keep-attribute` option, you enable a mode wherein you must
|
||||
supply it repeatedly to list *all* the vertex attributes you wish to keep in
|
||||
supply it repeatedly to list _all_ the vertex attributes you wish to keep in
|
||||
the conversion process. This is a way to trim the size of the resulting glTF
|
||||
if you know the FBX contains superfluous attributes. The supported arguments
|
||||
are `position`, `normal`, `tangent`, `color`, `uv0`, and `uv1`.
|
||||
|
@ -144,10 +148,12 @@ all of which are automatically downloaded and/or built.
|
|||
build system will not successfully locate any other version.
|
||||
|
||||
### Linux and MacOS X
|
||||
|
||||
Your development environment will need to have:
|
||||
|
||||
- build essentials (gcc for Linux, clang for Mac)
|
||||
- cmake
|
||||
- python 3.* and associated pip3/pip command
|
||||
- python 3.\* and associated pip3/pip command
|
||||
- zstd
|
||||
|
||||
Then, compilation on Unix machines will look something like:
|
||||
|
@ -165,11 +171,11 @@ else
|
|||
fi
|
||||
|
||||
# Fetch Project
|
||||
> GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/facebookincubator/FBX2glTF.git
|
||||
> git clone https://github.com/facebookincubator/FBX2glTF.git
|
||||
> cd FBX2glTF
|
||||
|
||||
# Fetch and unpack FBX SDK
|
||||
> curl -sL "${FBXSDK_TARBALL}" | tar xz --strip-components=1 --wildcards */sdk
|
||||
> curl -sL "${FBXSDK_TARBALL}" | tar xz --strip-components=1 --include */sdk/
|
||||
# Then decompress the contents
|
||||
> zstd -d -r --rm sdk
|
||||
|
||||
|
@ -198,10 +204,11 @@ versions of the IDE are unlikely to successfully build the tool.
|
|||
|
||||
Note that the `CMAKE_BUILD_TYPE` variable from the Unix Makefile system is
|
||||
entirely ignored here; it is when you open the generated solution that
|
||||
you will be choose one of the canonical build types — *Debug*,
|
||||
*Release*, *MinSizeRel*, and so on.
|
||||
you will be choose one of the canonical build types — _Debug_,
|
||||
_Release_, _MinSizeRel_, and so on.
|
||||
|
||||
## Conversion Process
|
||||
|
||||
The actual translation begins with the FBX SDK parsing the input file, and ends
|
||||
with the generation of the descriptive `JSON` that forms the core of glTF, along
|
||||
with binary buffers that hold geometry and animations (and optionally also
|
||||
|
@ -213,13 +220,14 @@ process happens in reverse when we construct meshes and materials that conform
|
|||
to the expectations of the glTF format.
|
||||
|
||||
### Animations
|
||||
|
||||
Every animation in the FBX file becomes an animation in the glTF file. The
|
||||
method used is one of "baking": we step through the interval of time spanned by
|
||||
the animation, keyframe by keyframe, calculate the local transform of each
|
||||
node, and whenever we find any node that's rotated, translated or scaled, we
|
||||
record that fact in the output.
|
||||
|
||||
Beyond skeleton-based animation, *Blend Shapes* are also supported; they are
|
||||
Beyond skeleton-based animation, _Blend Shapes_ are also supported; they are
|
||||
read from the FBX file on a per-mesh basis, and clips can use them by varying
|
||||
the weights associated with each one.
|
||||
|
||||
|
@ -228,6 +236,7 @@ drawback of creating potentially very large files. The more complex the
|
|||
animation rig, the less avoidable this data explosion is.
|
||||
|
||||
There are three future enhancements we hope to see for animations:
|
||||
|
||||
- Version 2.0 of glTF brought us support for expressing quadratic animation
|
||||
curves, where previously we had only had linear. Not coincidentally, quadratic
|
||||
splines are one of the key ways animations are expressed inside the FBX. When
|
||||
|
@ -257,33 +266,37 @@ old workflow often contain baked lighting of the type that would arise naturally
|
|||
in a PBR environment.
|
||||
|
||||
Some material settings remain well supported and transfer automatically:
|
||||
- Emissive constants and textures
|
||||
- Occlusion maps
|
||||
- Normal maps
|
||||
|
||||
- Emissive constants and textures
|
||||
- Occlusion maps
|
||||
- Normal maps
|
||||
|
||||
This leaves the other traditional settings, first of Lambert:
|
||||
- Ambient — this is anathema in the PBR world, where such effects should
|
||||
emerge naturally from the fundamental colour of the material and any ambient
|
||||
lighting present.
|
||||
- Diffuse — the material's direction-agnostic, non-specular reflection,
|
||||
and additionally, with Blinn/Phong:
|
||||
- Specular — a more polished material's direction-sensitive reflection,
|
||||
- Shininess — just how polished the material is; a higher value here yields a
|
||||
more mirror-like surface.
|
||||
|
||||
- Ambient — this is anathema in the PBR world, where such effects should
|
||||
emerge naturally from the fundamental colour of the material and any ambient
|
||||
lighting present.
|
||||
- Diffuse — the material's direction-agnostic, non-specular reflection,
|
||||
and additionally, with Blinn/Phong:
|
||||
- Specular — a more polished material's direction-sensitive reflection,
|
||||
- Shininess — just how polished the material is; a higher value here yields a
|
||||
more mirror-like surface.
|
||||
|
||||
(All these can be either constants or textures.)
|
||||
|
||||
#### Exporting as Unlit
|
||||
|
||||
If you have a model was constructed using an unlit workflow, e.g. a photogrammetry
|
||||
capture or a landscape with careful baked-in lighting, you may choose to export
|
||||
it using the --khr-materials-common switch. This incurs a dependency on the glTF
|
||||
extension 'KHR_materials_unlit; a client that accepts that extension is making
|
||||
extension 'KHR_materials_unlit; a client that accepts that extension is making
|
||||
a promise it'll do its best to render pixel values without lighting calculations.
|
||||
|
||||
**Note that at the time of writing, this glTF extension is still undergoing the
|
||||
ratification process**
|
||||
|
||||
#### Exporting as Metallic-Roughness PBR
|
||||
|
||||
Given the command line flag --pbr-metallic-roughness, we throw ourselves into
|
||||
the warm embrace of glTF 2.0's PBR preference.
|
||||
|
||||
|
@ -296,6 +309,7 @@ that route should be digested propertly by FBX2glTF.
|
|||
when hooked up to Maya.)
|
||||
|
||||
## Draco Compression
|
||||
|
||||
The tool will optionally apply [Draco](https://github.com/google/draco)
|
||||
compression to the geometric data of each mesh (vertex indices, positions,
|
||||
normals, per-vertex color, and so on). This can be dramatically effective
|
||||
|
@ -309,16 +323,18 @@ viewer that is willing and able to decompress the data.
|
|||
ratification process.**
|
||||
|
||||
## Future Improvements
|
||||
|
||||
This tool is under continuous development. We do not have a development roadmap
|
||||
per se, but some aspirations have been noted above. The canonical list of active
|
||||
TODO items can be found
|
||||
[on GitHub](https://github.com/facebookincubator/FBX2glTF/labels/enhancement).
|
||||
|
||||
|
||||
## Authors
|
||||
- Pär Winzell
|
||||
- J.M.P. van Waveren
|
||||
- Amanda Watson
|
||||
|
||||
- Pär Winzell
|
||||
- J.M.P. van Waveren
|
||||
- Amanda Watson
|
||||
|
||||
## License
|
||||
|
||||
FBX2glTF is licensed under the [3-clause BSD license](LICENSE).
|
||||
|
|
58
appveyor.yml
58
appveyor.yml
|
@ -1,58 +0,0 @@
|
|||
# version format
|
||||
version: 1.0.{build}
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
PYTHON: "C:\\Python36-x64"
|
||||
FBXSDK_SDKS: sdk
|
||||
|
||||
stack: python %PYTHON%
|
||||
|
||||
#temporarily disabled
|
||||
#cache:
|
||||
# - C:\Users\appveyor\.conan\data -> appveyor.yml, conanfile.py
|
||||
|
||||
init:
|
||||
- git config --global filter.lfs.required false
|
||||
- git config --global filter.lfs.smudge "git-lfs smudge --skip %f"
|
||||
- git config --global filter.lfs.process "git-lfs filter-process --skip"
|
||||
|
||||
install:
|
||||
- cinst zstandard
|
||||
- ps: Start-FileDownload 'https://github.com/zellski/FBXSDK-Windows/archive/2019.2.tar.gz'
|
||||
- 7z e 2019.2.tar.gz
|
||||
- 7z x 2019.2.tar
|
||||
- ps: move -v .\FBXSDK-Windows-2019.2\sdk\ .
|
||||
- ps: zstd -d -r --rm sdk
|
||||
- cmd: echo "Downloading conan..."
|
||||
- cmd: set PATH=%PYTHON%;%PYTHON%\Scripts;%PATH%
|
||||
- cmd: python --version
|
||||
- cmd: python -m pip install --upgrade pip
|
||||
- cmd: pip install conan
|
||||
- cmd: conan user # Create the conan data directory
|
||||
- cmd: conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan
|
||||
- cmd: conan --version
|
||||
|
||||
build_script:
|
||||
- cmd: conan install . -i build -s build_type=Release -s compiler="Visual Studio" -s compiler.version=15
|
||||
- cmd: conan build -bf build .
|
||||
- cmd: move build\Release\FBX2glTF.exe build\Release\FBX2glTF-windows-x64.exe
|
||||
|
||||
test: off
|
||||
|
||||
artifacts:
|
||||
- path: build/Release/FBX2glTF-windows-x64.exe
|
||||
|
||||
deploy:
|
||||
tag: $(APPVEYOR_REPO_TAG_NAME)
|
||||
description: ''
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: OSvhQP0O9uaH+OFOJpbGiiBMRTOJ+H/VJHqVBhq39RNDnOfUEr/yjJNKh3JSdNqj
|
||||
artifact: build/Release/FBX2glTF-windows-x64.exe
|
||||
draft: true
|
||||
prerelease: true
|
||||
on:
|
||||
branch: master
|
||||
APPVEYOR_REPO_TAG: true
|
|
@ -18,7 +18,7 @@ const binaries = {
|
|||
/**
|
||||
* Converts an FBX to a GTLF or GLB file.
|
||||
* @param string srcFile path to the source file.
|
||||
* @param string destFile path to the destination file.
|
||||
* @param string destFile path to the destination file or destination path.
|
||||
* This must end in `.glb` or `.gltf` (case matters).
|
||||
* @param string[] [opts] options to pass to the converter tool.
|
||||
* @return Promise<string> a promise that yields the full path to the converted
|
||||
|
@ -33,19 +33,31 @@ function convert(srcFile, destFile, opts = []) {
|
|||
throw new Error(`Unsupported OS: ${os.type()}`);
|
||||
}
|
||||
|
||||
let destExt;
|
||||
if (destFile.endsWith('.glb')) {
|
||||
destExt = '.glb';
|
||||
opts.includes('--binary') || opts.push('--binary');
|
||||
} else if (destFile.endsWith('.gltf')) {
|
||||
destExt = '.gltf';
|
||||
} else {
|
||||
let destExt = path.extname(destFile).toLowerCase();
|
||||
|
||||
if (!destExt) {
|
||||
destExt = '.gltf'
|
||||
|
||||
const srcFilename = path.basename(srcFile, path.extname(srcFile))
|
||||
destFile = path.join(destFile, srcFilename + destExt)
|
||||
}
|
||||
|
||||
if (destExt !== '.glb' && destExt !== '.gltf') {
|
||||
throw new Error(`Unsupported file extension: ${destFile}`);
|
||||
}
|
||||
|
||||
const binary = opts.includes('--binary') || opts.includes('-b');
|
||||
|
||||
if (binary && destExt !== '.glb') {
|
||||
destExt = '.glb';
|
||||
} else if (!binary && destExt === 'glb') {
|
||||
opts.push('--binary');
|
||||
}
|
||||
|
||||
let srcPath = fs.realpathSync(srcFile);
|
||||
let destDir = fs.realpathSync(path.dirname(destFile));
|
||||
let destPath = path.join(destDir, path.basename(destFile, destExt));
|
||||
let destFilename = path.basename(destFile, path.extname(destFile)) + destExt;
|
||||
let destPath = path.join(destDir, destFilename);
|
||||
|
||||
let args = opts.slice(0);
|
||||
args.push('--input', srcPath, '--output', destPath);
|
||||
|
@ -72,7 +84,7 @@ function convert(srcFile, destFile, opts = []) {
|
|||
reject(new Error(`Converter output:\n` +
|
||||
(output.length ? output : "<none>")));
|
||||
} else {
|
||||
resolve(destPath + destExt);
|
||||
resolve(destPath);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"name": "fbx2gltf",
|
||||
"version": "0.9.7-p1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz",
|
||||
"integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "fbx2gltf",
|
||||
"version": "0.9.7",
|
||||
"version": "0.9.7-p1",
|
||||
"description": "Node wrapper around FBX2glTF tools.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
|
|
@ -14,12 +14,21 @@ MeshData::MeshData(const std::string& name, const std::vector<float>& weights)
|
|||
|
||||
json MeshData::serialize() const {
|
||||
json jsonPrimitivesArray = json::array();
|
||||
json jsonTargetNamesArray = json::array();
|
||||
for (const auto& primitive : primitives) {
|
||||
jsonPrimitivesArray.push_back(*primitive);
|
||||
if (!primitive->targetNames.empty()) {
|
||||
for (auto targetName : primitive->targetNames) {
|
||||
jsonTargetNamesArray.push_back(targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
json result = {{"name", name}, {"primitives", jsonPrimitivesArray}};
|
||||
if (!weights.empty()) {
|
||||
result["weights"] = weights;
|
||||
}
|
||||
if (!jsonTargetNamesArray.empty()) {
|
||||
result["extras"]["targetNames"] = jsonTargetNamesArray;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ void PrimitiveData::AddTarget(
|
|||
positions->ix,
|
||||
normals != nullptr ? normals->ix : -1,
|
||||
tangents != nullptr ? tangents->ix : -1));
|
||||
targetNames.push_back(positions->name);
|
||||
}
|
||||
|
||||
void to_json(json& j, const PrimitiveData& d) {
|
||||
|
|
|
@ -68,6 +68,7 @@ struct PrimitiveData {
|
|||
const MeshMode mode;
|
||||
|
||||
std::vector<std::tuple<int, int, int>> targetAccessors{};
|
||||
std::vector<std::string> targetNames{};
|
||||
|
||||
std::map<std::string, int> attributes;
|
||||
std::map<std::string, int> dracoAttributes;
|
||||
|
|
Loading…
Reference in New Issue