Merge master
This commit is contained in:
commit
0d44fa3d54
|
@ -27,6 +27,7 @@ endif()
|
||||||
# DRACO
|
# DRACO
|
||||||
ExternalProject_Add(Draco
|
ExternalProject_Add(Draco
|
||||||
GIT_REPOSITORY https://github.com/google/draco
|
GIT_REPOSITORY https://github.com/google/draco
|
||||||
|
GIT_TAG 1.2.0
|
||||||
PREFIX draco
|
PREFIX draco
|
||||||
GIT_TAG 1.2.0
|
GIT_TAG 1.2.0
|
||||||
INSTALL_DIR
|
INSTALL_DIR
|
||||||
|
@ -76,6 +77,16 @@ ExternalProject_Add(Json
|
||||||
)
|
)
|
||||||
set(JSON_INCLUDE_DIR "${CMAKE_BINARY_DIR}/json/src/Json/src")
|
set(JSON_INCLUDE_DIR "${CMAKE_BINARY_DIR}/json/src/Json/src")
|
||||||
|
|
||||||
|
# stb
|
||||||
|
ExternalProject_Add(STB
|
||||||
|
PREFIX stb
|
||||||
|
GIT_REPOSITORY https://github.com/nothings/stb
|
||||||
|
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E echo "Skipping STB configure step."
|
||||||
|
BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Skipping STB build step."
|
||||||
|
INSTALL_COMMAND ${CMAKE_COMMAND} -E echo "Skipping STB install step."
|
||||||
|
)
|
||||||
|
set(STB_INCLUDE_DIR "${CMAKE_BINARY_DIR}/stb/src/STB")
|
||||||
|
|
||||||
# cppcodec
|
# cppcodec
|
||||||
ExternalProject_Add(CPPCodec
|
ExternalProject_Add(CPPCodec
|
||||||
PREFIX cppcodec
|
PREFIX cppcodec
|
||||||
|
@ -151,6 +162,7 @@ add_dependencies(FBX2glTF
|
||||||
MathFu
|
MathFu
|
||||||
FiFoMap
|
FiFoMap
|
||||||
Json
|
Json
|
||||||
|
STB
|
||||||
CxxOpts
|
CxxOpts
|
||||||
CPPCodec
|
CPPCodec
|
||||||
Fmt
|
Fmt
|
||||||
|
@ -182,6 +194,7 @@ target_include_directories(FBX2glTF PUBLIC
|
||||||
${FIFO_MAP_INCLUDE_DIR}
|
${FIFO_MAP_INCLUDE_DIR}
|
||||||
${JSON_INCLUDE_DIR}
|
${JSON_INCLUDE_DIR}
|
||||||
${CXXOPTS_INCLUDE_DIR}
|
${CXXOPTS_INCLUDE_DIR}
|
||||||
|
${STB_INCLUDE_DIR}
|
||||||
${CPPCODEC_INCLUDE_DIR}
|
${CPPCODEC_INCLUDE_DIR}
|
||||||
${FMT_INCLUDE_DIR}
|
${FMT_INCLUDE_DIR}
|
||||||
)
|
)
|
||||||
|
|
65
README.md
65
README.md
|
@ -44,9 +44,10 @@ Usage:
|
||||||
--khr-materials-common (WIP) Use KHR_materials_common extensions to
|
--khr-materials-common (WIP) Use KHR_materials_common extensions to
|
||||||
specify Unlit/Lambert/Blinn/Phong shaders.
|
specify Unlit/Lambert/Blinn/Phong shaders.
|
||||||
--pbr-metallic-roughness (WIP) Try to glean glTF 2.0 native PBR
|
--pbr-metallic-roughness (WIP) Try to glean glTF 2.0 native PBR
|
||||||
attributes from the FBX.
|
attributes from the FBX, or make a best effort
|
||||||
|
to convert from traditional shader models.
|
||||||
--pbr-specular-glossiness
|
--pbr-specular-glossiness
|
||||||
(WIP) Experimentally fill in the
|
(WIP) Very experimentally employ the
|
||||||
KHR_materials_pbrSpecularGlossiness extension.
|
KHR_materials_pbrSpecularGlossiness extension.
|
||||||
--blend-shape-normals Include blend shape normals, if reported
|
--blend-shape-normals Include blend shape normals, if reported
|
||||||
present by the FBX SDK.
|
present by the FBX SDK.
|
||||||
|
@ -192,50 +193,68 @@ With glTF 2.0, we leaped headlong into physically-based rendering (BPR), where
|
||||||
the canonical way of expressing what a mesh looks like is by describing its
|
the canonical way of expressing what a mesh looks like is by describing its
|
||||||
visible material in fundamental attributes like "how rough is this surface".
|
visible material in fundamental attributes like "how rough is this surface".
|
||||||
|
|
||||||
By contrast, FBX's material support remains in the older world of Lambert and
|
By contrast, FBX's material support remains largely in the older world of
|
||||||
Phong, with simpler and more direct illumination and shading models. These modes
|
Lambert and Phong, with simpler and more direct illumination and shading
|
||||||
are largely incompatible — for example, textures in the old workflow often
|
models. These modes are inherently incompatible — for example, textures in the
|
||||||
contain baked lighting, which would arise naturally in a PBR environment.
|
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:
|
Some material settings remain well supported and transfer automatically:
|
||||||
- Emissive constants and textures
|
- Emissive constants and textures
|
||||||
- Occlusion maps
|
- Occlusion maps
|
||||||
- Normal maps
|
- Normal maps
|
||||||
|
|
||||||
This leaves the other traditional settings of Lambert:
|
This leaves the other traditional settings, first of Lambert:
|
||||||
- Ambient — this is anathema in the PBR world, where such effects should
|
- Ambient — this is anathema in the PBR world, where such effects should
|
||||||
emerge naturally from the fundamental colour of the material and any ambient
|
emerge naturally from the fundamental colour of the material and any ambient
|
||||||
lighting present.
|
lighting present.
|
||||||
- Diffuse — the material's direction-agnostic, non-specular reflection,
|
- Diffuse — the material's direction-agnostic, non-specular reflection,
|
||||||
and additionally, with Blinn/Phong:
|
and additionally, with Blinn/Phong:
|
||||||
- Specular — a more polished material's direction-sensitive reflection,
|
- Specular — a more polished material's direction-sensitive reflection,
|
||||||
- Shininess — just how polished the material is,
|
- 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.)
|
(All these can be either constants or textures.)
|
||||||
|
|
||||||
#### Exporting as Unlit/Lambert/Phong
|
#### Exporting as Unlit/Lambert/Phong
|
||||||
Increasingly with PBR materials, these properties are just left at zero or
|
If you have a model was constructed using the traditional workflow, you may
|
||||||
default values in the FBX. But when they're there, and they're how you want the
|
choose to export it using the --khr-materials-common switch. This incurs a
|
||||||
glTF materials generated, one option is to use the --khr-materials-common
|
dependency on the glTF extension 'KHR_materials_common'; a client that accepts
|
||||||
command line switch, with the awareness that this incurs a required dependency
|
that extension is making a promise it'll do its best to render i.e. Lambert or
|
||||||
on the glTF extension `KHR_materials_common`.
|
Phong.
|
||||||
|
|
||||||
|
You can use this flag even for PBR models, but the conversion is imperfect to
|
||||||
|
say the least, and there is no reason why you would ever want to do such a
|
||||||
|
thing.
|
||||||
|
|
||||||
**Note that at the time of writing, this glTF extension is still undergoing the
|
**Note that at the time of writing, this glTF extension is still undergoing the
|
||||||
ratification process, and is furthermore likely to change names.**
|
ratification process, and is furthermore likely to change names.**
|
||||||
|
|
||||||
#### Exporting as Metallic-Roughness PBR
|
#### Exporting as Metallic-Roughness PBR
|
||||||
Given the command line flag --pbr-metallic-roughness, we accept glTF 2.0's PBR
|
Given the command line flag --pbr-metallic-roughness, we throw ourselves into
|
||||||
mode, but we do so very partially, filling in a couple of reasonable constants
|
the warm embrace of glTF 2.0's PBR preference.
|
||||||
for metalness and roughness and using the diffuse texture, if it exists, as the
|
|
||||||
`base colour` texture.
|
|
||||||
|
|
||||||
More work is needed to harness the power of glTF's 2.0's materials. The biggest
|
As mentioned above, there is lilttle consensus in the world on how PBR should be
|
||||||
issue here is the lack of any obviously emerging standards to complement FBX
|
represented in FBX. At present, we support only one format: Stingray PBS. This
|
||||||
itself. It's not clear what format an artist can export their PBR materials on,
|
is a featue that comes bundled with Maya, and any PBR model exported through
|
||||||
and when they can, how to communicate this information well to `FBX2glTF`.
|
that route should be digested propertly by FBX2glTF.
|
||||||
|
|
||||||
(*Stingray PBS* support is
|
(A happy note: Allegorithmic's Susbstance Painter also exports Stingray PBS,
|
||||||
[high on the TODO list](https://github.com/facebookincubator/FBX2glTF/issues/12).)
|
when hooked up to Maya.)
|
||||||
|
|
||||||
|
If your model is not a Stingray PBS one, but you still wish to export PBR
|
||||||
|
(perhaps you want to generate only core glTF wirhout reliance on extensions),
|
||||||
|
this converter will try its best to convert your old textures. It calculates, on
|
||||||
|
a pixel by pixel basis, reasonable values for base colour, metallicness and
|
||||||
|
roughness, using your model's diffuse, specular, and shinines textures.
|
||||||
|
|
||||||
|
It should noted here that this process cannot ever be perfect; this is very much
|
||||||
|
an apples and oranges situation.
|
||||||
|
|
||||||
|
A note of gratitude here to Gary Hsu who developed the formulae we use for this
|
||||||
|
process. They can be eyeballed
|
||||||
|
[here](https://github.com/KhronosGroup/glTF/blob/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows/js/three.pbrUtilities.js)
|
||||||
|
for the curious.
|
||||||
|
|
||||||
## Draco Compression
|
## Draco Compression
|
||||||
The tool will optionally apply [Draco](https://github.com/google/draco)
|
The tool will optionally apply [Draco](https://github.com/google/draco)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "fbx2gltf",
|
"name": "fbx2gltf",
|
||||||
"version": "0.9.2",
|
"version": "0.9.3",
|
||||||
"description": "Node wrapper around FBX2glTF tools.",
|
"description": "Node wrapper around FBX2glTF tools.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
394
src/Fbx2Raw.cpp
394
src/Fbx2Raw.cpp
|
@ -88,9 +88,102 @@ private:
|
||||||
const FbxLayerElementArrayTemplate<int> *indices;
|
const FbxLayerElementArrayTemplate<int> *indices;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FbxMaterialAccess
|
struct FbxMaterialInfo {
|
||||||
{
|
FbxMaterialInfo(const FbxString &name, const FbxString &shadingModel)
|
||||||
struct FbxMaterialProperties {
|
: name(name),
|
||||||
|
shadingModel(shadingModel)
|
||||||
|
{}
|
||||||
|
const FbxString name;
|
||||||
|
const FbxString shadingModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FbxRoughMetMaterialInfo : FbxMaterialInfo {
|
||||||
|
static constexpr const char *FBX_SHADER_METROUGH = "MetallicRoughness";
|
||||||
|
|
||||||
|
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 {};
|
||||||
|
|
||||||
|
static std::unique_ptr<FbxRoughMetMaterialInfo> From(
|
||||||
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
|
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FbxRoughMetMaterialInfo> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto getTex = [&](std::string propName) {
|
||||||
|
const FbxFileTexture *ptr = nullptr;
|
||||||
|
|
||||||
|
const FbxProperty useProp = mayaProp.FindHierarchical(("use_" + propName + "_map").c_str());
|
||||||
|
if (useProp.IsValid() && useProp.Get<bool>()) {
|
||||||
|
const FbxProperty texProp = mayaProp.FindHierarchical(("TEX_" + propName + "_map").c_str());
|
||||||
|
if (texProp.IsValid()) {
|
||||||
|
ptr = texProp.GetSrcObject<FbxFileTexture>();
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getVec = [&](std::string propName) -> FbxDouble3 {
|
||||||
|
const FbxProperty vecProp = mayaProp.FindHierarchical(propName.c_str());
|
||||||
|
return vecProp.IsValid() ? vecProp.Get<FbxDouble3>() : FbxDouble3(1, 1, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getVal = [&](std::string propName) -> FbxDouble {
|
||||||
|
const FbxProperty vecProp = mayaProp.FindHierarchical(propName .c_str());
|
||||||
|
return vecProp.IsValid() ? vecProp.Get<FbxDouble>() : 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");
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
FbxTraditionalMaterialInfo(const FbxString &name, const FbxString &shadingModel)
|
||||||
|
: FbxMaterialInfo(name, shadingModel)
|
||||||
|
{}
|
||||||
|
|
||||||
FbxFileTexture *texAmbient {};
|
FbxFileTexture *texAmbient {};
|
||||||
FbxVector4 colAmbient {};
|
FbxVector4 colAmbient {};
|
||||||
FbxFileTexture *texSpecular {};
|
FbxFileTexture *texSpecular {};
|
||||||
|
@ -102,80 +195,12 @@ class FbxMaterialAccess
|
||||||
FbxFileTexture *texNormal {};
|
FbxFileTexture *texNormal {};
|
||||||
FbxFileTexture *texShininess {};
|
FbxFileTexture *texShininess {};
|
||||||
FbxDouble shininess {};
|
FbxDouble shininess {};
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
static std::unique_ptr<FbxTraditionalMaterialInfo> From(
|
||||||
const FbxSurfaceMaterial *fbxMaterial;
|
FbxSurfaceMaterial *fbxMaterial,
|
||||||
const std::map<const FbxTexture *, FbxString> &textureLocations;
|
const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
|
||||||
public:
|
|
||||||
const FbxString name;
|
|
||||||
const FbxString shadingModel;
|
|
||||||
|
|
||||||
const struct FbxMaterialProperties props;
|
|
||||||
|
|
||||||
explicit FbxMaterialAccess(
|
|
||||||
const FbxSurfaceMaterial *fbxMaterial, const std::map<const FbxTexture *, FbxString> &textureNames) :
|
|
||||||
fbxMaterial(fbxMaterial),
|
|
||||||
name(fbxMaterial->GetName()),
|
|
||||||
shadingModel(fbxMaterial->ShadingModel),
|
|
||||||
textureLocations(textureNames),
|
|
||||||
props(extractTextures())
|
|
||||||
{}
|
|
||||||
|
|
||||||
struct FbxMaterialProperties extractTextures() {
|
|
||||||
struct FbxMaterialProperties res;
|
|
||||||
|
|
||||||
// four properties are on the same structure and follow the same rules
|
|
||||||
auto handleBasicProperty = [&](const char *colName, const char *facName) {
|
|
||||||
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
|
|
||||||
std::tie(std::ignore, res.texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap);
|
|
||||||
|
|
||||||
// shininess can be a map or a factor
|
|
||||||
std::tie(res.shininess, res.texShininess) = getSurfaceScalar(FbxSurfaceMaterial::sShininess);
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<FbxDouble, FbxFileTexture *> getSurfaceScalar(const char *propName) const
|
|
||||||
{
|
{
|
||||||
|
auto getSurfaceScalar = [&](const char *propName) -> std::tuple<FbxDouble, FbxFileTexture *> {
|
||||||
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
||||||
|
|
||||||
FbxDouble val(0);
|
FbxDouble val(0);
|
||||||
|
@ -187,10 +212,9 @@ public:
|
||||||
val = prop.Get<FbxDouble>();
|
val = prop.Get<FbxDouble>();
|
||||||
}
|
}
|
||||||
return std::make_tuple(val, tex);
|
return std::make_tuple(val, tex);
|
||||||
}
|
};
|
||||||
|
|
||||||
std::tuple<FbxDouble3, FbxFileTexture *> getSurfaceVector(const char *propName) const
|
auto getSurfaceVector = [&](const char *propName) -> std::tuple<FbxDouble3, FbxFileTexture *> {
|
||||||
{
|
|
||||||
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
const FbxProperty prop = fbxMaterial->FindProperty(propName);
|
||||||
|
|
||||||
FbxDouble3 val(1, 1, 1);
|
FbxDouble3 val(1, 1, 1);
|
||||||
|
@ -202,10 +226,9 @@ public:
|
||||||
val = prop.Get<FbxDouble3>();
|
val = prop.Get<FbxDouble3>();
|
||||||
}
|
}
|
||||||
return std::make_tuple(val, tex);
|
return std::make_tuple(val, tex);
|
||||||
}
|
};
|
||||||
|
|
||||||
std::tuple<FbxVector4, FbxFileTexture *, FbxFileTexture *> getSurfaceValues(const char *colName, const char *facName) const
|
auto getSurfaceValues = [&](const char *colName, const char *facName) -> std::tuple<FbxVector4, FbxFileTexture *, FbxFileTexture *> {
|
||||||
{
|
|
||||||
const FbxProperty colProp = fbxMaterial->FindProperty(colName);
|
const FbxProperty colProp = fbxMaterial->FindProperty(colName);
|
||||||
const FbxProperty facProp = fbxMaterial->FindProperty(facName);
|
const FbxProperty facProp = fbxMaterial->FindProperty(facName);
|
||||||
|
|
||||||
|
@ -234,8 +257,71 @@ public:
|
||||||
factorVal);
|
factorVal);
|
||||||
return std::make_tuple(val, colTex, facTex);
|
return std::make_tuple(val, colTex, facTex);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string name = fbxMaterial->GetName();
|
||||||
|
std::unique_ptr<FbxTraditionalMaterialInfo> 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<FbxVector4, FbxFileTexture *>{
|
||||||
|
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
|
||||||
|
std::tie(std::ignore, res->texNormal) = getSurfaceVector(FbxSurfaceMaterial::sNormalMap);
|
||||||
|
|
||||||
|
// shininess can be a map or a factor
|
||||||
|
std::tie(res->shininess, res->texShininess) = getSurfaceScalar(FbxSurfaceMaterial::sShininess);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
std::unique_ptr<FbxMaterialInfo>
|
||||||
|
GetMaterialInfo(FbxSurfaceMaterial *material, const std::map<const FbxTexture *, FbxString> &textureLocations)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FbxMaterialInfo> res;
|
||||||
|
res = FbxRoughMetMaterialInfo::From(material, textureLocations);
|
||||||
|
if (!res) {
|
||||||
|
res = FbxTraditionalMaterialInfo::From(material, textureLocations);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
class FbxMaterialsAccess
|
class FbxMaterialsAccess
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -273,14 +359,14 @@ public:
|
||||||
}
|
}
|
||||||
auto summary = summaries[materialNum];
|
auto summary = summaries[materialNum];
|
||||||
if (summary == nullptr) {
|
if (summary == nullptr) {
|
||||||
summary = summaries[materialNum] = std::make_shared<FbxMaterialAccess>(
|
summary = summaries[materialNum] = GetMaterialInfo(
|
||||||
mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum),
|
mesh->GetNode()->GetSrcObject<FbxSurfaceMaterial>(materialNum),
|
||||||
textureLocations);
|
textureLocations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::shared_ptr<FbxMaterialAccess> GetMaterial(const int polygonIndex) const
|
const std::shared_ptr<FbxMaterialInfo> GetMaterial(const int polygonIndex) const
|
||||||
{
|
{
|
||||||
if (mappingMode != FbxGeometryElement::eNone) {
|
if (mappingMode != FbxGeometryElement::eNone) {
|
||||||
const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0);
|
const int materialNum = indices->GetAt((mappingMode == FbxGeometryElement::eByPolygon) ? polygonIndex : 0);
|
||||||
|
@ -294,7 +380,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FbxGeometryElement::EMappingMode mappingMode;
|
FbxGeometryElement::EMappingMode mappingMode;
|
||||||
std::vector<std::shared_ptr<FbxMaterialAccess>> summaries {};
|
std::vector<std::shared_ptr<FbxMaterialInfo>> summaries {};
|
||||||
const FbxMesh *mesh;
|
const FbxMesh *mesh;
|
||||||
const FbxLayerElementArrayTemplate<int> *indices;
|
const FbxLayerElementArrayTemplate<int> *indices;
|
||||||
};
|
};
|
||||||
|
@ -338,7 +424,7 @@ public:
|
||||||
inverseBindMatrices.emplace_back(globalBindposeInverseMatrix);
|
inverseBindMatrices.emplace_back(globalBindposeInverseMatrix);
|
||||||
|
|
||||||
jointNodes.push_back(cluster->GetLink());
|
jointNodes.push_back(cluster->GetLink());
|
||||||
jointNames.push_back(*cluster->GetLink()->GetName() != '\0' ? cluster->GetLink()->GetName() : cluster->GetName());
|
jointIds.push_back(cluster->GetLink()->GetUniqueID());
|
||||||
|
|
||||||
const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform();
|
const FbxAMatrix globalNodeTransform = cluster->GetLink()->EvaluateGlobalTransform();
|
||||||
jointSkinningTransforms.push_back(FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix));
|
jointSkinningTransforms.push_back(FbxMatrix(globalNodeTransform * globalBindposeInverseMatrix));
|
||||||
|
@ -400,9 +486,9 @@ public:
|
||||||
return jointNodes[jointIndex];
|
return jointNodes[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *GetJointName(const int jointIndex) const
|
const long GetJointId(const int jointIndex) const
|
||||||
{
|
{
|
||||||
return jointNames[jointIndex].c_str();
|
return jointIds[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const FbxMatrix &GetJointSkinningTransform(const int jointIndex) const
|
const FbxMatrix &GetJointSkinningTransform(const int jointIndex) const
|
||||||
|
@ -415,10 +501,10 @@ public:
|
||||||
return jointInverseGlobalTransforms[jointIndex];
|
return jointInverseGlobalTransforms[jointIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *GetRootNode() const
|
const long GetRootNode() const
|
||||||
{
|
{
|
||||||
assert(rootIndex != -1);
|
assert(rootIndex != -1);
|
||||||
return jointNames[rootIndex].c_str();
|
return jointIds[rootIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
const FbxAMatrix &GetInverseBindMatrix(const int jointIndex) const
|
const FbxAMatrix &GetInverseBindMatrix(const int jointIndex) const
|
||||||
|
@ -440,7 +526,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int rootIndex;
|
int rootIndex;
|
||||||
std::vector<std::string> jointNames;
|
std::vector<long> jointIds;
|
||||||
std::vector<FbxNode *> jointNodes;
|
std::vector<FbxNode *> jointNodes;
|
||||||
std::vector<FbxMatrix> jointSkinningTransforms;
|
std::vector<FbxMatrix> jointSkinningTransforms;
|
||||||
std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
std::vector<FbxMatrix> jointInverseGlobalTransforms;
|
||||||
|
@ -607,8 +693,23 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
meshConverter.Triangulate(pNode->GetNodeAttribute(), true);
|
meshConverter.Triangulate(pNode->GetNodeAttribute(), true);
|
||||||
FbxMesh *pMesh = pNode->GetMesh();
|
FbxMesh *pMesh = pNode->GetMesh();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw.GetSurfaceById(surfaceId) >= 0) {
|
||||||
|
// This surface is already loaded
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const char *meshName = (pNode->GetName()[0] != '\0') ? pNode->GetName() : pMesh->GetName();
|
const char *meshName = (pNode->GetName()[0] != '\0') ? pNode->GetName() : pMesh->GetName();
|
||||||
const int rawSurfaceIndex = raw.AddSurface(meshName, pNode->GetName());
|
const int rawSurfaceIndex = raw.AddSurface(meshName, surfaceId);
|
||||||
|
|
||||||
const FbxVector4 *controlPoints = pMesh->GetControlPoints();
|
const FbxVector4 *controlPoints = pMesh->GetControlPoints();
|
||||||
const FbxLayerElementAccess<FbxVector4> normalLayer(pMesh->GetElementNormal(), pMesh->GetElementNormalCount());
|
const FbxLayerElementAccess<FbxVector4> normalLayer(pMesh->GetElementNormal(), pMesh->GetElementNormalCount());
|
||||||
|
@ -624,7 +725,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
"mesh %d: %s (skinned: %s)\n", rawSurfaceIndex, meshName,
|
"mesh %d: %s (skinned: %s)\n", rawSurfaceIndex, meshName,
|
||||||
skinning.IsSkinned() ? skinning.GetRootNode() : "NO");
|
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 geometric transformation describes how a FbxNodeAttribute is offset from
|
||||||
|
@ -638,7 +739,10 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
const FbxVector4 meshScaling = pNode->GetGeometricScaling(FbxNode::eSourcePivot);
|
const FbxVector4 meshScaling = pNode->GetGeometricScaling(FbxNode::eSourcePivot);
|
||||||
const FbxAMatrix meshTransform(meshTranslation, meshRotation, meshScaling);
|
const FbxAMatrix meshTransform(meshTranslation, meshRotation, meshScaling);
|
||||||
const FbxMatrix transform = meshTransform;
|
const FbxMatrix transform = meshTransform;
|
||||||
const FbxMatrix inverseTransposeTransform = transform.Inverse().Transpose();
|
|
||||||
|
// 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);
|
raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_POSITION);
|
||||||
if (normalLayer.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_NORMAL); }
|
if (normalLayer.LayerPresent()) { raw.AddVertexAttribute(RAW_VERTEX_ATTRIBUTE_NORMAL); }
|
||||||
|
@ -654,12 +758,12 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
|
|
||||||
RawSurface &rawSurface = raw.GetSurface(rawSurfaceIndex);
|
RawSurface &rawSurface = raw.GetSurface(rawSurfaceIndex);
|
||||||
|
|
||||||
rawSurface.skeletonRootName = (skinning.IsSkinned()) ? skinning.GetRootNode() : pNode->GetName();
|
rawSurface.skeletonRootId = (skinning.IsSkinned()) ? skinning.GetRootNode() : pNode->GetUniqueID();
|
||||||
for (int jointIndex = 0; jointIndex < skinning.GetNodeCount(); jointIndex++) {
|
for (int jointIndex = 0; jointIndex < skinning.GetNodeCount(); jointIndex++) {
|
||||||
const char *jointName = skinning.GetJointName(jointIndex);
|
const long jointId = skinning.GetJointId(jointIndex);
|
||||||
raw.GetNode(raw.GetNodeByName(jointName)).isJoint = true;
|
raw.GetNode(raw.GetNodeById(jointId)).isJoint = true;
|
||||||
|
|
||||||
rawSurface.jointNames.emplace_back(jointName);
|
rawSurface.jointIds.emplace_back(jointId);
|
||||||
rawSurface.inverseBindMatrices.push_back(toMat4f(skinning.GetInverseBindMatrix(jointIndex)));
|
rawSurface.inverseBindMatrices.push_back(toMat4f(skinning.GetInverseBindMatrix(jointIndex)));
|
||||||
rawSurface.jointGeometryMins.emplace_back(FLT_MAX, FLT_MAX, FLT_MAX);
|
rawSurface.jointGeometryMins.emplace_back(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||||
rawSurface.jointGeometryMaxs.emplace_back(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
rawSurface.jointGeometryMaxs.emplace_back(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
||||||
|
@ -684,27 +788,23 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
|
|
||||||
for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) {
|
for (int polygonIndex = 0; polygonIndex < pMesh->GetPolygonCount(); polygonIndex++) {
|
||||||
FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3);
|
FBX_ASSERT(pMesh->GetPolygonSize(polygonIndex) == 3);
|
||||||
|
const std::shared_ptr<FbxMaterialInfo> fbxMaterial = materials.GetMaterial(polygonIndex);
|
||||||
const std::shared_ptr<FbxMaterialAccess> fbxMaterial = materials.GetMaterial(polygonIndex);
|
|
||||||
|
|
||||||
int textures[RAW_TEXTURE_USAGE_MAX];
|
int textures[RAW_TEXTURE_USAGE_MAX];
|
||||||
std::fill_n(textures, RAW_TEXTURE_USAGE_MAX, -1);
|
std::fill_n(textures, (int) RAW_TEXTURE_USAGE_MAX, -1);
|
||||||
|
|
||||||
FbxString shadingModel, materialName;
|
std::shared_ptr<RawMatProps> rawMatProps;
|
||||||
FbxVector4 ambient, specular, diffuse, emissive;
|
FbxString materialName;
|
||||||
FbxDouble shininess;
|
|
||||||
|
|
||||||
if (fbxMaterial == nullptr) {
|
if (fbxMaterial == nullptr) {
|
||||||
materialName = "DefaultMaterial";
|
materialName = "DefaultMaterial";
|
||||||
shadingModel = "Lambert";
|
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));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
materialName = fbxMaterial->name;
|
materialName = fbxMaterial->name;
|
||||||
shadingModel = fbxMaterial->shadingModel;
|
|
||||||
|
|
||||||
const auto &matProps = fbxMaterial->props;
|
const auto maybeAddTexture = [&](const FbxFileTexture *tex, RawTextureUsage usage) {
|
||||||
|
|
||||||
const auto maybeAddTexture = [&](FbxFileTexture *tex, RawTextureUsage usage) {
|
|
||||||
if (tex != nullptr) {
|
if (tex != nullptr) {
|
||||||
// dig out the inferred filename from the textureLocations map
|
// dig out the inferred filename from the textureLocations map
|
||||||
FbxString inferredPath = textureLocations.find(tex)->second;
|
FbxString inferredPath = textureLocations.find(tex)->second;
|
||||||
|
@ -712,19 +812,44 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ambient = matProps.colAmbient;
|
std::shared_ptr<RawMatProps> matInfo;
|
||||||
maybeAddTexture(matProps.texAmbient, RAW_TEXTURE_USAGE_AMBIENT);
|
if (fbxMaterial->shadingModel == FbxRoughMetMaterialInfo::FBX_SHADER_METROUGH) {
|
||||||
specular = matProps.colSpecular;
|
FbxRoughMetMaterialInfo *fbxMatInfo = static_cast<FbxRoughMetMaterialInfo *>(fbxMaterial.get());
|
||||||
maybeAddTexture(matProps.texSpecular, RAW_TEXTURE_USAGE_SPECULAR);
|
|
||||||
diffuse = matProps.colDiffuse;
|
|
||||||
maybeAddTexture(matProps.texDiffuse, RAW_TEXTURE_USAGE_DIFFUSE);
|
|
||||||
emissive = matProps.colEmissive;
|
|
||||||
maybeAddTexture(matProps.texEmissive, RAW_TEXTURE_USAGE_EMISSIVE);
|
|
||||||
|
|
||||||
maybeAddTexture(matProps.texNormal, RAW_TEXTURE_USAGE_NORMAL);
|
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 {
|
||||||
|
|
||||||
shininess = matProps.shininess;
|
FbxTraditionalMaterialInfo *fbxMatInfo = static_cast<FbxTraditionalMaterialInfo *>(fbxMaterial.get());
|
||||||
maybeAddTexture(matProps.texShininess, RAW_TEXTURE_USAGE_SHININESS);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RawVertex rawVertices[3];
|
RawVertex rawVertices[3];
|
||||||
|
@ -852,9 +977,7 @@ static void ReadMesh(RawModel &raw, FbxScene *pScene, FbxNode *pNode, const std:
|
||||||
}
|
}
|
||||||
|
|
||||||
const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned());
|
const RawMaterialType materialType = GetMaterialType(raw, textures, vertexTransparency, skinning.IsSkinned());
|
||||||
const int rawMaterialIndex = raw.AddMaterial(
|
const int rawMaterialIndex = raw.AddMaterial(materialName, materialType, textures, rawMatProps);
|
||||||
materialName, shadingModel, materialType, textures,
|
|
||||||
toVec3f(ambient), toVec4f(diffuse), toVec3f(specular), toVec3f(emissive), shininess);
|
|
||||||
|
|
||||||
raw.AddTriangle(rawVertexIndices[0], rawVertexIndices[1], rawVertexIndices[2], rawMaterialIndex, rawSurfaceIndex);
|
raw.AddTriangle(rawVertexIndices[0], rawVertexIndices[1], rawVertexIndices[2], rawMaterialIndex, rawSurfaceIndex);
|
||||||
}
|
}
|
||||||
|
@ -865,12 +988,12 @@ static void ReadCamera(RawModel &raw, FbxScene *pScene, FbxNode *pNode)
|
||||||
const FbxCamera *pCamera = pNode->GetCamera();
|
const FbxCamera *pCamera = pNode->GetCamera();
|
||||||
if (pCamera->ProjectionType.Get() == FbxCamera::EProjectionType::ePerspective) {
|
if (pCamera->ProjectionType.Get() == FbxCamera::EProjectionType::ePerspective) {
|
||||||
raw.AddCameraPerspective(
|
raw.AddCameraPerspective(
|
||||||
"", pNode->GetName(), (float) pCamera->FilmAspectRatio,
|
"", pNode->GetUniqueID(), (float) pCamera->FilmAspectRatio,
|
||||||
(float) pCamera->FieldOfViewX, (float) pCamera->FieldOfViewX,
|
(float) pCamera->FieldOfViewX, (float) pCamera->FieldOfViewX,
|
||||||
(float) pCamera->NearPlane, (float) pCamera->FarPlane);
|
(float) pCamera->NearPlane, (float) pCamera->FarPlane);
|
||||||
} else {
|
} else {
|
||||||
raw.AddCameraOrthographic(
|
raw.AddCameraOrthographic(
|
||||||
"", pNode->GetName(),
|
"", pNode->GetUniqueID(),
|
||||||
(float) pCamera->OrthoZoom, (float) pCamera->OrthoZoom,
|
(float) pCamera->OrthoZoom, (float) pCamera->OrthoZoom,
|
||||||
(float) pCamera->FarPlane, (float) pCamera->NearPlane);
|
(float) pCamera->FarPlane, (float) pCamera->NearPlane);
|
||||||
}
|
}
|
||||||
|
@ -946,10 +1069,11 @@ static FbxVector4 computeLocalScale(FbxNode *pNode, FbxTime pTime = FBXSDK_TIME_
|
||||||
|
|
||||||
static void ReadNodeHierarchy(
|
static void ReadNodeHierarchy(
|
||||||
RawModel &raw, FbxScene *pScene, FbxNode *pNode,
|
RawModel &raw, FbxScene *pScene, FbxNode *pNode,
|
||||||
const std::string &parentName, const std::string &path)
|
const long parentId, const std::string &path)
|
||||||
{
|
{
|
||||||
|
const FbxUInt64 nodeId = pNode->GetUniqueID();
|
||||||
const char *nodeName = pNode->GetName();
|
const char *nodeName = pNode->GetName();
|
||||||
const int nodeIndex = raw.AddNode(nodeName, parentName.c_str());
|
const int nodeIndex = raw.AddNode(nodeId, nodeName, parentId);
|
||||||
RawNode &node = raw.GetNode(nodeIndex);
|
RawNode &node = raw.GetNode(nodeIndex);
|
||||||
|
|
||||||
FbxTransform::EInheritType lInheritType;
|
FbxTransform::EInheritType lInheritType;
|
||||||
|
@ -962,7 +1086,7 @@ static void ReadNodeHierarchy(
|
||||||
|
|
||||||
static int warnRrSsCount = 0;
|
static int warnRrSsCount = 0;
|
||||||
static int warnRrsCount = 0;
|
static int warnRrsCount = 0;
|
||||||
if (lInheritType == FbxTransform::eInheritRrSs && !parentName.empty()) {
|
if (lInheritType == FbxTransform::eInheritRrSs && parentId) {
|
||||||
if (++warnRrSsCount == 1) {
|
if (++warnRrSsCount == 1) {
|
||||||
fmt::printf("Warning: node %s uses unsupported transform inheritance type 'eInheritRrSs'.\n", newPath);
|
fmt::printf("Warning: node %s uses unsupported transform inheritance type 'eInheritRrSs'.\n", newPath);
|
||||||
fmt::printf(" (Further warnings of this type squelched.)\n");
|
fmt::printf(" (Further warnings of this type squelched.)\n");
|
||||||
|
@ -989,19 +1113,19 @@ static void ReadNodeHierarchy(
|
||||||
node.rotation = toQuatf(localRotation);
|
node.rotation = toQuatf(localRotation);
|
||||||
node.scale = toVec3f(localScaling);
|
node.scale = toVec3f(localScaling);
|
||||||
|
|
||||||
if (parentName.size() > 0) {
|
if (parentId) {
|
||||||
RawNode &parentNode = raw.GetNode(raw.GetNodeByName(parentName.c_str()));
|
RawNode &parentNode = raw.GetNode(raw.GetNodeById(parentId));
|
||||||
// Add unique child name to the parent node.
|
// Add unique child name to the parent node.
|
||||||
if (std::find(parentNode.childNames.begin(), parentNode.childNames.end(), nodeName) == parentNode.childNames.end()) {
|
if (std::find(parentNode.childIds.begin(), parentNode.childIds.end(), nodeId) == parentNode.childIds.end()) {
|
||||||
parentNode.childNames.push_back(nodeName);
|
parentNode.childIds.push_back(nodeId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If there is no parent then this is the root node.
|
// If there is no parent then this is the root node.
|
||||||
raw.SetRootNode(nodeName);
|
raw.SetRootNode(nodeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int child = 0; child < pNode->GetChildCount(); child++) {
|
for (int child = 0; child < pNode->GetChildCount(); child++) {
|
||||||
ReadNodeHierarchy(raw, pScene, pNode->GetChild(child), nodeName, newPath);
|
ReadNodeHierarchy(raw, pScene, pNode->GetChild(child), nodeId, newPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1057,7 +1181,7 @@ static void ReadAnimations(RawModel &raw, FbxScene *pScene)
|
||||||
bool hasMorphs = false;
|
bool hasMorphs = false;
|
||||||
|
|
||||||
RawChannel channel;
|
RawChannel channel;
|
||||||
channel.nodeIndex = raw.GetNodeByName(pNode->GetName());
|
channel.nodeIndex = raw.GetNodeById(pNode->GetUniqueID());
|
||||||
|
|
||||||
for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) {
|
for (FbxLongLong frameIndex = firstFrameIndex; frameIndex <= lastFrameIndex; frameIndex++) {
|
||||||
FbxTime pTime;
|
FbxTime pTime;
|
||||||
|
@ -1300,7 +1424,7 @@ bool LoadFBXFile(RawModel &raw, const char *fbxFileName, const char *textureExte
|
||||||
FbxSystemUnit::m.ConvertScene(pScene);
|
FbxSystemUnit::m.ConvertScene(pScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), "", "");
|
ReadNodeHierarchy(raw, pScene, pScene->GetRootNode(), 0, "");
|
||||||
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
|
ReadNodeAttributes(raw, pScene, pScene->GetRootNode(), textureLocations);
|
||||||
ReadAnimations(raw, pScene);
|
ReadAnimations(raw, pScene);
|
||||||
|
|
||||||
|
|
644
src/Raw2Gltf.cpp
644
src/Raw2Gltf.cpp
|
@ -12,6 +12,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include <stb_image.h>
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "FBX2glTF.h"
|
#include "FBX2glTF.h"
|
||||||
#include "utils/String_Utils.h"
|
#include "utils/String_Utils.h"
|
||||||
#include "utils/Image_Utils.h"
|
#include "utils/Image_Utils.h"
|
||||||
|
@ -217,13 +222,29 @@ struct GLTFData
|
||||||
Holder<SceneData> scenes;
|
Holder<SceneData> scenes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void WriteToVectorContext(void *context, void *data, int size) {
|
||||||
|
auto *vec = static_cast<std::vector<char> *>(context);
|
||||||
|
for (int ii = 0; ii < size; ii ++) {
|
||||||
|
vec->push_back(((char *) data)[ii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method sanity-checks existance and then returns a *reference* to the *Data instance
|
* This method sanity-checks existance and then returns a *reference* to the *Data instance
|
||||||
* registered under that name. This is safe in the context of this tool, where all such data
|
* 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.
|
* classes are guaranteed to stick around for the duration of the process.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T &require(std::map<std::string, std::shared_ptr<T>> map, std::string key)
|
T &require(std::map<std::string, std::shared_ptr<T>> map, const std::string &key)
|
||||||
|
{
|
||||||
|
auto iter = map.find(key);
|
||||||
|
assert(iter != map.end());
|
||||||
|
T &result = *iter->second;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T &require(std::map<long, std::shared_ptr<T>> map, long key)
|
||||||
{
|
{
|
||||||
auto iter = map.find(key);
|
auto iter = map.find(key);
|
||||||
assert(iter != map.end());
|
assert(iter != map.end());
|
||||||
|
@ -260,7 +281,7 @@ ModelData *Raw2Gltf(
|
||||||
for (int i = 0; i < raw.GetMaterialCount(); i++) {
|
for (int i = 0; i < raw.GetMaterialCount(); i++) {
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
"Material %d: %s [shading: %s]\n", i, raw.GetMaterial(i).name.c_str(),
|
"Material %d: %s [shading: %s]\n", i, raw.GetMaterial(i).name.c_str(),
|
||||||
raw.GetMaterial(i).shadingModel.c_str());
|
Describe(raw.GetMaterial(i).info->shadingModel));
|
||||||
}
|
}
|
||||||
if (raw.GetVertexCount() > 2 * raw.GetTriangleCount()) {
|
if (raw.GetVertexCount() > 2 * raw.GetTriangleCount()) {
|
||||||
fmt::printf(
|
fmt::printf(
|
||||||
|
@ -282,9 +303,10 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
std::unique_ptr<GLTFData> gltf(new GLTFData(options.outputBinary));
|
std::unique_ptr<GLTFData> gltf(new GLTFData(options.outputBinary));
|
||||||
|
|
||||||
std::map<std::string, std::shared_ptr<NodeData>> nodesByName;
|
std::map<long, std::shared_ptr<NodeData>> nodesById;
|
||||||
std::map<std::string, std::shared_ptr<MaterialData>> materialsByName;
|
std::map<std::string, std::shared_ptr<MaterialData>> materialsByName;
|
||||||
std::map<std::string, std::shared_ptr<MeshData>> meshByNodeName;
|
std::map<std::string, std::shared_ptr<TextureData>> textureByIndicesKey;
|
||||||
|
std::map<long, std::shared_ptr<MeshData>> meshBySurfaceId;
|
||||||
|
|
||||||
// for now, we only have one buffer; data->binary points to the same vector as that BufferData does.
|
// for now, we only have one buffer; data->binary points to the same vector as that BufferData does.
|
||||||
BufferData &buffer = *gltf->buffers.hold(
|
BufferData &buffer = *gltf->buffers.hold(
|
||||||
|
@ -303,13 +325,13 @@ ModelData *Raw2Gltf(
|
||||||
auto nodeData = gltf->nodes.hold(
|
auto nodeData = gltf->nodes.hold(
|
||||||
new NodeData(node.name, node.translation, node.rotation, node.scale, node.isJoint));
|
new NodeData(node.name, node.translation, node.rotation, node.scale, node.isJoint));
|
||||||
|
|
||||||
for (const auto &childName : node.childNames) {
|
for (const auto &childId : node.childIds) {
|
||||||
int childIx = raw.GetNodeByName(childName.c_str());
|
int childIx = raw.GetNodeById(childId);
|
||||||
assert(childIx >= 0);
|
assert(childIx >= 0);
|
||||||
nodeData->AddChildNode(childIx);
|
nodeData->AddChildNode(childIx);
|
||||||
}
|
}
|
||||||
assert(nodesByName.find(nodeData->name) == nodesByName.end());
|
|
||||||
nodesByName.insert(std::make_pair(nodeData->name, nodeData));
|
nodesById.insert(std::make_pair(node.id, nodeData));
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -344,7 +366,7 @@ ModelData *Raw2Gltf(
|
||||||
channel.scales.size(), channel.weights.size());
|
channel.scales.size(), channel.weights.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeData &nDat = require(nodesByName, node.name);
|
NodeData &nDat = require(nodesById, node.id);
|
||||||
if (!channel.translations.empty()) {
|
if (!channel.translations.empty()) {
|
||||||
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), "translation");
|
aDat.AddNodeChannel(nDat, *gltf->AddAccessorAndView(buffer, GLT_VEC3F, channel.translations), "translation");
|
||||||
}
|
}
|
||||||
|
@ -370,23 +392,204 @@ ModelData *Raw2Gltf(
|
||||||
// textures
|
// textures
|
||||||
//
|
//
|
||||||
|
|
||||||
for (int textureIndex = 0; textureIndex < raw.GetTextureCount(); textureIndex++) {
|
using pixel = std::array<float, 4>; // pixel components are floats in [0, 1]
|
||||||
const RawTexture &texture = raw.GetTexture(textureIndex);
|
using pixel_merger = std::function<pixel(const std::vector<const pixel *>)>;
|
||||||
const std::string textureName = Gltf::StringUtils::GetFileBaseString(texture.name);
|
|
||||||
const std::string relativeFilename = Gltf::StringUtils::GetFileNameString(texture.fileLocation);
|
|
||||||
|
|
||||||
ImageData *source = nullptr;
|
auto texIndicesKey = [&](std::vector<int> ixVec, std::string tag) -> std::string {
|
||||||
|
std::string result = tag;
|
||||||
|
for (int ix : ixVec) {
|
||||||
|
result += "_" + std::to_string(ix);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new derived TextureData for the two given RawTexture indexes, or return a previously created one.
|
||||||
|
* Each pixel in the derived texture will be determined from its equivalent in each source pixels, as decided
|
||||||
|
* by the provided `combine` function.
|
||||||
|
*/
|
||||||
|
auto getDerivedTexture = [&](
|
||||||
|
std::vector<int> rawTexIndices,
|
||||||
|
const pixel_merger &combine,
|
||||||
|
const std::string &tag,
|
||||||
|
bool transparentOutput
|
||||||
|
) -> std::shared_ptr<TextureData>
|
||||||
|
{
|
||||||
|
const std::string key = texIndicesKey(rawTexIndices, tag);
|
||||||
|
auto iter = textureByIndicesKey.find(key);
|
||||||
|
if (iter != textureByIndicesKey.end()) {
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto describeChannel = [&](int channels) -> std::string {
|
||||||
|
switch(channels) {
|
||||||
|
case 1: return "G";
|
||||||
|
case 2: return "GA";
|
||||||
|
case 3: return "RGB";
|
||||||
|
case 4: return "RGBA";
|
||||||
|
default:
|
||||||
|
return fmt::format("?%d?", channels);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// keep track of some texture data as we load them
|
||||||
|
struct TexInfo {
|
||||||
|
explicit TexInfo(int rawTexIx) : rawTexIx(rawTexIx) {}
|
||||||
|
|
||||||
|
const int rawTexIx;
|
||||||
|
int width {};
|
||||||
|
int height {};
|
||||||
|
int channels {};
|
||||||
|
uint8_t *pixels {};
|
||||||
|
};
|
||||||
|
|
||||||
|
int width = -1, height = -1;
|
||||||
|
std::string mergedName = tag;
|
||||||
|
std::string mergedFilename = tag;
|
||||||
|
std::vector<TexInfo> texes { };
|
||||||
|
for (const int rawTexIx : rawTexIndices) {
|
||||||
|
TexInfo info(rawTexIx);
|
||||||
|
if (rawTexIx >= 0) {
|
||||||
|
const RawTexture &rawTex = raw.GetTexture(rawTexIx);
|
||||||
|
const std::string &fileLoc = rawTex.fileLocation;
|
||||||
|
const std::string &fileLocBase = Gltf::StringUtils::GetFileBaseString(Gltf::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,
|
||||||
|
Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc)));
|
||||||
|
} 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",
|
||||||
|
Gltf::StringUtils::GetFileBaseString(Gltf::StringUtils::GetFileNameString(fileLoc)),
|
||||||
|
info.width, info.height, width, height);
|
||||||
|
// this is bad enough that we abort the whole merge
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
mergedName += "_" + rawTex.fileName;
|
||||||
|
mergedFilename += "_" + fileLocBase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
texes.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = transparentOutput ? 4 : 3;
|
||||||
|
|
||||||
|
std::vector<uint8_t> mergedPixels(static_cast<size_t>(channels * width * height));
|
||||||
|
std::vector<pixel> pixels(texes.size());
|
||||||
|
std::vector<const pixel *> 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 = combine(pixelPointers);
|
||||||
|
int ii = channels * (xx + yy*width);
|
||||||
|
for (int jj = 0; jj < channels; jj ++) {
|
||||||
|
mergedPixels[ii + jj] = static_cast<uint8_t>(fmax(0, fmin(255.0f, merged[jj] * 255.0f)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write a .png iff we need transparency in the destination texture
|
||||||
|
bool png = transparentOutput;
|
||||||
|
|
||||||
|
std::vector<char> 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) {
|
if (options.outputBinary) {
|
||||||
auto bufferView = gltf->AddBufferViewForFile(buffer, texture.fileLocation);
|
const auto bufferView = gltf->AddRawBufferView(buffer, 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<TextureData> texDat = gltf->textures.hold(
|
||||||
|
new TextureData(mergedName, 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. */
|
||||||
|
auto getSimpleTexture = [&](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 = Gltf::StringUtils::GetFileBaseString(rawTexture.name);
|
||||||
|
const std::string relativeFilename = Gltf::StringUtils::GetFileNameString(rawTexture.fileLocation);
|
||||||
|
|
||||||
|
ImageData *image = nullptr;
|
||||||
|
if (options.outputBinary) {
|
||||||
|
auto bufferView = gltf->AddBufferViewForFile(buffer, rawTexture.fileLocation);
|
||||||
if (bufferView) {
|
if (bufferView) {
|
||||||
std::string suffix = Gltf::StringUtils::GetFileSuffixString(texture.fileLocation);
|
std::string suffix = Gltf::StringUtils::GetFileSuffixString(rawTexture.fileLocation);
|
||||||
source = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix));
|
image = new ImageData(relativeFilename, *bufferView, suffixToMimeType(suffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (!relativeFilename.empty()) {
|
} else if (!relativeFilename.empty()) {
|
||||||
source = new ImageData(relativeFilename, relativeFilename);
|
image = new ImageData(relativeFilename, relativeFilename);
|
||||||
std::string outputPath = outputFolder + relativeFilename;
|
std::string outputPath = outputFolder + relativeFilename;
|
||||||
if (FileUtils::CopyFile(texture.fileLocation, outputPath)) {
|
if (FileUtils::CopyFile(rawTexture.fileLocation, outputPath)) {
|
||||||
if (verboseOutput) {
|
if (verboseOutput) {
|
||||||
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);
|
fmt::printf("Copied texture '%s' to output folder: %s\n", textureName, outputPath);
|
||||||
}
|
}
|
||||||
|
@ -396,15 +599,16 @@ ModelData *Raw2Gltf(
|
||||||
// reference, even if the copy failed.
|
// reference, even if the copy failed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!source) {
|
if (!image) {
|
||||||
// fallback is tiny transparent gif
|
// fallback is tiny transparent gif
|
||||||
source = new ImageData(textureName, "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=");
|
image = new ImageData(textureName, "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=");
|
||||||
}
|
}
|
||||||
|
|
||||||
const TextureData &texDat = *gltf->textures.hold(
|
std::shared_ptr<TextureData> texDat = gltf->textures.hold(
|
||||||
new TextureData(textureName, defaultSampler, *gltf->images.hold(source)));
|
new TextureData(textureName, defaultSampler, *gltf->images.hold(image)));
|
||||||
assert(texDat.ix == textureIndex);
|
textureByIndicesKey.insert(std::make_pair(key, texDat));
|
||||||
}
|
return texDat;
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// materials
|
// materials
|
||||||
|
@ -416,42 +620,290 @@ ModelData *Raw2Gltf(
|
||||||
material.type == RAW_MATERIAL_TYPE_TRANSPARENT ||
|
material.type == RAW_MATERIAL_TYPE_TRANSPARENT ||
|
||||||
material.type == RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT;
|
material.type == RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT;
|
||||||
|
|
||||||
// find a texture by usage and return it as a TextureData*, or nullptr if none exists.
|
Vec3f emissiveFactor;
|
||||||
auto getTex = [&](RawTextureUsage usage)
|
float emissiveIntensity;
|
||||||
{
|
|
||||||
// note that we depend on TextureData.ix == rawTexture's index
|
const Vec3f dielectric(0.04f, 0.04f, 0.04f);
|
||||||
return (material.textures[usage] >= 0) ? gltf->textures.ptrs[material.textures[usage]].get() : nullptr;
|
const float epsilon = 1e-6f;
|
||||||
|
|
||||||
|
// acquire the texture of a specific RawTextureUsage as *TextData, or nullptr if none exists
|
||||||
|
auto simpleTex = [&](RawTextureUsage usage) -> std::shared_ptr<TextureData> {
|
||||||
|
return (material.textures[usage] >= 0) ? getSimpleTexture(material.textures[usage], "simple") : nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// acquire derived texture of two RawTextureUsage as *TextData, or nullptr if neither exists
|
||||||
|
auto merge2Tex = [&](
|
||||||
|
const std::string tag,
|
||||||
|
RawTextureUsage u1,
|
||||||
|
RawTextureUsage u2,
|
||||||
|
const pixel_merger &combine,
|
||||||
|
bool outputHasAlpha
|
||||||
|
) -> std::shared_ptr<TextureData> {
|
||||||
|
return getDerivedTexture(
|
||||||
|
{ material.textures[u1], material.textures[u2] },
|
||||||
|
combine, tag, outputHasAlpha);
|
||||||
|
};
|
||||||
|
|
||||||
|
// acquire derived texture of two RawTextureUsage as *TextData, or nullptr if neither exists
|
||||||
|
auto merge3Tex = [&](
|
||||||
|
const std::string tag,
|
||||||
|
RawTextureUsage u1,
|
||||||
|
RawTextureUsage u2,
|
||||||
|
RawTextureUsage u3,
|
||||||
|
const pixel_merger &combine,
|
||||||
|
bool outputHasAlpha
|
||||||
|
) -> std::shared_ptr<TextureData> {
|
||||||
|
return getDerivedTexture(
|
||||||
|
{ material.textures[u1], material.textures[u2], material.textures[u3] },
|
||||||
|
combine, tag, outputHasAlpha);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto getMaxComponent = [&](const Vec3f &color) {
|
||||||
|
return fmax(color.x, fmax(color.y, color.z));
|
||||||
|
};
|
||||||
|
auto getPerceivedBrightness = [&](const Vec3f &color) {
|
||||||
|
return sqrt(0.299 * color.x * color.x + 0.587 * color.y * color.y + 0.114 * color.z * color.z);
|
||||||
|
};
|
||||||
|
auto toVec3f = [&](const pixel &pix) -> const Vec3f {
|
||||||
|
return Vec3f(pix[0], pix[1], pix[2]);
|
||||||
|
};
|
||||||
|
auto toVec4f = [&](const pixel &pix) -> const Vec4f {
|
||||||
|
return Vec4f(pix[0], pix[1], pix[2], pix[3]);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<PBRMetallicRoughness> pbrMetRough;
|
std::shared_ptr<PBRMetallicRoughness> pbrMetRough;
|
||||||
if (options.usePBRMetRough) {
|
if (options.usePBRMetRough) {
|
||||||
pbrMetRough.reset(new PBRMetallicRoughness(getTex(RAW_TEXTURE_USAGE_DIFFUSE), material.diffuseFactor));
|
// albedo is a basic texture, no merging needed
|
||||||
|
std::shared_ptr<TextureData> baseColorTex, metRoughTex;
|
||||||
|
|
||||||
|
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
|
||||||
|
metRoughTex = merge2Tex("met_rough",
|
||||||
|
RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
|
||||||
|
[&](const std::vector<const pixel *> pixels) -> pixel { return { 0, (*pixels[1])[0], (*pixels[0])[0], 0 }; },
|
||||||
|
false);
|
||||||
|
baseColorTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
metallic = props->metallic;
|
||||||
|
roughness = props->roughness;
|
||||||
|
emissiveFactor = props->emissiveFactor;
|
||||||
|
emissiveIntensity = props->emissiveIntensity;
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* Traditional FBX Material -> PBR Met/Rough glTF.
|
||||||
|
*
|
||||||
|
* Diffuse channel is used as base colour. No metallic/roughness texture is attempted. Constant
|
||||||
|
* metallic/roughness values are hard-coded.
|
||||||
|
*
|
||||||
|
* TODO: If we have specular & optional shininess map, we can generate a reasonable rough/met map.
|
||||||
|
*/
|
||||||
|
const RawTraditionalMatProps *props = ((RawTraditionalMatProps *) material.info.get());
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
// TODO: make configurable on the command line, or do a better job guessing from other, supplied params
|
||||||
|
if (material.info->shadingModel == RAW_SHADING_MODEL_LAMBERT) {
|
||||||
|
metallic = 0.6f;
|
||||||
|
roughness = 1.0f;
|
||||||
|
} else {
|
||||||
|
metallic = 0.2f;
|
||||||
|
roughness = 0.6f;
|
||||||
}
|
}
|
||||||
|
auto solveMetallic = [&](float pDiff, float pSpec, float oneMinusSpecularStrength)
|
||||||
|
{
|
||||||
|
if (pSpec < dielectric.x) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float a = dielectric.x;
|
||||||
|
float b = pDiff * oneMinusSpecularStrength / (1 - dielectric.x) + pSpec - 2 * dielectric.x;
|
||||||
|
float c = dielectric.x - pSpec;
|
||||||
|
float D = fmax(b * b - 4 * a * c, 0);
|
||||||
|
return fmax(0.0, fmin(1.0, (-b + sqrt(D)) / (2 * a)));
|
||||||
|
};
|
||||||
|
metRoughTex = merge3Tex("rough_met",
|
||||||
|
RAW_TEXTURE_USAGE_SPECULAR, RAW_TEXTURE_USAGE_SHININESS, RAW_TEXTURE_USAGE_DIFFUSE,
|
||||||
|
[&](const std::vector<const pixel *> pixels) -> pixel {
|
||||||
|
const Vec3f specular = pixels[0] ? toVec3f(*pixels[0]) : props->specularFactor;
|
||||||
|
float shininess = pixels[1] ? (*pixels[1])[0] : props->shininess;
|
||||||
|
const Vec4f diffuse = pixels[2] ? toVec4f(*pixels[2]) : props->diffuseFactor;
|
||||||
|
|
||||||
|
float pixelMet = solveMetallic(
|
||||||
|
getPerceivedBrightness(diffuse.xyz()),
|
||||||
|
getPerceivedBrightness(specular),
|
||||||
|
1 - getMaxComponent(specular));
|
||||||
|
float pixelRough = 1 - shininess;
|
||||||
|
|
||||||
|
return { 0, pixelRough, pixelMet, 0 };
|
||||||
|
}, false);
|
||||||
|
if (material.textures[RAW_TEXTURE_USAGE_DIFFUSE] >= 0) {
|
||||||
|
const RawTexture &diffuseTex = raw.GetTexture(material.textures[RAW_TEXTURE_USAGE_DIFFUSE]);
|
||||||
|
baseColorTex = merge2Tex("base_col", RAW_TEXTURE_USAGE_DIFFUSE, RAW_TEXTURE_USAGE_SPECULAR,
|
||||||
|
[&](const std::vector<const pixel *> pixels) -> pixel {
|
||||||
|
const Vec4f diffuse = pixels[0] ? toVec4f(*pixels[0]) : props->diffuseFactor;
|
||||||
|
const Vec3f specular = pixels[1] ? toVec3f(*pixels[1]) : props->specularFactor;
|
||||||
|
|
||||||
|
float oneMinus = 1 - getMaxComponent(specular);
|
||||||
|
|
||||||
|
float pixelMet = solveMetallic(
|
||||||
|
getPerceivedBrightness(diffuse.xyz()),
|
||||||
|
getPerceivedBrightness(specular),
|
||||||
|
oneMinus);
|
||||||
|
|
||||||
|
Vec3f fromDiffuse = diffuse.xyz() * (oneMinus / (1.0f - dielectric.x) / fmax(1.0f - pixelMet, epsilon));
|
||||||
|
Vec3f fromSpecular = specular - dielectric * (1.0f - pixelMet) * (1.0f / fmax(pixelMet, epsilon));
|
||||||
|
Vec3f baseColor = Vec3f::Lerp(fromDiffuse, fromSpecular, pixelMet * pixelMet);
|
||||||
|
|
||||||
|
return { baseColor[0], baseColor[1], baseColor[2], diffuse[3] };
|
||||||
|
}, diffuseTex.occlusion == RAW_TEXTURE_OCCLUSION_TRANSPARENT);
|
||||||
|
}
|
||||||
|
emissiveFactor = props->emissiveFactor;
|
||||||
|
emissiveIntensity = 1.0f;
|
||||||
|
}
|
||||||
|
pbrMetRough.reset(new PBRMetallicRoughness(baseColorTex.get(), metRoughTex.get(), diffuseFactor, metallic, roughness));
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<PBRSpecularGlossiness> pbrSpecGloss;
|
std::shared_ptr<PBRSpecularGlossiness> pbrSpecGloss;
|
||||||
if (options.usePBRSpecGloss) {
|
if (options.usePBRSpecGloss) {
|
||||||
|
Vec4f diffuseFactor;
|
||||||
|
Vec3f specularFactor;
|
||||||
|
float glossiness;
|
||||||
|
std::shared_ptr<TextureData> specGlossTex;
|
||||||
|
std::shared_ptr<TextureData> diffuseTex;
|
||||||
|
|
||||||
|
const Vec3f dielectric(0.04f, 0.04f, 0.04f);
|
||||||
|
const float epsilon = 1e-6f;
|
||||||
|
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
||||||
|
/**
|
||||||
|
* PBR FBX Material -> PBR Spec/Gloss glTF.
|
||||||
|
*
|
||||||
|
* TODO: A complete mess. Low priority.
|
||||||
|
*/
|
||||||
|
RawMetRoughMatProps *props = (RawMetRoughMatProps *) material.info.get();
|
||||||
|
// we can estimate spec/gloss from met/rough by between Vec4f(0.04, 0.04, 0.04) and baseColor
|
||||||
|
// according to metalness, and then taking gloss to be the inverse of roughness
|
||||||
|
specGlossTex = merge3Tex("specgloss",
|
||||||
|
RAW_TEXTURE_USAGE_ALBEDO, RAW_TEXTURE_USAGE_METALLIC, RAW_TEXTURE_USAGE_ROUGHNESS,
|
||||||
|
[&](const std::vector<const pixel *> pixels) -> pixel {
|
||||||
|
Vec3f baseColor(1.0f);
|
||||||
|
if (pixels[0]) {
|
||||||
|
baseColor.x = (*pixels[0])[0];
|
||||||
|
baseColor.y = (*pixels[0])[1];
|
||||||
|
baseColor.z = (*pixels[0])[2];
|
||||||
|
}
|
||||||
|
float metallic = pixels[1] ? (*pixels[1])[0] : 1.0f;
|
||||||
|
float roughness = pixels[2] ? (*pixels[2])[0] : 1.0f;
|
||||||
|
Vec3f spec = Vec3f::Lerp(dielectric, baseColor, metallic);
|
||||||
|
return { spec[0], spec[1], spec[2], 1.0f - roughness };
|
||||||
|
}, false);
|
||||||
|
diffuseTex = merge2Tex("albedo",
|
||||||
|
RAW_TEXTURE_USAGE_ALBEDO, RAW_TEXTURE_USAGE_METALLIC,
|
||||||
|
[&](const std::vector<const pixel *> pixels) -> pixel {
|
||||||
|
Vec3f baseColor(1.0f);
|
||||||
|
float alpha = 1.0f;
|
||||||
|
if (pixels[0]) {
|
||||||
|
baseColor[0] = (*pixels[0])[0];
|
||||||
|
baseColor[1] = (*pixels[0])[1];
|
||||||
|
baseColor[2] = (*pixels[0])[2];
|
||||||
|
alpha = (*pixels[0])[3];
|
||||||
|
}
|
||||||
|
float metallic = pixels[1] ? (*pixels[1])[0] : 1.0f;
|
||||||
|
Vec3f spec = Vec3f::Lerp(dielectric, baseColor, metallic);
|
||||||
|
float maxSpecComp = fmax(fmax(spec.x, spec.y), spec.z);
|
||||||
|
// attenuate baseColor to get specgloss-compliant diffuse; for details consult
|
||||||
|
// https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_pbrSpecularGlossiness
|
||||||
|
Vec3f diffuse = baseColor * (1 - dielectric[0]) * (1 - metallic) * fmax(1.0f - maxSpecComp, epsilon);
|
||||||
|
return { diffuse[0], diffuse[1], diffuse[2], alpha };
|
||||||
|
}, true);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
specularFactor = Vec3f::Lerp(dielectric, props->diffuseFactor.xyz(), props->metallic);
|
||||||
|
glossiness = 1.0f - props->roughness;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* Traditional FBX Material -> PBR Spec/Gloss glTF.
|
||||||
|
*
|
||||||
|
* TODO: A complete mess. Low priority.
|
||||||
|
*/
|
||||||
|
// TODO: this section a ludicrous over-simplifictation; we can surely do better.
|
||||||
|
const RawTraditionalMatProps *props = ((RawTraditionalMatProps *) material.info.get());
|
||||||
|
specGlossTex = merge2Tex("specgloss",
|
||||||
|
RAW_TEXTURE_USAGE_SPECULAR, RAW_TEXTURE_USAGE_SHININESS,
|
||||||
|
[&](const std::vector<const pixel *> pixels) -> pixel {
|
||||||
|
const auto &spec = *(pixels[0]);
|
||||||
|
const auto &shine = *(pixels[1]);
|
||||||
|
return { spec[0], spec[1], spec[2], shine[0] };
|
||||||
|
}, false);
|
||||||
|
diffuseTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
specularFactor = props->specularFactor;
|
||||||
|
glossiness = props->shininess;
|
||||||
|
}
|
||||||
|
|
||||||
pbrSpecGloss.reset(
|
pbrSpecGloss.reset(
|
||||||
new PBRSpecularGlossiness(
|
new PBRSpecularGlossiness(
|
||||||
getTex(RAW_TEXTURE_USAGE_DIFFUSE), material.diffuseFactor,
|
diffuseTex.get(), diffuseFactor, specGlossTex.get(), specularFactor, glossiness));
|
||||||
getTex(RAW_TEXTURE_USAGE_SPECULAR), material.specularFactor, material.shininess));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<KHRCommonMats> khrComMat;
|
std::shared_ptr<KHRCommonMats> khrComMat;
|
||||||
if (options.useKHRMatCom) {
|
if (options.useKHRMatCom) {
|
||||||
|
float shininess;
|
||||||
|
Vec3f ambientFactor, specularFactor;
|
||||||
|
Vec4f diffuseFactor;
|
||||||
|
std::shared_ptr<TextureData> diffuseTex;
|
||||||
auto type = KHRCommonMats::MaterialType::Constant;
|
auto type = KHRCommonMats::MaterialType::Constant;
|
||||||
if (material.shadingModel == "Lambert") {
|
|
||||||
|
if (material.info->shadingModel == RAW_SHADING_MODEL_PBR_MET_ROUGH) {
|
||||||
|
/**
|
||||||
|
* PBR FBX Material -> KHR Common Materials glTF.
|
||||||
|
*
|
||||||
|
* TODO: We can use the specularFactor calculation below to generate a reasonable specular map, too.
|
||||||
|
*/
|
||||||
|
const RawMetRoughMatProps *props = (RawMetRoughMatProps *) material.info.get();
|
||||||
|
shininess = 1.0f - props->roughness;
|
||||||
|
ambientFactor = Vec3f(0.0f, 0.0f, 0.0f);
|
||||||
|
diffuseTex = simpleTex(RAW_TEXTURE_USAGE_ALBEDO);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
specularFactor = Vec3f::Lerp(Vec3f(0.04f, 0.04f, 0.04f), props->diffuseFactor.xyz(), props->metallic);
|
||||||
|
// render as Phong if there's any specularity, otherwise Lambert
|
||||||
|
type = (specularFactor.LengthSquared() > 1e-4) ?
|
||||||
|
KHRCommonMats::MaterialType::Phong : KHRCommonMats::MaterialType::Lambert;
|
||||||
|
// TODO: convert textures
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* Traditional FBX Material -> KHR Common Materials glTF.
|
||||||
|
*
|
||||||
|
* Should be in good shape. Essentially pass-through.
|
||||||
|
*/
|
||||||
|
const RawTraditionalMatProps *props = (RawTraditionalMatProps *) material.info.get();
|
||||||
|
shininess = props->shininess;
|
||||||
|
ambientFactor = props->ambientFactor;
|
||||||
|
diffuseTex = simpleTex(RAW_TEXTURE_USAGE_DIFFUSE);
|
||||||
|
diffuseFactor = props->diffuseFactor;
|
||||||
|
specularFactor = props->specularFactor;
|
||||||
|
|
||||||
|
if (material.info->shadingModel == RAW_SHADING_MODEL_LAMBERT) {
|
||||||
type = KHRCommonMats::MaterialType::Lambert;
|
type = KHRCommonMats::MaterialType::Lambert;
|
||||||
} else if (material.shadingModel == "Blinn") {
|
} else if (material.info->shadingModel == RAW_SHADING_MODEL_BLINN) {
|
||||||
type = KHRCommonMats::MaterialType::Blinn;
|
type = KHRCommonMats::MaterialType::Blinn;
|
||||||
} else if (material.shadingModel == "Phong") {
|
} else if (material.info->shadingModel == RAW_SHADING_MODEL_PHONG) {
|
||||||
type = KHRCommonMats::MaterialType::Phong;
|
type = KHRCommonMats::MaterialType::Phong;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
khrComMat.reset(
|
khrComMat.reset(
|
||||||
new KHRCommonMats(
|
new KHRCommonMats(type,
|
||||||
type,
|
simpleTex(RAW_TEXTURE_USAGE_SHININESS).get(), shininess,
|
||||||
getTex(RAW_TEXTURE_USAGE_SHININESS), material.shininess,
|
simpleTex(RAW_TEXTURE_USAGE_AMBIENT).get(), ambientFactor,
|
||||||
getTex(RAW_TEXTURE_USAGE_AMBIENT), material.ambientFactor,
|
diffuseTex.get(), diffuseFactor,
|
||||||
getTex(RAW_TEXTURE_USAGE_DIFFUSE), material.diffuseFactor,
|
simpleTex(RAW_TEXTURE_USAGE_SPECULAR).get(), specularFactor));
|
||||||
getTex(RAW_TEXTURE_USAGE_SPECULAR), material.specularFactor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<KHRCmnConstantMaterial> khrCmnConstantMat;
|
std::shared_ptr<KHRCmnConstantMaterial> khrCmnConstantMat;
|
||||||
|
@ -464,38 +916,27 @@ ModelData *Raw2Gltf(
|
||||||
|
|
||||||
std::shared_ptr<MaterialData> mData = gltf->materials.hold(
|
std::shared_ptr<MaterialData> mData = gltf->materials.hold(
|
||||||
new MaterialData(
|
new MaterialData(
|
||||||
material.name, isTransparent, getTex(RAW_TEXTURE_USAGE_NORMAL),
|
material.name, isTransparent,
|
||||||
getTex(RAW_TEXTURE_USAGE_EMISSIVE), material.emissiveFactor,
|
simpleTex(RAW_TEXTURE_USAGE_NORMAL).get(), simpleTex(RAW_TEXTURE_USAGE_EMISSIVE).get(),
|
||||||
|
emissiveFactor * emissiveIntensity,
|
||||||
khrComMat, khrCmnConstantMat, pbrMetRough, pbrSpecGloss));
|
khrComMat, khrCmnConstantMat, pbrMetRough, pbrSpecGloss));
|
||||||
materialsByName[materialHash(material)] = mData;
|
materialsByName[materialHash(material)] = mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// surfaces
|
|
||||||
//
|
|
||||||
|
|
||||||
// in GLTF 2.0, the structural limitation is that a node can
|
|
||||||
// only belong to a single mesh. A mesh can however contain any
|
|
||||||
// number of primitives, which are essentially little meshes.
|
|
||||||
//
|
|
||||||
// so each RawSurface turns into a primitive, and we sort them
|
|
||||||
// by root node using this map; one mesh per node.
|
|
||||||
|
|
||||||
for (size_t surfaceIndex = 0; surfaceIndex < materialModels.size(); surfaceIndex++) {
|
for (size_t surfaceIndex = 0; surfaceIndex < materialModels.size(); surfaceIndex++) {
|
||||||
const RawModel &surfaceModel = materialModels[surfaceIndex];
|
const RawModel &surfaceModel = materialModels[surfaceIndex];
|
||||||
|
|
||||||
assert(surfaceModel.GetSurfaceCount() == 1);
|
assert(surfaceModel.GetSurfaceCount() == 1);
|
||||||
const RawSurface &rawSurface = surfaceModel.GetSurface(0);
|
const RawSurface &rawSurface = surfaceModel.GetSurface(0);
|
||||||
|
const int surfaceId = rawSurface.id;
|
||||||
|
|
||||||
const RawMaterial &rawMaterial = surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex);
|
const RawMaterial &rawMaterial = surfaceModel.GetMaterial(surfaceModel.GetTriangle(0).materialIndex);
|
||||||
const MaterialData &mData = require(materialsByName, materialHash(rawMaterial));
|
const MaterialData &mData = require(materialsByName, materialHash(rawMaterial));
|
||||||
|
|
||||||
std::string nodeName = rawSurface.nodeName;
|
|
||||||
NodeData &meshNode = require(nodesByName, nodeName);
|
|
||||||
|
|
||||||
MeshData *mesh = nullptr;
|
MeshData *mesh = nullptr;
|
||||||
auto meshIter = meshByNodeName.find(nodeName);
|
auto meshIter = meshBySurfaceId.find(surfaceId);
|
||||||
if (meshIter != meshByNodeName.end()) {
|
if (meshIter != meshBySurfaceId.end()) {
|
||||||
mesh = meshIter->second.get();
|
mesh = meshIter->second.get();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -504,36 +945,10 @@ ModelData *Raw2Gltf(
|
||||||
defaultDeforms.push_back(channel.defaultDeform);
|
defaultDeforms.push_back(channel.defaultDeform);
|
||||||
}
|
}
|
||||||
auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms));
|
auto meshPtr = gltf->meshes.hold(new MeshData(rawSurface.name, defaultDeforms));
|
||||||
meshByNodeName[nodeName] = meshPtr;
|
meshBySurfaceId[surfaceId] = meshPtr;
|
||||||
meshNode.SetMesh(meshPtr->ix);
|
|
||||||
mesh = meshPtr.get();
|
mesh = meshPtr.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// surface skin
|
|
||||||
//
|
|
||||||
if (!rawSurface.jointNames.empty()) {
|
|
||||||
if (meshNode.skin == -1) {
|
|
||||||
// glTF uses column-major matrices
|
|
||||||
std::vector<Mat4f> inverseBindMatrices;
|
|
||||||
for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) {
|
|
||||||
inverseBindMatrices.push_back(inverseBindMatrice.Transpose());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint32_t> jointIndexes;
|
|
||||||
for (const auto &jointName : rawSurface.jointNames) {
|
|
||||||
jointIndexes.push_back(require(nodesByName, jointName).ix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write out inverseBindMatrices
|
|
||||||
auto accIBM = gltf->AddAccessorAndView(buffer, GLT_MAT4F, inverseBindMatrices);
|
|
||||||
|
|
||||||
auto skeletonRoot = require(nodesByName, rawSurface.skeletonRootName);
|
|
||||||
auto skin = *gltf->skins.hold(new SkinData(jointIndexes, *accIBM, skeletonRoot));
|
|
||||||
meshNode.SetSkin(skin.ix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<PrimitiveData> primitive;
|
std::shared_ptr<PrimitiveData> primitive;
|
||||||
if (options.useDraco) {
|
if (options.useDraco) {
|
||||||
int triangleCount = surfaceModel.GetTriangleCount();
|
int triangleCount = surfaceModel.GetTriangleCount();
|
||||||
|
@ -674,6 +1089,53 @@ ModelData *Raw2Gltf(
|
||||||
mesh->AddPrimitive(primitive);
|
mesh->AddPrimitive(primitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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<Mat4f> inverseBindMatrices;
|
||||||
|
for (const auto &inverseBindMatrice : rawSurface.inverseBindMatrices) {
|
||||||
|
inverseBindMatrices.push_back(inverseBindMatrice.Transpose());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> 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
|
// cameras
|
||||||
//
|
//
|
||||||
|
@ -698,16 +1160,16 @@ ModelData *Raw2Gltf(
|
||||||
}
|
}
|
||||||
// Add the camera to the node hierarchy.
|
// Add the camera to the node hierarchy.
|
||||||
|
|
||||||
auto iter = nodesByName.find(cam.nodeName);
|
auto iter = nodesById.find(cam.nodeId);
|
||||||
if (iter == nodesByName.end()) {
|
if (iter == nodesById.end()) {
|
||||||
fmt::printf("Warning: Camera node name %s does not exist.\n", cam.nodeName);
|
fmt::printf("Warning: Camera node id %s does not exist.\n", cam.nodeId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
iter->second->AddCamera(cam.name);
|
iter->second->SetCamera(camera.ix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeData &rootNode = require(nodesByName, "RootNode");
|
NodeData &rootNode = require(nodesById, raw.GetRootNode());
|
||||||
const SceneData &rootScene = *gltf->scenes.hold(new SceneData(defaultSceneName, rootNode));
|
const SceneData &rootScene = *gltf->scenes.hold(new SceneData(defaultSceneName, rootNode));
|
||||||
|
|
||||||
if (options.outputBinary) {
|
if (options.outputBinary) {
|
||||||
|
|
|
@ -111,35 +111,25 @@ int RawModel::AddTexture(const std::string &name, const std::string &fileName, c
|
||||||
|
|
||||||
int RawModel::AddMaterial(const RawMaterial &material)
|
int RawModel::AddMaterial(const RawMaterial &material)
|
||||||
{
|
{
|
||||||
return AddMaterial(
|
return AddMaterial(material.name.c_str(), material.type, material.textures, material.info);
|
||||||
material.name.c_str(), material.shadingModel.c_str(), material.type, material.textures, material.ambientFactor,
|
|
||||||
material.diffuseFactor, material.specularFactor, material.emissiveFactor, material.shininess);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::AddMaterial(
|
int RawModel::AddMaterial(
|
||||||
const char *name, const char *shadingModel, const RawMaterialType materialType,
|
const char *name,
|
||||||
const int textures[RAW_TEXTURE_USAGE_MAX], const Vec3f ambientFactor,
|
const RawMaterialType materialType,
|
||||||
const Vec4f diffuseFactor, const Vec3f specularFactor,
|
const int textures[RAW_TEXTURE_USAGE_MAX],
|
||||||
const Vec3f emissiveFactor, float shinineness)
|
std::shared_ptr<RawMatProps> materialInfo)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < materials.size(); i++) {
|
for (size_t i = 0; i < materials.size(); i++) {
|
||||||
if (materials[i].name != name) {
|
if (materials[i].name != name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (materials[i].shadingModel != shadingModel) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (materials[i].type != materialType) {
|
if (materials[i].type != materialType) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (materials[i].ambientFactor != ambientFactor ||
|
if (*(materials[i].info) != *materialInfo) {
|
||||||
materials[i].diffuseFactor != diffuseFactor ||
|
|
||||||
materials[i].specularFactor != specularFactor ||
|
|
||||||
materials[i].emissiveFactor != emissiveFactor ||
|
|
||||||
materials[i].shininess != shinineness) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool match = true;
|
bool match = true;
|
||||||
for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) {
|
for (int j = 0; match && j < RAW_TEXTURE_USAGE_MAX; j++) {
|
||||||
match = match && (materials[i].textures[j] == textures[j]);
|
match = match && (materials[i].textures[j] == textures[j]);
|
||||||
|
@ -151,13 +141,8 @@ int RawModel::AddMaterial(
|
||||||
|
|
||||||
RawMaterial material;
|
RawMaterial material;
|
||||||
material.name = name;
|
material.name = name;
|
||||||
material.shadingModel = shadingModel;
|
|
||||||
material.type = materialType;
|
material.type = materialType;
|
||||||
material.ambientFactor = ambientFactor;
|
material.info = materialInfo;
|
||||||
material.diffuseFactor = diffuseFactor;
|
|
||||||
material.specularFactor = specularFactor;
|
|
||||||
material.emissiveFactor = emissiveFactor;
|
|
||||||
material.shininess = shinineness;
|
|
||||||
|
|
||||||
for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) {
|
for (int i = 0; i < RAW_TEXTURE_USAGE_MAX; i++) {
|
||||||
material.textures[i] = textures[i];
|
material.textures[i] = textures[i];
|
||||||
|
@ -180,18 +165,18 @@ int RawModel::AddSurface(const RawSurface &surface)
|
||||||
return (int) (surfaces.size() - 1);
|
return (int) (surfaces.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::AddSurface(const char *name, const char *nodeName)
|
int RawModel::AddSurface(const char *name, const long surfaceId)
|
||||||
{
|
{
|
||||||
assert(name[0] != '\0');
|
assert(name[0] != '\0');
|
||||||
|
|
||||||
for (size_t i = 0; i < surfaces.size(); i++) {
|
for (size_t i = 0; i < surfaces.size(); i++) {
|
||||||
if (Gltf::StringUtils::CompareNoCase(surfaces[i].name, name) == 0) {
|
if (surfaces[i].id == surfaceId) {
|
||||||
return (int) i;
|
return (int) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RawSurface surface;
|
RawSurface surface;
|
||||||
|
surface.id = surfaceId;
|
||||||
surface.name = name;
|
surface.name = name;
|
||||||
surface.nodeName = nodeName;
|
|
||||||
surface.bounds.Clear();
|
surface.bounds.Clear();
|
||||||
surface.discrete = false;
|
surface.discrete = false;
|
||||||
|
|
||||||
|
@ -208,8 +193,8 @@ int RawModel::AddAnimation(const RawAnimation &animation)
|
||||||
int RawModel::AddNode(const RawNode &node)
|
int RawModel::AddNode(const RawNode &node)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < nodes.size(); i++) {
|
for (size_t i = 0; i < nodes.size(); i++) {
|
||||||
if (Gltf::StringUtils::CompareNoCase(nodes[i].name.c_str(), node.name) == 0) {
|
if (nodes[i].id == node.id) {
|
||||||
return (int) i;
|
return (int)i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,12 +203,12 @@ int RawModel::AddNode(const RawNode &node)
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::AddCameraPerspective(
|
int RawModel::AddCameraPerspective(
|
||||||
const char *name, const char *nodeName, const float aspectRatio, const float fovDegreesX, const float fovDegreesY, const float nearZ,
|
const char *name, const long nodeId, const float aspectRatio, const float fovDegreesX, const float fovDegreesY, const float nearZ,
|
||||||
const float farZ)
|
const float farZ)
|
||||||
{
|
{
|
||||||
RawCamera camera;
|
RawCamera camera;
|
||||||
camera.name = name;
|
camera.name = name;
|
||||||
camera.nodeName = nodeName;
|
camera.nodeId = nodeId;
|
||||||
camera.mode = RawCamera::CAMERA_MODE_PERSPECTIVE;
|
camera.mode = RawCamera::CAMERA_MODE_PERSPECTIVE;
|
||||||
camera.perspective.aspectRatio = aspectRatio;
|
camera.perspective.aspectRatio = aspectRatio;
|
||||||
camera.perspective.fovDegreesX = fovDegreesX;
|
camera.perspective.fovDegreesX = fovDegreesX;
|
||||||
|
@ -235,11 +220,11 @@ int RawModel::AddCameraPerspective(
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::AddCameraOrthographic(
|
int RawModel::AddCameraOrthographic(
|
||||||
const char *name, const char *nodeName, const float magX, const float magY, const float nearZ, const float farZ)
|
const char *name, const long nodeId, const float magX, const float magY, const float nearZ, const float farZ)
|
||||||
{
|
{
|
||||||
RawCamera camera;
|
RawCamera camera;
|
||||||
camera.name = name;
|
camera.name = name;
|
||||||
camera.nodeName = nodeName;
|
camera.nodeId = nodeId;
|
||||||
camera.mode = RawCamera::CAMERA_MODE_ORTHOGRAPHIC;
|
camera.mode = RawCamera::CAMERA_MODE_ORTHOGRAPHIC;
|
||||||
camera.orthographic.magX = magX;
|
camera.orthographic.magX = magX;
|
||||||
camera.orthographic.magY = magY;
|
camera.orthographic.magY = magY;
|
||||||
|
@ -249,20 +234,22 @@ int RawModel::AddCameraOrthographic(
|
||||||
return (int) cameras.size() - 1;
|
return (int) cameras.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::AddNode(const char *name, const char *parentName)
|
int RawModel::AddNode(const long id, const char *name, const long parentId)
|
||||||
{
|
{
|
||||||
assert(name[0] != '\0');
|
assert(name[0] != '\0');
|
||||||
|
|
||||||
for (size_t i = 0; i < nodes.size(); i++) {
|
for (size_t i = 0; i < nodes.size(); i++) {
|
||||||
if (Gltf::StringUtils::CompareNoCase(nodes[i].name, name) == 0) {
|
if (nodes[i].id == id ) {
|
||||||
return (int) i;
|
return (int) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RawNode joint;
|
RawNode joint;
|
||||||
joint.isJoint = false;
|
joint.isJoint = false;
|
||||||
|
joint.id = id;
|
||||||
joint.name = name;
|
joint.name = name;
|
||||||
joint.parentName = parentName;
|
joint.parentId = parentId;
|
||||||
|
joint.surfaceId = 0;
|
||||||
joint.translation = Vec3f(0, 0, 0);
|
joint.translation = Vec3f(0, 0, 0);
|
||||||
joint.rotation = Quatf(0, 0, 0, 1);
|
joint.rotation = Quatf(0, 0, 0, 1);
|
||||||
joint.scale = Vec3f(1, 1, 1);
|
joint.scale = Vec3f(1, 1, 1);
|
||||||
|
@ -281,7 +268,7 @@ void RawModel::Condense()
|
||||||
|
|
||||||
for (auto &triangle : triangles) {
|
for (auto &triangle : triangles) {
|
||||||
const RawSurface &surface = oldSurfaces[triangle.surfaceIndex];
|
const RawSurface &surface = oldSurfaces[triangle.surfaceIndex];
|
||||||
const int surfaceIndex = AddSurface(surface.name.c_str(), surface.nodeName.c_str());
|
const int surfaceIndex = AddSurface(surface.name.c_str(), surface.id);
|
||||||
surfaces[surfaceIndex] = surface;
|
surfaces[surfaceIndex] = surface;
|
||||||
triangle.surfaceIndex = surfaceIndex;
|
triangle.surfaceIndex = surfaceIndex;
|
||||||
}
|
}
|
||||||
|
@ -469,9 +456,9 @@ void RawModel::CreateMaterialModels(
|
||||||
RawSurface &rawSurface = model->GetSurface(surfaceIndex);
|
RawSurface &rawSurface = model->GetSurface(surfaceIndex);
|
||||||
|
|
||||||
if (model->GetSurfaceCount() > prevSurfaceCount) {
|
if (model->GetSurfaceCount() > prevSurfaceCount) {
|
||||||
const std::vector<std::string> &jointNames = surfaces[sortedTriangles[i].surfaceIndex].jointNames;
|
const std::vector<long> &jointIds = surfaces[sortedTriangles[i].surfaceIndex].jointIds;
|
||||||
for (const auto &jointName : jointNames) {
|
for (const auto &jointId : jointIds) {
|
||||||
const int nodeIndex = GetNodeByName(jointName.c_str());
|
const int nodeIndex = GetNodeById(jointId);
|
||||||
assert(nodeIndex != -1);
|
assert(nodeIndex != -1);
|
||||||
model->AddNode(GetNode(nodeIndex));
|
model->AddNode(GetNode(nodeIndex));
|
||||||
}
|
}
|
||||||
|
@ -529,12 +516,22 @@ void RawModel::CreateMaterialModels(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int RawModel::GetNodeByName(const char *name) const
|
int RawModel::GetNodeById(const long nodeId) const
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < nodes.size(); i++) {
|
for (size_t i = 0; i < nodes.size(); i++) {
|
||||||
if (nodes[i].name == name) {
|
if (nodes[i].id == nodeId) {
|
||||||
return (int) i;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
188
src/RawModel.h
188
src/RawModel.h
|
@ -95,8 +95,32 @@ struct RawTriangle
|
||||||
int surfaceIndex;
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline std::string Describe(RawShadingModel model) {
|
||||||
|
switch(model) {
|
||||||
|
case RAW_SHADING_MODEL_UNKNOWN: return "<unknown>";
|
||||||
|
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 "<unknown>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum RawTextureUsage
|
enum RawTextureUsage
|
||||||
{
|
{
|
||||||
|
RAW_TEXTURE_USAGE_NONE = -1,
|
||||||
RAW_TEXTURE_USAGE_AMBIENT,
|
RAW_TEXTURE_USAGE_AMBIENT,
|
||||||
RAW_TEXTURE_USAGE_DIFFUSE,
|
RAW_TEXTURE_USAGE_DIFFUSE,
|
||||||
RAW_TEXTURE_USAGE_NORMAL,
|
RAW_TEXTURE_USAGE_NORMAL,
|
||||||
|
@ -104,32 +128,28 @@ enum RawTextureUsage
|
||||||
RAW_TEXTURE_USAGE_SHININESS,
|
RAW_TEXTURE_USAGE_SHININESS,
|
||||||
RAW_TEXTURE_USAGE_EMISSIVE,
|
RAW_TEXTURE_USAGE_EMISSIVE,
|
||||||
RAW_TEXTURE_USAGE_REFLECTION,
|
RAW_TEXTURE_USAGE_REFLECTION,
|
||||||
|
RAW_TEXTURE_USAGE_ALBEDO,
|
||||||
|
RAW_TEXTURE_USAGE_OCCLUSION,
|
||||||
|
RAW_TEXTURE_USAGE_ROUGHNESS,
|
||||||
|
RAW_TEXTURE_USAGE_METALLIC,
|
||||||
RAW_TEXTURE_USAGE_MAX
|
RAW_TEXTURE_USAGE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::string DescribeTextureUsage(int usage)
|
static inline std::string Describe(RawTextureUsage usage)
|
||||||
{
|
{
|
||||||
if (usage < 0) {
|
switch (usage) {
|
||||||
return "<none>";
|
case RAW_TEXTURE_USAGE_NONE: return "<none>";
|
||||||
}
|
case RAW_TEXTURE_USAGE_AMBIENT: return "ambient";
|
||||||
switch (static_cast<RawTextureUsage>(usage)) {
|
case RAW_TEXTURE_USAGE_DIFFUSE: return "diffuse";
|
||||||
case RAW_TEXTURE_USAGE_AMBIENT:
|
case RAW_TEXTURE_USAGE_NORMAL: return "normal";
|
||||||
return "ambient";
|
case RAW_TEXTURE_USAGE_SPECULAR: return "specuar";
|
||||||
case RAW_TEXTURE_USAGE_DIFFUSE:
|
case RAW_TEXTURE_USAGE_SHININESS: return "shininess";
|
||||||
return "diffuse";
|
case RAW_TEXTURE_USAGE_EMISSIVE: return "emissive";
|
||||||
case RAW_TEXTURE_USAGE_NORMAL:
|
case RAW_TEXTURE_USAGE_REFLECTION: return "reflection";
|
||||||
return "normal";
|
case RAW_TEXTURE_USAGE_OCCLUSION: return "occlusion";
|
||||||
case RAW_TEXTURE_USAGE_SPECULAR:
|
case RAW_TEXTURE_USAGE_ROUGHNESS: return "roughness";
|
||||||
return "specuar";
|
case RAW_TEXTURE_USAGE_METALLIC: return "metallic";
|
||||||
case RAW_TEXTURE_USAGE_SHININESS:
|
case RAW_TEXTURE_USAGE_MAX:default: return "unknown";
|
||||||
return "shininess";
|
|
||||||
case RAW_TEXTURE_USAGE_EMISSIVE:
|
|
||||||
return "emissive";
|
|
||||||
case RAW_TEXTURE_USAGE_REFLECTION:
|
|
||||||
return "reflection";
|
|
||||||
case RAW_TEXTURE_USAGE_MAX:
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -159,17 +179,90 @@ enum RawMaterialType
|
||||||
RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT,
|
RAW_MATERIAL_TYPE_SKINNED_TRANSPARENT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RawMatProps {
|
||||||
|
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; };
|
||||||
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
|
{}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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
|
struct RawMaterial
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string shadingModel; // typically "Surface", "Anisotropic", "Blinn", "Lambert", "Phong", "Phone E"
|
|
||||||
RawMaterialType type;
|
RawMaterialType type;
|
||||||
Vec3f ambientFactor;
|
std::shared_ptr<RawMatProps> info;
|
||||||
Vec4f diffuseFactor;
|
|
||||||
Vec3f specularFactor;
|
|
||||||
Vec3f emissiveFactor;
|
|
||||||
float shininess;
|
|
||||||
int textures[RAW_TEXTURE_USAGE_MAX];
|
int textures[RAW_TEXTURE_USAGE_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -182,11 +275,11 @@ struct RawBlendChannel
|
||||||
|
|
||||||
struct RawSurface
|
struct RawSurface
|
||||||
{
|
{
|
||||||
|
long id;
|
||||||
std::string name; // The name of this surface
|
std::string name; // The name of this surface
|
||||||
std::string nodeName; // The node that links to this surface.
|
long skeletonRootId; // The id of the root node of the skeleton.
|
||||||
std::string skeletonRootName; // The name of the root of the skeleton.
|
|
||||||
Bounds<float, 3> bounds;
|
Bounds<float, 3> bounds;
|
||||||
std::vector<std::string> jointNames;
|
std::vector<long> jointIds;
|
||||||
std::vector<Vec3f> jointGeometryMins;
|
std::vector<Vec3f> jointGeometryMins;
|
||||||
std::vector<Vec3f> jointGeometryMaxs;
|
std::vector<Vec3f> jointGeometryMaxs;
|
||||||
std::vector<Mat4f> inverseBindMatrices;
|
std::vector<Mat4f> inverseBindMatrices;
|
||||||
|
@ -213,7 +306,7 @@ struct RawAnimation
|
||||||
struct RawCamera
|
struct RawCamera
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string nodeName;
|
long nodeId;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -242,12 +335,14 @@ struct RawCamera
|
||||||
struct RawNode
|
struct RawNode
|
||||||
{
|
{
|
||||||
bool isJoint;
|
bool isJoint;
|
||||||
|
long id;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string parentName;
|
long parentId;
|
||||||
std::vector<std::string> childNames;
|
std::vector<long> childIds;
|
||||||
Vec3f translation;
|
Vec3f translation;
|
||||||
Quatf rotation;
|
Quatf rotation;
|
||||||
Vec3f scale;
|
Vec3f scale;
|
||||||
|
long surfaceId;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RawModel
|
class RawModel
|
||||||
|
@ -262,22 +357,20 @@ public:
|
||||||
int AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage);
|
int AddTexture(const std::string &name, const std::string &fileName, const std::string &fileLocation, RawTextureUsage usage);
|
||||||
int AddMaterial(const RawMaterial &material);
|
int AddMaterial(const RawMaterial &material);
|
||||||
int AddMaterial(
|
int AddMaterial(
|
||||||
const char *name, const char *shadingModel, RawMaterialType materialType,
|
const char *name, const RawMaterialType materialType, const int textures[RAW_TEXTURE_USAGE_MAX],
|
||||||
const int textures[RAW_TEXTURE_USAGE_MAX], Vec3f ambientFactor,
|
std::shared_ptr<RawMatProps> materialInfo);
|
||||||
Vec4f diffuseFactor, Vec3f specularFactor,
|
|
||||||
Vec3f emissiveFactor, float shinineness);
|
|
||||||
int AddSurface(const RawSurface &suface);
|
int AddSurface(const RawSurface &suface);
|
||||||
int AddSurface(const char *name, const char *nodeName);
|
int AddSurface(const char *name, long surfaceId);
|
||||||
int AddAnimation(const RawAnimation &animation);
|
int AddAnimation(const RawAnimation &animation);
|
||||||
int AddCameraPerspective(
|
int AddCameraPerspective(
|
||||||
const char *name, const char *nodeName, const float aspectRatio, const float fovDegreesX, const float fovDegreesY,
|
const char *name, const long nodeId, const float aspectRatio, const float fovDegreesX, const float fovDegreesY,
|
||||||
const float nearZ, const float farZ);
|
const float nearZ, const float farZ);
|
||||||
int
|
int
|
||||||
AddCameraOrthographic(const char *name, const char *nodeName, const float magX, const float magY, const float nearZ, const float farZ);
|
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 RawNode &node);
|
||||||
int AddNode(const char *name, const char *parentName);
|
int AddNode(const long id, const char *name, const long parentId);
|
||||||
void SetRootNode(const char *name) { rootNodeName = name; }
|
void SetRootNode(const long nodeId) { rootNodeId = nodeId; }
|
||||||
const char *GetRootNode() const { return rootNodeName.c_str(); }
|
const long GetRootNode() const { return rootNodeId; }
|
||||||
|
|
||||||
// Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces.
|
// Remove unused vertices, textures or materials after removing vertex attributes, textures, materials or surfaces.
|
||||||
void Condense();
|
void Condense();
|
||||||
|
@ -307,6 +400,7 @@ public:
|
||||||
int GetSurfaceCount() const { return (int) surfaces.size(); }
|
int GetSurfaceCount() const { return (int) surfaces.size(); }
|
||||||
const RawSurface &GetSurface(const int index) const { return surfaces[index]; }
|
const RawSurface &GetSurface(const int index) const { return surfaces[index]; }
|
||||||
RawSurface &GetSurface(const int index) { return surfaces[index]; }
|
RawSurface &GetSurface(const int index) { return surfaces[index]; }
|
||||||
|
int GetSurfaceById(const long id) const;
|
||||||
|
|
||||||
// Iterate over the animations.
|
// Iterate over the animations.
|
||||||
int GetAnimationCount() const { return (int) animations.size(); }
|
int GetAnimationCount() const { return (int) animations.size(); }
|
||||||
|
@ -320,7 +414,7 @@ public:
|
||||||
int GetNodeCount() const { return (int) nodes.size(); }
|
int GetNodeCount() const { return (int) nodes.size(); }
|
||||||
const RawNode &GetNode(const int index) const { return nodes[index]; }
|
const RawNode &GetNode(const int index) const { return nodes[index]; }
|
||||||
RawNode &GetNode(const int index) { return nodes[index]; }
|
RawNode &GetNode(const int index) { return nodes[index]; }
|
||||||
int GetNodeByName(const char *name) const;
|
int GetNodeById(const long nodeId) const;
|
||||||
|
|
||||||
// Create individual attribute arrays.
|
// Create individual attribute arrays.
|
||||||
// Returns true if the vertices store the particular attribute.
|
// Returns true if the vertices store the particular attribute.
|
||||||
|
@ -334,7 +428,7 @@ public:
|
||||||
std::vector<RawModel> &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const;
|
std::vector<RawModel> &materialModels, const int maxModelVertices, const int keepAttribs, const bool forceDiscrete) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string rootNodeName;
|
long rootNodeId;
|
||||||
int vertexAttributes;
|
int vertexAttributes;
|
||||||
std::unordered_map<RawVertex, int, VertexHasher> vertexHash;
|
std::unordered_map<RawVertex, int, VertexHasher> vertexHash;
|
||||||
std::vector<RawVertex> vertices;
|
std::vector<RawVertex> vertices;
|
||||||
|
|
|
@ -135,10 +135,11 @@ void to_json(json &j, const PBRSpecularGlossiness &d)
|
||||||
}
|
}
|
||||||
|
|
||||||
PBRMetallicRoughness::PBRMetallicRoughness(
|
PBRMetallicRoughness::PBRMetallicRoughness(
|
||||||
const TextureData *baseColorTexture, const Vec4f &baseolorFactor,
|
const TextureData *baseColorTexture, const TextureData *metRoughTexture,
|
||||||
float metallic, float roughness)
|
const Vec4f &baseColorFactor, float metallic, float roughness)
|
||||||
: baseColorTexture(Tex::ref(baseColorTexture)),
|
: baseColorTexture(Tex::ref(baseColorTexture)),
|
||||||
baseColorFactor(baseolorFactor),
|
metRoughTexture(Tex::ref(metRoughTexture)),
|
||||||
|
baseColorFactor(baseColorFactor),
|
||||||
metallic(metallic),
|
metallic(metallic),
|
||||||
roughness(roughness)
|
roughness(roughness)
|
||||||
{
|
{
|
||||||
|
@ -153,10 +154,14 @@ void to_json(json &j, const PBRMetallicRoughness &d)
|
||||||
if (d.baseColorFactor.LengthSquared() > 0) {
|
if (d.baseColorFactor.LengthSquared() > 0) {
|
||||||
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
j["baseColorFactor"] = toStdVec(d.baseColorFactor);
|
||||||
}
|
}
|
||||||
if (d.metallic != 1.0f) {
|
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["metallicFactor"] = d.metallic;
|
||||||
}
|
|
||||||
if (d.roughness != 1.0f) {
|
|
||||||
j["roughnessFactor"] = d.roughness;
|
j["roughnessFactor"] = d.roughness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,10 +75,11 @@ struct PBRSpecularGlossiness
|
||||||
struct PBRMetallicRoughness
|
struct PBRMetallicRoughness
|
||||||
{
|
{
|
||||||
PBRMetallicRoughness(
|
PBRMetallicRoughness(
|
||||||
const TextureData *baseColorTexture, const Vec4f &baseolorFactor,
|
const TextureData *baseColorTexture, const TextureData *metRoughTexture,
|
||||||
float metallic = 0.1f, float roughness = 0.4f);
|
const Vec4f &baseColorFactor, float metallic = 0.1f, float roughness = 0.6f);
|
||||||
|
|
||||||
std::unique_ptr<Tex> baseColorTexture;
|
std::unique_ptr<Tex> baseColorTexture;
|
||||||
|
std::unique_ptr<Tex> metRoughTexture;
|
||||||
const Vec4f baseColorFactor;
|
const Vec4f baseColorFactor;
|
||||||
const float metallic;
|
const float metallic;
|
||||||
const float roughness;
|
const float roughness;
|
||||||
|
|
|
@ -20,7 +20,7 @@ NodeData::NodeData(
|
||||||
scale(scale),
|
scale(scale),
|
||||||
children(),
|
children(),
|
||||||
mesh(-1),
|
mesh(-1),
|
||||||
cameraName(""),
|
camera(-1),
|
||||||
skin(-1)
|
skin(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,10 @@ void NodeData::SetSkin(uint32_t skinIx)
|
||||||
skin = skinIx;
|
skin = skinIx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeData::AddCamera(std::string camera)
|
void NodeData::SetCamera(uint32_t cameraIndex)
|
||||||
{
|
{
|
||||||
assert(!isJoint);
|
assert(!isJoint);
|
||||||
cameraName = std::move(camera);
|
camera = cameraIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
json NodeData::serialize() const
|
json NodeData::serialize() const
|
||||||
|
@ -75,8 +75,8 @@ json NodeData::serialize() const
|
||||||
if (skin >= 0) {
|
if (skin >= 0) {
|
||||||
result["skin"] = skin;
|
result["skin"] = skin;
|
||||||
}
|
}
|
||||||
if (!cameraName.empty()) {
|
if (camera >= 0) {
|
||||||
result["camera"] = cameraName;
|
result["camera"] = camera;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -19,7 +19,7 @@ struct NodeData : Holdable
|
||||||
void AddChildNode(uint32_t childIx);
|
void AddChildNode(uint32_t childIx);
|
||||||
void SetMesh(uint32_t meshIx);
|
void SetMesh(uint32_t meshIx);
|
||||||
void SetSkin(uint32_t skinIx);
|
void SetSkin(uint32_t skinIx);
|
||||||
void AddCamera(std::string camera);
|
void SetCamera(uint32_t camera);
|
||||||
|
|
||||||
json serialize() const override;
|
json serialize() const override;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ struct NodeData : Holdable
|
||||||
Vec3f scale;
|
Vec3f scale;
|
||||||
std::vector<uint32_t> children;
|
std::vector<uint32_t> children;
|
||||||
int32_t mesh;
|
int32_t mesh;
|
||||||
std::string cameraName;
|
int32_t camera;
|
||||||
int32_t skin;
|
int32_t skin;
|
||||||
std::vector<std::string> skeletons;
|
std::vector<std::string> skeletons;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue