Files
Webcam/include/modules/import_fbx/import_fbx.cpp

986 lines
39 KiB
C++

/* -----------------------------------------------------------------------------
GSFramework
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */
#include "import_fbx/import_fbx.h"
#include "scene3d/scene.h"
#include "scene3d/mobject.h"
#include "scene3d/mlight.h"
#include "scene3d/mcamera.h"
#include "core/graphic_resource_factory.h"
#include "core/geometry.h"
#include "core/renderer.h"
#include "metafile/nml_object.h"
#include "picture/pict_io.h"
#include "filesystem/filesystem.h"
#include "platform.h"
using namespace GS;
using namespace GS::Core;
using namespace GS::S3D;
//------------------------------------------------------------------------------
static FbxAMatrix ConvertGlobalMatrix(const FbxAMatrix &m) {
FbxAMatrix k_m;
k_m.SetS(FbxVector4(-1, 1, 1));
return m * k_m;
}
static Matrix4 FBXMatrixToMatrix4(const FbxAMatrix &fbx_m) {
Matrix4 matrix;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
matrix.m[i][j] = (float) fbx_m[j][i];
return matrix;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
FbxScene *FBXImporter::LoadNativeScene(const char *fbx_path) {
// Create an IOSettings object
FbxIOSettings *ios = FbxIOSettings::Create(sdk_manager, IOSROOT);
// set some IOSettings options
ios->SetBoolProp(IMP_FBX_MATERIAL, true);
ios->SetBoolProp(IMP_FBX_TEXTURE, true);
ios->SetBoolProp(IMP_FBX_LINK, false);
ios->SetBoolProp(IMP_FBX_SHAPE, false);
ios->SetBoolProp(IMP_FBX_GOBO, false);
ios->SetBoolProp(IMP_FBX_ANIMATION, true);
ios->SetBoolProp(IMP_FBX_GLOBAL_SETTINGS, true);
// Create an empty scene
FbxScene *fbx_scene = FbxScene::Create(sdk_manager, "");
// Create an importer.
FBXImporter *fbx_importer = FBXImporter::Create(sdk_manager, "");
if (fbx_importer->Initialize(fbx_path, -1, ios) && fbx_importer->Import(fbx_scene)) {
input_path = String(fbx_path).CutFileName();
// Convert to our axis system and scale.
FbxAxisSystem axis_system(FbxAxisSystem::eYAxis, FbxAxisSystem::eParityOdd, FbxAxisSystem::eRightHanded);
axis_system.ConvertScene(fbx_scene);
const FbxSystemUnit::ConversionOptions options =
{
false, /* mConvertRrsNodes */
true, /* mConvertAllLimits */
true, /* mConvertClusters */
true, /* mConvertLightIntensity */
true, /* mConvertPhotometricLProperties */
true /* mConvertCameraClipPlanes */
};
FbxSystemUnit unit_system(100.f / config->scale);
unit_system.ConvertScene(fbx_scene, options);
} else {
fbx_scene->Destroy();
fbx_scene = NULL;
}
fbx_importer->Destroy();
return fbx_scene;
}
//------------------------------------------------------------------------------
//#define __DEBUG_EULER__
//------------------------------------------------------------------------------
void FBXImporter::ExportMotionChannel(FbxNode *pNode, FbxAnimCurve *pCurve, Motion *motion,
MotionChannel::Type channel_type) {
if (!pCurve)
return;
MotionChannel *channel = motion->AddChannel(channel_type);
if (!channel)
return;
channel->AllocatePoint(pCurve->KeyGetCount());
for (int n = 0; n < pCurve->KeyGetCount(); ++n) {
FbxTime time = pCurve->KeyGetTime(n);
CurvePoint *point = (CurvePoint *) channel->GetPoints()[n];
point->t = Time::fromSec(float(time.GetSecondDouble()));
point->v = pCurve->KeyGetValue(n);
switch (pCurve->KeyGetInterpolation(n)) {
default:
case FbxAnimCurveDef::eInterpolationLinear: point->shape = CurvePoint::Shape_Linear;
break;
case FbxAnimCurveDef::eInterpolationConstant: point->shape = CurvePoint::Shape_Step;
break;
case FbxAnimCurveDef::eInterpolationCubic: point->shape = CurvePoint::Shape_Hermite;
break;
}
}
}
void FBXImporter::BakeTransformation(FbxNode *pNode, MItem *item, Motion *motion) {
motion->SetUseQuaternion(true);
// Allocate position/scale.
#ifdef __DEBUG_EULER__
motion->AddChannels(9);
#else
motion->AddChannels(6);
#endif
motion->GetChannel(0)->type = MotionChannel::XPos;
motion->GetChannel(1)->type = MotionChannel::YPos;
motion->GetChannel(2)->type = MotionChannel::ZPos;
motion->GetChannel(3)->type = MotionChannel::XScl;
motion->GetChannel(4)->type = MotionChannel::YScl;
motion->GetChannel(5)->type = MotionChannel::ZScl;
#ifdef __DEBUG_EULER__
motion->GetChannel(6)->type = MotionChannel::XRot;
motion->GetChannel(7)->type = MotionChannel::YRot;
motion->GetChannel(8)->type = MotionChannel::ZRot;
#endif
// Bake animation.
FbxTime tStart = fbx_scene->GetEvaluator()->GetContext()->ReferenceStart.Get(),
tEnd = fbx_scene->GetEvaluator()->GetContext()->ReferenceStop.Get();
FbxTime tStep;
tStep.SetSecondDouble(1.0 / double(config->frame_per_second));
for (FbxTime t = tStart; t < (tEnd + tStep); t += tStep) // Make sure to include the last key.
{
Time ts = Time::fromSec(float(t.GetSecondDouble()));
Vector4 p, s;
Matrix3 r;
FbxAMatrix m;
int dummy = -1;
FbxAMatrix node_global_transform = fbx_scene->GetEvaluator()->GetNodeGlobalTransformFast(pNode, dummy, t);
dummy = -1;
if (pNode->GetParent()) {
FbxAMatrix parent_global_transform = fbx_scene->GetEvaluator()->GetNodeGlobalTransformFast(
pNode->GetParent(), dummy, t);
dummy = -1;
m = ConvertGlobalMatrix(parent_global_transform).Inverse() * ConvertGlobalMatrix(node_global_transform);
} else
m = ConvertGlobalMatrix(node_global_transform);
FBXMatrixToMatrix4(m).Decompose(&p, &s, &r);
motion->GetChannel(0)->Append(CurvePoint(ts, p.x));
motion->GetChannel(1)->Append(CurvePoint(ts, p.y));
motion->GetChannel(2)->Append(CurvePoint(ts, p.z));
motion->GetChannel(3)->Append(CurvePoint(ts, s.x));
motion->GetChannel(4)->Append(CurvePoint(ts, s.y));
motion->GetChannel(5)->Append(CurvePoint(ts, s.z));
#ifdef __DEBUG_EULER__
Vector4 e = r.AsEuler();
motion->GetChannel(6)->Insert(CurvePoint(ts, e.x));
motion->GetChannel(7)->Insert(CurvePoint(ts, e.y));
motion->GetChannel(8)->Insert(CurvePoint(ts, e.z));
#else
Quaternion q = Quaternion::FromMatrix3(r);
motion->GetQuaternion().Insert(QuaternionKey(ts, q));
#endif
}
// motion->Optimize();
}
void FBXImporter::ExportMotions(FbxNode *pNode, MItem *item) {
if (config->import_animation == false)
return;
for (int n = 0; n < fbx_scene->GetSrcObjectCount<FbxAnimStack>(); n++) {
FbxAnimStack *anim_stack = FbxCast<FbxAnimStack>(fbx_scene->GetSrcObject<FbxAnimStack>(n));
if (!anim_stack)
continue;
fbx_scene->GetEvaluator()->SetContext(anim_stack);
// Convert to motion.
Motion *motion = new Motion;
if (!motion)
continue;
String take_name(anim_stack->GetNameOnly());
motion->name = take_name;
BakeTransformation(pNode, item, motion);
// Add to scene motion set.
{
SceneMotion *set = NULL;
ListForeachPtr(SceneMotion *, s, scene->motion.motions)
if (s->name == motion->name) {
set = s;
break;
}
if (set == NULL) // create a new motion set
{
set = new SceneMotion;
set->name = take_name;
scene->motion.motions.Add(set);
}
SceneMotion::ItemMotion *item_motion = new SceneMotion::ItemMotion; // new item motion
item_motion->uid = item->GetUid();
item_motion->motion = motion;
set->item_motions.Add(item_motion); // add to set
}
// Add to item motion list.
{
// item->automation_player->AddMotion(motion);
}
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool FBXImporter::ExportDeformers(FbxMesh *fbx_mesh, FbxNode *pNode, Geometry &geo, MObject *object) {
FbxSkin *fbx_skin = ((FbxSkin *) fbx_mesh->GetDeformer(0, FbxDeformer::eSkin));
if (!fbx_skin)
return false;
// Allocate geometry skin.
geo.skin.Allocate(geo.vtx.GetCount());
for (uint n = 0; n < geo.vtx.GetCount(); ++n)
for (int j = 0; j < __PV_BONE_LIMIT__; ++j) {
geo.skin[n].bone_index[j] = 0;
geo.skin[n].w[j] = 0.f;
}
// For each skin entry select the clusters with the largest weight.
geo.AllocateBone(fbx_skin->GetClusterCount());
for (int n = 0; n < (int) geo.bone_name.GetCount(); ++n) {
FbxCluster *cluster = fbx_skin->GetCluster(n);
if (FbxNode *bone = cluster->GetLink())
geo.bone_name[n] = bone->GetName();
// Import bind pose.
FbxAMatrix cluster_matrix, bind_matrix;
cluster->GetTransformMatrix(cluster_matrix);
cluster->GetTransformLinkMatrix(bind_matrix);
geo.bone_bind_matrix[n] = FBXMatrixToMatrix4(
(ConvertGlobalMatrix(cluster_matrix).Inverse() * ConvertGlobalMatrix(bind_matrix)).Inverse());
// Import weights.
int *fbx_index = cluster->GetControlPointIndices();
double *fbx_weight = cluster->GetControlPointWeights();
for (int i = 0; i < cluster->GetControlPointIndicesCount(); ++i) {
GeometrySkin *skin = &geo.skin[fbx_index[i]];
// Perform insertion.
for (int c = 0; c < __PV_BONE_LIMIT__; ++c)
if (fbx_weight[i] > skin->w[c]) {
// Shift the lower influences out.
for (int j = __PV_BONE_LIMIT__ - 1; j > c; --j) {
skin->w[j] = skin->w[j - 1];
skin->bone_index[j] = skin->bone_index[j - 1];
}
// Insert new influence.
skin->w[c] = (float) fbx_weight[i];
skin->bone_index[c] = (ushort) n;
break;
}
}
}
// Normalize weights.
for (uint n = 0; n < geo.vtx.GetCount(); ++n) {
GeometrySkin *skin = &geo.skin[n];
float w_sum = 0;
for (int c = 0; c < __PV_BONE_LIMIT__; ++c)
w_sum += skin->w[c];
if (w_sum > 0)
for (int c = 0; c < __PV_BONE_LIMIT__; ++c)
skin->w[c] /= w_sum;
}
// Set geometry and bind bones.
object->geometry = geo.name;
if (object->AllocateSkin(geo.GetBoneCount()))
for (uint n = 0; n < geo.GetBoneCount(); ++n)
if (MItem *item = ExportNode(fbx_skin->GetCluster(n)->GetLink()))
object->BindBone(n, item->GetBaseItem());
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
String FBXImporter::ExportFileTexture(FbxFileTexture *fbx_texture) {
if (!fbx_texture)
return NULL;
// Try to locate texture.
String in_path;
forever {
in_path = fbx_texture->GetFileName();
if (Platform::Get().io->Exists(in_path))
break;
in_path.FileCutPath();
if (Platform::Get().io->Exists(in_path))
break;
in_path = input_path + "/" + in_path;
if (Platform::Get().io->Exists(in_path))
break;
return NULL;
}
// Import texture.
String out_path;
if (GetOutputPath(out_path, config->base_path, in_path.GetFileName(), "texture", in_path.GetFileExtension(),
config->exists_policy_texture)) {
Platform::Get().io->FileCopy(in_path, out_path);
out_path = Platform::Get().io->StripRootPath(out_path);
}
return out_path;
}
String FBXImporter::ExportLayeredTexture(FbxLayeredTexture *object) {
String out_path;
for (int n = 0; n < object->GetSrcObjectCount<FbxTexture>(); ++n) {
if (FbxFileTexture *t = object->GetSrcObject<FbxFileTexture>(n))
out_path = ExportFileTexture(t);
// if (FbxLayeredTexture *t = object->GetSrcObject(FBX_TYPE(FbxLayeredTexture), n))
// texture = ExportLayeredTexture(t);
if (!out_path.IsEmpty())
break;
}
return out_path;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
String FBXImporter::SaveMaterial(const Material &material, const char *name) {
String out_path;
if (GetOutputPath(out_path, config->base_path, name, "material", "nmm", config->exists_policy_material))
NML::SaveToFile(material, out_path);
return Platform::Get().io->StripRootPath(out_path);
}
String FBXImporter::ExportMaterial(FbxSurfaceMaterial *fbx_material, FbxMesh *fbx_mesh, bool use_skin) {
static const char *texture_type_to_export[] =
{
FbxSurfaceMaterial::sDiffuse,
FbxSurfaceMaterial::sEmissive,
FbxSurfaceMaterial::sAmbient,
FbxSurfaceMaterial::sSpecular,
FbxSurfaceMaterial::sNormalMap,
FbxSurfaceMaterial::sShininess,
FbxSurfaceMaterial::sBump,
FbxSurfaceMaterial::sTransparentColor,
FbxSurfaceMaterial::sReflection,
0
};
static MaterialChannel export_texture_to_channel[] =
{
Channel_Diffuse,
Channel_SelfIllum,
Channel_Light,
Channel_Specular,
Channel_Normal,
Channel_Glossiness,
Channel_Normal,
Channel_Opacity,
Channel_Reflection
};
if (!fbx_material)
return NULL;
Material material;
material.renderword |= Material::Render_Smooth;
if (use_skin)
material.renderword |= Material::Render_Skinned;
// Phong.
if (fbx_material->GetClassId().Is(FbxSurfacePhong::ClassId)) {
FbxSurfacePhong *fbx_phong = (FbxSurfacePhong *) fbx_material;
material.specular.Set(float(fbx_phong->Specular.Get()[0] * fbx_phong->SpecularFactor.Get()),
float(fbx_phong->Specular.Get()[1] * fbx_phong->SpecularFactor.Get()),
float(fbx_phong->Specular.Get()[2] * fbx_phong->SpecularFactor.Get()));
material.glossiness = Types::Clamp((float) fbx_phong->Shininess.Get() / 64.f, 0.01f, 0.5f);
// Completely random conversion factor.
}
// Lambert.
if (fbx_material->GetClassId().Is(FbxSurfacePhong::ClassId) || fbx_material->GetClassId().Is(
FbxSurfaceLambert::ClassId)) {
FbxSurfaceLambert *fbx_lambert = (FbxSurfaceLambert *) fbx_material;
material.ambient.Set(float(fbx_lambert->Ambient.Get()[0] * fbx_lambert->AmbientFactor.Get()),
float(fbx_lambert->Ambient.Get()[1] * fbx_lambert->AmbientFactor.Get()),
float(fbx_lambert->Ambient.Get()[2] * fbx_lambert->AmbientFactor.Get()));
material.diffuse.Set(float(fbx_lambert->Diffuse.Get()[0] * fbx_lambert->DiffuseFactor.Get()),
float(fbx_lambert->Diffuse.Get()[1] * fbx_lambert->DiffuseFactor.Get()),
float(fbx_lambert->Diffuse.Get()[2] * fbx_lambert->DiffuseFactor.Get()));
material.self.Set(float(fbx_lambert->Emissive.Get()[0] * fbx_lambert->EmissiveFactor.Get()),
float(fbx_lambert->Emissive.Get()[1] * fbx_lambert->EmissiveFactor.Get()),
float(fbx_lambert->Emissive.Get()[2] * fbx_lambert->EmissiveFactor.Get()));
// material.opacity = 1.f - fbx_lambert->GetTransparencyFactor().Get(); // Broken exporters make the importer appear broken.
}
// Export material textures.
for (int t = 0; texture_type_to_export[t]; ++t) {
// Export texture from FBX.
FbxProperty fbx_texture_prop = fbx_material->FindProperty(texture_type_to_export[t]);
FbxTexture *fbx_texture = fbx_texture_prop.GetSrcObject<FbxTexture>(0);
if (!fbx_texture)
continue;
String texture;
if (FbxFileTexture *t = fbx_texture_prop.GetSrcObject<FbxFileTexture>(0))
texture = ExportFileTexture(t);
if (FbxLayeredTexture *t = fbx_texture_prop.GetSrcObject<FbxLayeredTexture>(0))
texture = ExportLayeredTexture(t);
// Identify UV channel.
int uv_index = -1, uv_count = 0;
for (int l = 0; l < fbx_mesh->GetLayerCount(); ++l) {
FbxLayer *fbx_layer = fbx_mesh->GetLayer(l);
for (int n = 0; n < fbx_layer->GetUVSetCount(); ++n) {
FbxArray<FbxLayerElement::EType> uv_types = fbx_layer->GetUVSetChannels();
for (int t = 0; t < uv_types.GetCount(); ++t)
if (fbx_texture->UVSet.Get() == fbx_layer->GetUVs(uv_types[t])->GetName()) {
uv_index = uv_count;
goto done_uv;
}
++uv_count;
if (uv_count == __UV_PER_GEOMETRY__)
goto done_uv;
}
}
done_uv:;
// Create stage.
if (Material::TextureStage *stage = material.NewStage(export_texture_to_channel[t], texture, Material::UV_UV,
(uchar) (uv_index == -1 ? 0 : uv_index))) {
// Normal map defaults to tangent.
if (stage->channel == Channel_Normal)
material.renderword |= Material::Render_NormalTangent;
}
}
return SaveMaterial(material, fbx_material->GetName());
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
String FBXImporter::ExportGeometry(FbxMesh *fbx_mesh, FbxNode *pNode, MObject *object) {
// Build local transformation.
FbxAMatrix mesh_matrix, mesh_rmatrix;
if (pNode) {
mesh_matrix.SetTRS(pNode->GetGeometricTranslation(FbxNode::eSourcePivot),
pNode->GetGeometricRotation(FbxNode::eSourcePivot),
pNode->GetGeometricScaling(FbxNode::eSourcePivot));
mesh_rmatrix.SetR(pNode->GetGeometricRotation(FbxNode::eSourcePivot));
FbxAMatrix export_global_mtx;
export_global_mtx.SetS(FbxVector4(-1, 1, 1));
mesh_matrix = export_global_mtx * mesh_matrix;
mesh_rmatrix = export_global_mtx * mesh_rmatrix;
}
// Export.
Geometry geo;
geo.name = pNode->GetName();
// Transfer topology.
geo.AllocateVertex(fbx_mesh->GetControlPointsCount());
for (uint n = 0; n < geo.vtx.GetCount(); ++n) {
FbxVector4 v = mesh_matrix.MultT(fbx_mesh->GetControlPoints()[n]);
geo.vtx[n].Set((float) v[0], (float) v[1], (float) v[2]);
}
geo.AllocatePolygon(fbx_mesh->GetPolygonCount());
for (uint n = 0; n < geo.pol.GetCount(); ++n) {
geo.pol[n].vtx_count = (ushort) fbx_mesh->GetPolygonSize(n);
geo.pol[n].material = 0;
}
Array<uint> pol_index;
geo.ComputePolygonIndex(pol_index);
geo.AllocatePolygonBinding();
#define __PolIndex (pol_index[p] + v)
#define __PolRemapIndex (pol_index[p] + (geo.pol[p].vtx_count - 1 - v))
// #define __PolRemapIndex (geometry->pol_index[p] + v)
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v)
geo.pol[p].binding[v] = fbx_mesh->GetPolygonVertices()[__PolRemapIndex];
// Export materials.
FbxLayer *fbx_layer = fbx_mesh->GetLayer(0);
// Normal.
if (const FbxLayerElementNormal *normal_layer = fbx_layer->GetNormals())
if (geo.vtx_normal.Allocate(geo.binding.GetCount()))
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v) {
FbxVector4 N;
fbx_mesh->GetPolygonVertexNormal(p, v, N);
N = mesh_rmatrix.MultT(N);
geo.vtx_normal[__PolRemapIndex].Set((float) N[0], (float) N[1], (float) N[2]);
}
// Tangent and binormal.
const FbxLayerElementTangent *tangent_layer = fbx_layer->GetTangents();
const FbxLayerElementBinormal *binormal_layer = fbx_layer->GetBinormals();
if (tangent_layer && binormal_layer) {
if ((tangent_layer->GetMappingMode() == FbxLayerElement::eByPolygonVertex) && (
binormal_layer->GetMappingMode() == FbxLayerElement::eByPolygonVertex)) {
if (geo.vtx_tangent.Allocate(geo.binding.GetCount()))
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v) {
FbxVector4 T = tangent_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect
? tangent_layer->GetDirectArray()[tangent_layer->GetIndexArray()[
__PolRemapIndex]]
: tangent_layer->GetDirectArray()[__PolRemapIndex];
T = mesh_rmatrix.MultT(T);
geo.vtx_tangent[__PolIndex].T.Set((float) T[0], (float) -T[1], (float) T[2]);
// This is UV dependent and textures are reversed on V from the FBX convention.
FbxVector4 B = binormal_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect
? binormal_layer->GetDirectArray()[binormal_layer->GetIndexArray()[
__PolRemapIndex]]
: binormal_layer->GetDirectArray()[__PolRemapIndex];
B = mesh_rmatrix.MultT(T);
geo.vtx_tangent[__PolIndex].B.Set((float) B[0], (float) -B[1], (float) B[2]);
// This is UV dependent and textures are reversed on V from the FBX convention.
}
} else
__LOG_W__ << "Unsupported tangent layer mapping mode (" << tangent_layer->GetMappingMode() << ").\n";
}
// Vertex color.
if (const FbxLayerElementVertexColor *color_layer = fbx_layer->GetVertexColors()) {
if (geo.rgb.Allocate(geo.binding.GetCount()))
switch (color_layer->GetMappingMode()) {
case FbxLayerElement::eByControlPoint:
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v) {
uint v_idx = geo.pol[p].binding[v];
const FbxColor &cl = color_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect
? color_layer->GetDirectArray()[color_layer->GetIndexArray()[
v_idx]]
: color_layer->GetDirectArray()[v_idx];
geo.rgb[__PolIndex].Set((float) cl.mRed, (float) cl.mGreen, (float) cl.mBlue);
}
break;
case FbxLayerElement::eByPolygonVertex:
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v) {
const FbxColor &cl = color_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect
? color_layer->GetDirectArray()[color_layer->GetIndexArray()[
__PolRemapIndex]]
: color_layer->GetDirectArray()[__PolRemapIndex];
geo.rgb[__PolIndex].Set((float) cl.mRed, (float) cl.mGreen, (float) cl.mBlue);
}
break;
default:
__LOG_W__ << "Unsupported vertex color layer mapping mode (" << color_layer->GetMappingMode() <<
").\n";
}
else
__LOG_E__ << "Failed to allocate vertex color set.\n";
}
// UV Channel (searched for on all available layers).
uint uv_count = 0;
for (int l = 0; l < fbx_mesh->GetLayerCount(); ++l) {
FbxLayer *fbx_layer = fbx_mesh->GetLayer(l);
for (int n = 0; n < fbx_layer->GetUVSetCount(); ++n) {
const FbxLayerElementUV *uv_layer = fbx_layer->GetUVSets()[n];
if (geo.uv[uv_count].Allocate(geo.binding.GetCount()))
switch (uv_layer->GetMappingMode()) {
case FbxLayerElement::eByControlPoint:
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v) {
uint v_idx = geo.pol[p].binding[v];
const FbxVector2 &UV = uv_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect
? uv_layer->GetDirectArray()[uv_layer->GetIndexArray()[
v_idx]]
: uv_layer->GetDirectArray()[v_idx];
geo.uv[uv_count][__PolIndex].Set((float) UV[0], 1.f - (float) UV[1]);
}
break;
case FbxLayerElement::eByPolygonVertex:
for (uint p = 0; p < geo.pol.GetCount(); ++p)
for (int v = 0; v < geo.pol[p].vtx_count; ++v) {
const FbxVector2 &UV = uv_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect
? uv_layer->GetDirectArray()[uv_layer->GetIndexArray()[
__PolRemapIndex]]
: uv_layer->GetDirectArray()[__PolRemapIndex];
geo.uv[uv_count][__PolIndex].Set((float) UV[0], 1.f - (float) UV[1]);
}
break;
default:
__LOG_W__ << "Unsupported UV layer mapping mode (" << uv_layer->GetMappingMode() << ").\n";
}
else
__LOG_E__ << "Failed to allocate UV set.\n";
if (++uv_count == __UV_PER_GEOMETRY__) {
__LOG_W__ << "UV map limit per geometry exceeded (" << __UV_PER_GEOMETRY__ <<
"), increase nUVMapLimit.\n";
break;
}
}
if (uv_count == __UV_PER_GEOMETRY__)
break;
}
// Export deformers.
bool use_skin = ExportDeformers(fbx_mesh, pNode, geo, object);
// Materials.
int material_count = fbx_mesh->GetNode()->GetMaterialCount();
if (material_count > 0) {
geo.material_table.Allocate(material_count);
for (int n = 0; n < material_count; ++n)
geo.material_table[n].name = ExportMaterial((FbxSurfaceMaterial *) fbx_mesh->GetNode()->GetMaterial(n),
fbx_mesh, use_skin);
} else {
Material material;
if (use_skin)
material.renderword |= Material::Render_Skinned;
geo.material_table.Allocate(1);
geo.material_table[0].name = SaveMaterial(material, geo.name);
}
// Export the material mapping to polygon.
const FbxLayerElementMaterial *material_layer = fbx_layer->GetMaterials();
if (material_layer)
switch (material_layer->GetMappingMode()) {
case FbxLayerElement::eByPolygon: {
// Map polygon to material.
if (material_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect)
for (uint n = 0; n < geo.pol.GetCount(); ++n) {
int idx = material_layer->GetIndexArray().GetAt(n);
geo.pol[n].material = (ushort) idx;
if (geo.pol[n].material >= geo.material_table.GetCount()) {
__LOG_E__ << "Invalid material index (" << idx << ") for polygon " << n <<
" (FBX powered).\n";
geo.pol[n].material = 0;
}
}
else
for (uint n = 0; n < geo.pol.GetCount(); ++n)
geo.pol[n].material = (ushort) n;
}
break;
case FbxLayerElement::eAllSame: {
if (material_layer->GetReferenceMode() == FbxLayerElement::eIndexToDirect)
for (uint n = 0; n < geo.pol.GetCount(); ++n) {
int idx = material_layer->GetIndexArray().GetAt(0);
geo.pol[n].material = (ushort) idx;
if (geo.pol[n].material >= geo.material_table.GetCount()) {
__LOG_E__ << "Invalid material index (" << idx << ") for polygon " << n <<
" (FBX powered).\n";
geo.pol[n].material = 0;
}
}
else
for (uint n = 0; n < geo.pol.GetCount(); ++n)
geo.pol[n].material = 0;
}
break;
default:
__LOG_W__ << "Unsupported material mapping mode (" << material_layer->GetMappingMode() << ").\n";
break;
}
// Output to path.
String out_path;
if (GetOutputPath(out_path, config->base_path, geo.name, "geometry", "nmg", config->exists_policy_geometry)) {
geo.name = out_path;
NML::SaveToFile(geo, geo.name);
geo.name = Platform::Get().io->StripRootPath(geo.name);
}
object->geometry = geo.name;
return geo.name;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
MObject *FBXImporter::ExportObject(FbxNodeAttribute *pAttr, FbxNode *pNode) {
FbxMesh *fbx_mesh = (FbxMesh *) pAttr;
MObject *object = new MObject;
object->name = pNode->GetNameOnly();
scene->AddItem(object, true);
ExportGeometry(fbx_mesh, pNode, object);
return object;
}
MItem *FBXImporter::ExportCamera(FbxNodeAttribute *pAttr, FbxNode *pNode) {
FbxCamera *fbx_camera = (FbxCamera *) pAttr;
MCamera *camera = new MCamera;
camera->name = pNode->GetNameOnly();
scene->AddItem(camera, true);
if (fbx_camera->GetNearPlane() != 10)
camera->SetNearClippingPlane((float) fbx_camera->GetNearPlane());
if (fbx_camera->GetFarPlane() != 4000)
camera->SetFarClippingPlane((float) fbx_camera->GetFarPlane());
camera->aspect_ratio = (float) fbx_camera->GetPixelRatio();
camera->SetFov(Units::DegreeToRadian((float) fbx_camera->FieldOfView.Get()));
camera->is_orthographic = asbool(fbx_camera->ProjectionType.Get() == FbxCamera::eOrthogonal);
return camera;
}
MItem *FBXImporter::ExportLight(FbxNodeAttribute *pAttr, FbxNode *pNode) {
FbxLight *fbx_light = (FbxLight *) pAttr;
MLight *light = new MLight;
light->name = pNode->GetNameOnly();
scene->AddItem(light, true);
switch (fbx_light->LightType.Get()) {
case FbxLight::ePoint: light->model = MLight::Model_Point;
break;
case FbxLight::eDirectional: light->model = MLight::Model_Linear;
break;
case FbxLight::eSpot: light->model = MLight::Model_Spot;
break;
}
light->diffuse_color.Set((float) fbx_light->Color.Get()[0], (float) fbx_light->Color.Get()[1],
(float) fbx_light->Color.Get()[2]);
light->diffuse_intensity = (float) fbx_light->Intensity.Get() / 100.f;
light->specular_color = light->diffuse_color;
if (fbx_light->EnableFarAttenuation.Get()) {
light->range = (float) fbx_light->FarAttenuationEnd.Get();
light->volume_range = light->range + Units::Mtr(0.5f);
}
if (fbx_light->CastShadows.Get())
light->shadow = Light::Shadow_Map;
light->shadow_color.Set((float) fbx_light->ShadowColor.Get()[0], (float) fbx_light->ShadowColor.Get()[1],
(float) fbx_light->ShadowColor.Get()[2]);
return light;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool FBXImporter::GetNodeItem(FbxNode *pNode, MItem **item) {
ListForeachPtr(ExportedNode *, exported_node, node_list)
if (exported_node->node == pNode) {
if (item)
*item = exported_node->item;
return true;
}
return false;
}
MItem *FBXImporter::ExportNode(FbxNode *pNode) {
if (config->event_handler)
config->event_handler->LoadProgress(String::Format("Importing node '%s'...", pNode->GetName()),
(float) current_node_index / fbx_scene->GetNodeCount());
current_node_index++;
MItem *item = NULL;
if (GetNodeItem(pNode, &item))
return item;
// Export this node.
if (pNode != fbx_scene->GetRootNode()) {
if (pNode->GetNodeAttribute()) {
FbxNodeAttribute::EType type = pNode->GetNodeAttribute()->GetAttributeType();
switch (type) {
default:
case FbxNodeAttribute::eUnknown:
case FbxNodeAttribute::eNull:
case FbxNodeAttribute::eMarker:
case FbxNodeAttribute::eNurbs:
case FbxNodeAttribute::ePatch:
case FbxNodeAttribute::eCameraStereo:
case FbxNodeAttribute::eCameraSwitcher:
case FbxNodeAttribute::eOpticalReference:
case FbxNodeAttribute::eOpticalMarker:
case FbxNodeAttribute::eNurbsCurve:
case FbxNodeAttribute::eTrimNurbsSurface:
case FbxNodeAttribute::eBoundary:
case FbxNodeAttribute::eNurbsSurface:
case FbxNodeAttribute::eShape:
case FbxNodeAttribute::eLODGroup:
case FbxNodeAttribute::eSubDiv:
case FbxNodeAttribute::eSkeleton:
if (MObject *o = new MObject) {
o->name = pNode->GetNameOnly();
scene->AddItem(o, true);
item = o;
}
break;
case FbxNodeAttribute::eMesh:
item = ExportObject(pNode->GetNodeAttribute(), pNode);
break;
case FbxNodeAttribute::eCamera:
item = ExportCamera(pNode->GetNodeAttribute(), pNode);
break;
case FbxNodeAttribute::eLight:
item = ExportLight(pNode->GetNodeAttribute(), pNode);
break;
}
} else {
MObject *o = new MObject;
o->name = pNode->GetNameOnly();
scene->AddItem(o, true);
item = o;
}
}
// Register node.
node_list.Add(new ExportedNode(pNode, item));
if (item) {
FbxAMatrix m;
if (pNode->GetParent())
m = ConvertGlobalMatrix(fbx_scene->GetEvaluator()->GetNodeGlobalTransform(pNode->GetParent())).Inverse() *
ConvertGlobalMatrix(fbx_scene->GetEvaluator()->GetNodeGlobalTransform(pNode));
else m = ConvertGlobalMatrix(fbx_scene->GetEvaluator()->GetNodeGlobalTransform(pNode));
item->GetBaseItem()->SetMatrix(FBXMatrixToMatrix4(m));
ExportMotions(pNode, item);
}
// Export children.
for (int i = 0; i < pNode->GetChildCount(); i++) {
MItem *child = ExportNode(pNode->GetChild(i));
if (child && item)
child->GetBaseItem()->SetParent(item->GetBaseItem());
}
return item;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool FBXImporter::TestImport(const char *uri) {
String ext = String::FileGetExtension(uri).Lower();
return asbool((ext == "dae") || (ext == "fbx") || (ext == "3ds"));
}
bool FBXImporter::ImportScene(Scene *_scene, const char *_input, const Config &_config, Group **) {
if (!sdk_manager)
__ERR__(__LOG_E__ << "Importer not initialized.\n", false)
if (_input == NULL)
__ERR__(__LOG_E__ << "No input file specified.\n", false)
if (_scene == NULL)
__ERR__(__LOG_E__ << "No scene to load into.\n", false)
// Load native FBX.
scene = _scene;
config = &_config;
if ((fbx_scene = LoadNativeScene(_input)) == NULL)
return false;
if (config->event_handler)
config->event_handler->OpenLoad();
// Drop lists.
geometry_list.Clear();
// Perform conversion.
current_node_index = 0;
ExportNode(fbx_scene->GetRootNode());
// Convert globals.
FbxGlobalLightSettings &gsettings = fbx_scene->GlobalLightSettings();
scene->ambient_color.Set((float) gsettings.GetAmbientColor().mRed, (float) gsettings.GetAmbientColor().mGreen,
(float) gsettings.GetAmbientColor().mBlue);
scene->ambient_intensity = 1.f;
scene->fog_color.Set((float) gsettings.GetFogColor().mRed, (float) gsettings.GetFogColor().mGreen,
(float) gsettings.GetFogColor().mBlue);
if (gsettings.GetFogEnable()) {
scene->fog_near = (float) gsettings.GetFogStart();
scene->fog_far = (float) gsettings.GetFogEnd();
}
// Save.
if (!config->base_path.IsEmpty()) {
scene->name = String::Format("%s/%s.nms", config->base_path.toUtf8(),
String(_input).CutFilePath().CutFileExtension().toUtf8());
NML::SaveToFile(*scene, scene->name);
scene->name = Platform::Get().io->StripRootPath(scene->name);
}
fbx_scene->Destroy();
ListDeleteAllPtr(ExportedNode *, node_list)
if (config->event_handler)
config->event_handler->EndLoad();
return true;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
FBXImporter::FBXImporter() {
sdk_manager = FbxManager::Create();
fbx_scene = NULL;
}
FBXImporter::~FBXImporter() {
if (sdk_manager)
sdk_manager->Destroy();
}
//------------------------------------------------------------------------------