LoginSignup
23
20

More than 5 years have passed since last update.

cocos2d-x v3.3 unityちゃんのfbxデータを変換する際のメモ

Posted at

正確なバージョンは把握してないのですが、v3.1あたりからCamera3D Testが追加され3Dモデルの表示テストができるようになってます。
これにunityちゃんfbxデータをc3bにして表示しようとすると、表示自体はできるのですが数点問題が発生したので、その対処方法のメモ。上手くいくと以下な感じで表示できます。
unitychan02.png

unityのサイトからunityちゃんのデータを入手

今回はSD版のunityちゃんデータを利用します。
http://unity-chan.com/contents/guideline/?id=SDUnityChan

unitypackageから以下を参考にfbxを取り出す

落としたデータはunitypackageなので、以下を参考にfbxを取り出します。
http://unrealengine.hatenablog.com/entry/2014/04/15/184432
複数のfbxが含まれますが、SD_unitychan_generic.fbxを利用します。

fbx-convにてc3bに変換

fbx-convはcocos2d-xに付属しており、tools\fbx-conv\win32以下にあります。(mac版は未検証です)
fbx-conv.exe -f SD_unitychan_generic.fbx
テクスチャのuvが反転しているので、-fオプションを指定します。
変換時にWARNINGが表示されますが、これでSD_unitychan_generic.c3bができるはず。

Sprite3DTestを改変する

Node: Camera3D Testでunityちゃんが表示されるようにSprite3DTestへ手を加えます。
Sprite3DTestのc3b読み込み部分を修正。

Sprite3DTest.cpp
    addNewSpriteWithCoords( Vec3(0,0,0),"Sprite3DTest/SD_unitychan_generic.c3b",false,0.2f,true);

SD_unitychan_generic.c3bファイルと、fbxで利用されているtgaファイルをcpp-tests\Resources\Sprite3DTestへコピー。
これでSD_unitychan_generic.c3b自体が表示されるようになります。
が、以下のようになっているはずです。
unitychan01.png

unityちゃんfbxをfbx-convする際の問題点

問題としては2点あります。

顔のモデルがスキニングされておらず、表示位置がおかしくなる

顔のモデルである_faceノードがCharacter1_Headの子になっていますが、スキニングされていないため、原点位置に表示されます。

Cgfxシェーダーが利用されている箇所が赤くなる

cocos2d-xはcgfxには対応していないため、fbx-conv時にdiffuse(1,0,0)のマテリアルに置き換わります。
fbx-conv時に表示されている
WARNING: [nol_mat] Material doesn't extend FbxSurfaceLambert, replaced with RED diffuse
は、このマテリアル置き換えを指しています。

問題点への対処方法

前述2つの問題ですが、fbx-convを改変して対処できます。
fbx-convのビルドは以下を参考に。
http://qiita.com/makanai/items/ae7776aef0147c30cc7e

スキニングされていないmodelについて、強制的にスキニングする

顔のモデルがスキニングされておらず、表示位置がおかしくなる問題について、
スキニングされていないmodelは強制的にスキニングを行って対処します。

FbxConvert.h
        void prefetchMeshes() {
            {
                int cnt = scene->GetGeometryCount();
                FbxGeometryConverter converter(manager);
                std::vector<FbxGeometry *> triangulate;
                for (int i = 0; i < scene->GetGeometryCount(); ++i) {
                    FbxGeometry * geometry = scene->GetGeometry(i);
                    if (!geometry->Is<FbxMesh>() || !((FbxMesh*)geometry)->IsTriangleMesh())
                        triangulate.push_back(geometry);
                }
                for (std::vector<FbxGeometry *>::iterator it = triangulate.begin(); it != triangulate.end(); ++it)
                {
                    log->status(log::sSourceConvertFbxTriangulate, getGeometryName(*it), (*it)->GetClassId().GetName());
                    FbxNodeAttribute * const attr = converter.Triangulate(*it, true);
                }
            }

            int cnt = scene->GetGeometryCount();
            for (int i = 0; i < cnt; ++i) {
                FbxGeometry * geometry = scene->GetGeometry(i);

                if (fbxMeshMap.find(geometry) == fbxMeshMap.end()) {
                    if (!geometry->Is<FbxMesh>()) {
                        log->warning(log::wSourceConvertFbxCantTriangulate, geometry->GetClassId().GetName());
                        continue;
                    }
                    FbxMesh *mesh = (FbxMesh*)geometry;
                    int indexCount = (mesh->GetPolygonCount() * 3);
                    log->verbose(log::iSourceConvertFbxMeshInfo, getGeometryName(mesh), mesh->GetPolygonCount(), indexCount, mesh->GetControlPointsCount());
                    if (indexCount > settings->maxIndexCount)
                        log->warning(log::wSourceConvertFbxExceedsIndices, indexCount, settings->maxIndexCount);
                    if (mesh->GetElementMaterialCount() <= 0) {
                        log->error(log::wSourceConvertFbxNoMaterial, getGeometryName(mesh));
                        continue;
                    }

                    //スキニングされてない場合、親ノードへ強制的にスキニングする
                    if (mesh->GetDeformerCount(FbxDeformer::eSkin)==0){

                        std::function<FbxNode*(FbxNode* node, FbxGeometry* geo)> findGeometry = [&findGeometry](FbxNode* node, FbxGeometry* geo)->FbxNode*{
                            if (node->GetGeometry() == geo)return node;
                            for (int i = 0; i < node->GetChildCount(); i++){
                                auto n = findGeometry(node->GetChild(i), geo);
                                if (n)return n;
                            }
                            return nullptr;
                        };

                        FbxNode* nChild = findGeometry(scene->GetRootNode(), geometry);
                        FbxNode* n = nChild->GetParent();
                        n->RemoveChild(nChild);
                        scene->GetRootNode()->AddChild(nChild);
                        FbxAnimEvaluator* mySceneEvaluator = scene->GetAnimationEvaluator();
                        auto mBase = mySceneEvaluator->GetNodeGlobalTransform(n);
                        auto m = mBase.Inverse();

                        FbxCluster *lClusterToRoot = FbxCluster::Create(scene, "");
                        lClusterToRoot->SetLink(n);
                        lClusterToRoot->SetTransformLinkMatrix(mBase);
                        lClusterToRoot->SetLinkMode(FbxCluster::eTotalOne);
                        for (int j = 0; j < mesh->GetControlPointsCount(); ++j){
                            lClusterToRoot->AddControlPointIndex(j, 1.0f);
                        }
                        FbxSkin* lSkin = FbxSkin::Create(scene, "");
                        lSkin->AddCluster(lClusterToRoot);
                        mesh->AddDeformer(lSkin);
                    }
                    FbxMeshInfo * const info = new FbxMeshInfo(log, mesh, settings->packColors, settings->maxVertexBonesCount, settings->forceMaxVertexBoneCount, settings->maxNodePartBonesCount);
                    meshInfos.push_back(info);
                    fbxMeshMap[mesh] = info;
                    if (info->bonesOverflow)
                        log->warning(log::wSourceConvertFbxExceedsBones);
                }
                else {
                    log->warning(log::wSourceConvertFbxDuplicateMesh, getGeometryName(geometry));
                }
            }
        }

cgfx内のテクスチャ名をdiffuseに使用する

Cgfxシェーダー利用時にマテリアル置き換えしている箇所を改変し、Cgfx中のテクスチャをdiffuseとしてアサインするようにします。
unityちゃんではDiffuseTextureというテクスチャがdiffuseに該当しているので、それを利用します。

FbxConvert.h
        Material *createMaterial(FbxSurfaceMaterial * const &material) {    
            Material * const result = new Material();
            result->source = material;
            result->id = material->GetName();

            if ((!material->Is<FbxSurfaceLambert>()) || GetImplementation(material, FBXSDK_IMPLEMENTATION_HLSL) || GetImplementation(material, FBXSDK_IMPLEMENTATION_CGFX)) {
                if (!material->Is<FbxSurfaceLambert>()){
                    log->warning(log::wSourceConvertFbxMaterialUnknown, result->id.c_str());
                    float f[3] = { 1, 0, 0 };
                    result->diffuse.set(f);
                }
                if (GetImplementation(material, FBXSDK_IMPLEMENTATION_HLSL)){
                    log->warning(log::wSourceConvertFbxMaterialHLSL, result->id.c_str());
                    float f[3] = { 1, 0, 0 };
                    result->diffuse.set(f);
                }
                if (GetImplementation(material, FBXSDK_IMPLEMENTATION_CGFX)){
                    log->warning(log::wSourceConvertFbxMaterialCgFX, result->id.c_str());
                    float f[3] = { 1, 1, 1 };
                    result->diffuse.set(f);

                    //cgfxからdiffuseテクスチャ名を探す
                    auto lImplementation = GetImplementation(material, FBXSDK_IMPLEMENTATION_CGFX);

                    const FbxBindingTable* lRootTable = lImplementation->GetRootTable();
                    FbxString lFileName = lRootTable->DescAbsoluteURL.Get();
                    FbxString lTechniqueName = lRootTable->DescTAG.Get();

                    const FbxBindingTable* lTable = lImplementation->GetRootTable();
                    size_t lEntryNum = lTable->GetEntryCount();

                    for (int i = 0; i < (int)lEntryNum; ++i)
                    {
                        const FbxBindingTableEntry& lEntry = lTable->GetEntry(i);
                        const char* lEntrySrcType = lEntry.GetEntryType(true);
                        FbxProperty lFbxProp;

                        FbxString lTest = lEntry.GetSource();
                        if (lTest.Find("DiffuseTexture") < 0)continue;

                        if (strcmp(FbxPropertyEntryView::sEntryType, lEntrySrcType) == 0)
                        {
                            lFbxProp = material->FindPropertyHierarchical(lEntry.GetSource());
                            if (!lFbxProp.IsValid())
                            {
                                lFbxProp = material->RootProperty.FindHierarchical(lEntry.GetSource());
                            }
                        }
                        else if (strcmp(FbxConstantEntryView::sEntryType, lEntrySrcType) == 0)
                        {
                            lFbxProp = lImplementation->GetConstants().FindHierarchical(lEntry.GetSource());
                        }
                        if (lFbxProp.IsValid())
                        {
                            if (lFbxProp.GetSrcObjectCount<FbxTexture>() > 0)
                            {
                                for (int j = 0; j < lFbxProp.GetSrcObjectCount<FbxFileTexture>(); ++j)
                                {
                                    FbxFileTexture *lTex = lFbxProp.GetSrcObject<FbxFileTexture>(j);
                                    add_if_not_null(result->textures, createTexture(lTex, Material::Texture::Diffuse));
                                }
                            }
                        }
                    }
                }
                return result;
            }

あとはfbx-convをビルドし、出来上がったexeで変換すれば問題が解決するはずです。
またフェイシャル以外のアニメーションはそのまま利用できたので、必要ならunitypackage付属のアニメーションを変換して利用すると良いかと思います。

23
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
20