Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
|
739ee5db94 | |
|
37f992321e | |
|
be627fa228 | |
|
1d735698ba | |
|
5c3229d6cf | |
|
3c08169510 | |
|
b9c7d0a400 | |
|
44d2d5bf97 | |
|
24092a80bb | |
|
31e3665862 | |
|
7b39358f46 | |
|
648fdfb944 | |
|
6437d02e5f |
75
README.md
75
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,8 +36,9 @@ 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.6: Generate a glTF 2.0 representation of an FBX model.
|
||||
FBX2glTF 0.9.7: Generate a glTF 2.0 representation of an FBX model.
|
||||
Usage: FBX2glTF [OPTIONS] [FBX Model]
|
||||
|
||||
Positionals:
|
||||
|
@ -64,6 +68,7 @@ Options:
|
|||
--blend-shape-tangents Include blend shape tangents, if reported present by the FBX SDK.
|
||||
-k,--keep-attribute (position|normal|tangent|binormial|color|uv0|uv1|auto) ...
|
||||
Used repeatedly to build a limiting set of vertex attributes to keep.
|
||||
--fbx-temp-dir DIR Temporary directory to be used by FBX SDK.
|
||||
|
||||
|
||||
Materials:
|
||||
|
@ -105,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.
|
||||
|
@ -115,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`.
|
||||
|
@ -143,30 +148,34 @@ 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:
|
||||
|
||||
```
|
||||
# Determine SDK location & build settings for Linux vs (Recent) Mac OS X
|
||||
> if [[ "$OSTYPE" == "darwin" ]]; then
|
||||
> if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
export CONAN_CONFIG="-s compiler=apple-clang -s compiler.version=10.0 -s compiler.libcxx=libc++"
|
||||
export FBXSDK_TARBALL="https://github.com/zellski/FBXSDK-Darwin/archive/2019.2.tar.gz"
|
||||
else
|
||||
elif [[ "$OSTYPE" == "linux"* ]]; then
|
||||
export CONAN_CONFIG="-s compiler.libcxx=libstdc++11"
|
||||
export FBXSDK_TARBALL="https://github.com/zellski/FBXSDK-Linux/archive/2019.2.tar.gz"
|
||||
fi
|
||||
else
|
||||
echo "This snippet only handles Mac OS X and Linux."
|
||||
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
|
||||
|
||||
|
@ -195,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
|
||||
|
@ -210,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.
|
||||
|
||||
|
@ -225,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
|
||||
|
@ -254,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.
|
||||
|
||||
|
@ -293,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
|
||||
|
@ -306,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.6",
|
||||
"version": "0.9.7-p1",
|
||||
"description": "Node wrapper around FBX2glTF tools.",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
@ -26,4 +26,4 @@
|
|||
"dependencies": {
|
||||
"rimraf": "^2.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,26 +4,31 @@
|
|||
|
||||
balanced-match@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
glob@^7.0.5:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||
glob@^7.1.3:
|
||||
version "7.1.4"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
|
||||
integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
|
@ -34,37 +39,44 @@ glob@^7.0.5:
|
|||
|
||||
inflight@^1.0.4:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
|
||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
|
||||
dependencies:
|
||||
once "^1.3.0"
|
||||
wrappy "1"
|
||||
|
||||
inherits@2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
minimatch@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
|
||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
once@^1.3.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
|
||||
|
||||
rimraf@^2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
|
||||
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
|
||||
dependencies:
|
||||
glob "^7.0.5"
|
||||
glob "^7.1.3"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||
|
|
|
@ -237,6 +237,8 @@ int main(int argc, char* argv[]) {
|
|||
->check(CLI::Range(1, 32))
|
||||
->group("Draco");
|
||||
|
||||
app.add_option("--fbx-temp-dir", gltfOptions.fbxTempDir, "Temporary directory to be used by FBX SDK.")->check(CLI::ExistingDirectory);
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
bool do_flip_u = false;
|
||||
|
@ -290,14 +292,24 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
// the path of the actual .glb or .gltf file
|
||||
std::string modelPath;
|
||||
const auto& suffix = FileUtils::GetFileSuffix(outputPath);
|
||||
|
||||
// Assume binary output if extension is glb
|
||||
if (suffix.has_value() && suffix.value() == "glb") {
|
||||
gltfOptions.outputBinary = true;
|
||||
}
|
||||
|
||||
if (gltfOptions.outputBinary) {
|
||||
const auto& suffix = FileUtils::GetFileSuffix(outputPath);
|
||||
// add .glb to output path, unless it already ends in exactly that
|
||||
if (suffix.has_value() && suffix.value() == "glb") {
|
||||
modelPath = outputPath;
|
||||
} else {
|
||||
modelPath = outputPath + ".glb";
|
||||
}
|
||||
// if the extension is gltf set the output folder to the parent directory
|
||||
} else if (suffix.has_value() && suffix.value() == "gltf") {
|
||||
outputFolder = FileUtils::getFolder(outputPath) + "/";
|
||||
modelPath = outputPath;
|
||||
} else {
|
||||
// in gltf mode, we create a folder and write into that
|
||||
outputFolder = fmt::format("{}_out/", outputPath.c_str());
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#define FBX2GLTF_VERSION std::string("0.9.6")
|
||||
#define FBX2GLTF_VERSION std::string("0.9.7")
|
||||
|
||||
#include <fmt/printf.h>
|
||||
|
||||
|
@ -122,4 +122,7 @@ struct GltfOptions {
|
|||
UseLongIndicesOptions useLongIndices = UseLongIndicesOptions::AUTO;
|
||||
/** Select baked animation framerate. */
|
||||
AnimationFramerateOptions animationFramerate = AnimationFramerateOptions::BAKE24;
|
||||
|
||||
/** Temporary directory used by FBX SDK. */
|
||||
std::string fbxTempDir;
|
||||
};
|
||||
|
|
|
@ -34,6 +34,26 @@
|
|||
|
||||
float scaleFactor;
|
||||
|
||||
static std::string NativeToUTF8(const std::string& str) {
|
||||
#if _WIN32
|
||||
char* u8cstr = nullptr;
|
||||
#if (_UNICODE || UNICODE)
|
||||
FbxWCToUTF8(reinterpret_cast<const char*>(str.c_str()), u8cstr);
|
||||
#else
|
||||
FbxAnsiToUTF8(str.c_str(), u8cstr);
|
||||
#endif
|
||||
if (!u8cstr) {
|
||||
return str;
|
||||
} else {
|
||||
std::string u8str = u8cstr;
|
||||
delete[] u8cstr;
|
||||
return u8str;
|
||||
}
|
||||
#else
|
||||
return str;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool TriangleTexturePolarity(const Vec2f& uv0, const Vec2f& uv1, const Vec2f& uv2) {
|
||||
const Vec2f d0 = uv1 - uv0;
|
||||
const Vec2f d1 = uv2 - uv0;
|
||||
|
@ -516,12 +536,12 @@ static void ReadCamera(RawModel& raw, FbxScene* pScene, FbxNode* pNode) {
|
|||
break;
|
||||
}
|
||||
case FbxCamera::EApertureMode::eHorizontal: {
|
||||
fovx = pCamera->FieldOfViewX;
|
||||
fovx = pCamera->FieldOfView;
|
||||
fovy = HFOV2VFOV(fovx, apertureRatio);
|
||||
break;
|
||||
}
|
||||
case FbxCamera::EApertureMode::eVertical: {
|
||||
fovy = pCamera->FieldOfViewY;
|
||||
fovy = pCamera->FieldOfView;
|
||||
fovx = VFOV2HFOV(fovy, 1.0 / apertureRatio);
|
||||
break;
|
||||
}
|
||||
|
@ -1073,13 +1093,26 @@ bool LoadFBXFile(
|
|||
const std::string fbxFileName,
|
||||
const std::set<std::string>& textureExtensions,
|
||||
const GltfOptions& options) {
|
||||
std::string fbxFileNameU8 = NativeToUTF8(fbxFileName);
|
||||
FbxManager* pManager = FbxManager::Create();
|
||||
|
||||
if (!options.fbxTempDir.empty()) {
|
||||
pManager->GetXRefManager().AddXRefProject("embeddedFileProject", options.fbxTempDir.c_str());
|
||||
FbxXRefManager::sEmbeddedFileProject = "embeddedFileProject";
|
||||
pManager->GetXRefManager().AddXRefProject("configurationProject", options.fbxTempDir.c_str());
|
||||
FbxXRefManager::sConfigurationProject = "configurationProject";
|
||||
pManager->GetXRefManager().AddXRefProject("localizationProject", options.fbxTempDir.c_str());
|
||||
FbxXRefManager::sLocalizationProject = "localizationProject";
|
||||
pManager->GetXRefManager().AddXRefProject("temporaryFileProject", options.fbxTempDir.c_str());
|
||||
FbxXRefManager::sTemporaryFileProject = "temporaryFileProject";
|
||||
}
|
||||
|
||||
FbxIOSettings* pIoSettings = FbxIOSettings::Create(pManager, IOSROOT);
|
||||
pManager->SetIOSettings(pIoSettings);
|
||||
|
||||
FbxImporter* pImporter = FbxImporter::Create(pManager, "");
|
||||
|
||||
if (!pImporter->Initialize(fbxFileName.c_str(), -1, pManager->GetIOSettings())) {
|
||||
if (!pImporter->Initialize(fbxFileNameU8.c_str(), -1, pManager->GetIOSettings())) {
|
||||
if (verboseOutput) {
|
||||
fmt::printf("%s\n", pImporter->GetStatus().GetErrorString());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -22,6 +22,15 @@
|
|||
#include "utils/Image_Utils.hpp"
|
||||
#include "utils/String_Utils.hpp"
|
||||
|
||||
size_t VertexHasher::operator()(const RawVertex& v) const {
|
||||
size_t seed = 5381;
|
||||
const auto hasher = std::hash<float>{};
|
||||
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;
|
||||
}
|
||||
|
||||
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) &&
|
||||
|
|
|
@ -29,9 +29,9 @@ enum RawVertexAttribute {
|
|||
};
|
||||
|
||||
struct RawBlendVertex {
|
||||
Vec3f position{};
|
||||
Vec3f normal{};
|
||||
Vec4f tangent{};
|
||||
Vec3f position{0.0f};
|
||||
Vec3f normal{0.0f};
|
||||
Vec4f tangent{0.0f};
|
||||
|
||||
bool operator==(const RawBlendVertex& other) const {
|
||||
return position == other.position && normal == other.normal && tangent == other.tangent;
|
||||
|
@ -39,8 +39,6 @@ struct RawBlendVertex {
|
|||
};
|
||||
|
||||
struct RawVertex {
|
||||
RawVertex() : polarityUv0(false), pad1(false), pad2(false), pad3(false) {}
|
||||
|
||||
Vec3f position{0.0f};
|
||||
Vec3f normal{0.0f};
|
||||
Vec3f binormal{0.0f};
|
||||
|
@ -57,12 +55,12 @@ struct RawVertex {
|
|||
int blendSurfaceIx = -1;
|
||||
// the size of this vector is always identical to the size of the corresponding
|
||||
// RawSurface.blendChannels
|
||||
std::vector<RawBlendVertex> blends{};
|
||||
std::vector<RawBlendVertex> blends;
|
||||
|
||||
bool polarityUv0;
|
||||
bool pad1;
|
||||
bool pad2;
|
||||
bool pad3;
|
||||
bool polarityUv0 = false;
|
||||
bool pad1 = false;
|
||||
bool pad2 = false;
|
||||
bool pad3 = false;
|
||||
|
||||
bool operator==(const RawVertex& other) const;
|
||||
size_t Difference(const RawVertex& other) const;
|
||||
|
@ -70,14 +68,7 @@ struct RawVertex {
|
|||
|
||||
class VertexHasher {
|
||||
public:
|
||||
size_t operator()(const RawVertex& v) const {
|
||||
size_t seed = 5381;
|
||||
const auto hasher = std::hash<float>{};
|
||||
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;
|
||||
}
|
||||
size_t operator()(const RawVertex& v) const;
|
||||
};
|
||||
|
||||
struct RawTriangle {
|
||||
|
|
Loading…
Reference in New Issue