/* ----------------------------------------------------------------------------- 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(); n++) { FbxAnimStack *anim_stack = FbxCast(fbx_scene->GetSrcObject(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(); ++n) { if (FbxFileTexture *t = object->GetSrcObject(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(0); if (!fbx_texture) continue; String texture; if (FbxFileTexture *t = fbx_texture_prop.GetSrcObject(0)) texture = ExportFileTexture(t); if (FbxLayeredTexture *t = fbx_texture_prop.GetSrcObject(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 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 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(); } //------------------------------------------------------------------------------