zoukankan      html  css  js  c++  java
  • UE4笔记-Runtime下使用Assimp库加载任意3D格式文件(fbx/obj/gltf等)到UStaticMesh

    备忘笔记..........

    ---------------------------- 割 --------------------------------------------

    实现的插件地址:

    https://github.com/linqingwudiv1/RuntimeMeshLoaderextend

    (居于https://github.com/GameInstitute/RuntimeMeshLoader 开源的修改)

    原本的RuntimeMeshLoader 源码仅支持从Assimp数据到UProducalMeshComponent。

    并不能满足我希望从assimp网格化数据到UStaticMesh的转换

    所以就参考UE4源码的ProceduralMeshComponentEditor 模块的ProceduralMeshComponentDetails.cpp里的ClickedOnConvertToStaticMesh 函数,

    对RuntimeMeshLoader插件进行扩展

    予人肥皂,手有余香 ( 滑稽 ) 

    可优化点记录: (随缘更新完善)

      1.使用TBB加速

      2.StaticMesh 单分段单材质,并导入格式自带贴图

      3.SkeletalMesh的动态加载

      4.可Transform变换

      5.以及全平台支持(Assimp是支持全平台的,但是,目前我只编译了Windows 和 Mac OSX 平台下的Assimp Library(带 export 模块),linux或andorid/IOS等需要自己编译集成.a,.so)

        6.Collision多方式支持

    -------------------------------------- 割 -------------------------------------------

     nOTE:有个挺严重的问题:打包后Collision失效,正在翻UStaticMesh的BodySetup尝试决解ing..(人生苦短我看源码,难啊!)

    翻源码的分析过程记录:

    目标:在不改动源码的的情况下,在Runtime环境下根据Assimp加载的网格创建UStaticMesh

    主要参考 UE4 自带插件 UProceduralMeshComponent 的 Editor 功能:FProceduralMeshComponentDetails::ClickedOnConvertToStaticMesh 函数,

    在使用assimp库导入原生信息时,Vertex到StaticMesh的过程,中间有遇到一些Editor函数转

    通过assimp加载网格 环境完整代码参考:

    .h

    UENUM(BlueprintType)
    enum class EPathType : uint8
    {
        Absolute UMETA(DisplayName = "Absolute"),
        Relative UMETA(DisplayName = "Relative")
    };
    
    USTRUCT(BlueprintType)
    struct FMeshInfo
    {
        GENERATED_USTRUCT_BODY()
        
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FVector> Vertices;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            /** Vertices index */
            TArray<int32> Triangles;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FVector> Normals;
        
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FVector2D> UV0;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FVector2D> UV1;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FVector2D> UV2;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FVector2D> UV3;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FLinearColor> VertexColors;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FProcMeshTangent> Tangents;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            FTransform RelativeTransform;
    };
    
    USTRUCT(BlueprintType)
    struct FReturnedData
    {
        GENERATED_USTRUCT_BODY()
    
    public:
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            /**/
            bool bSuccess;
    
        /** Contain Mesh Count  */
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            int32 NumMeshes;
    
        UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "ReturnedData")
            TArray<FMeshInfo> meshInfo;
    };
    
    
    
    
    /**
     * 
     */
    UCLASS()
    class RUNTIMEMESHLOADER_API ULoaderBPFunctionLibrary : public UBlueprintFunctionLibrary
    {
        GENERATED_BODY()
    public:
        
        /**  */
        UFUNCTION( BlueprintCallable, Category="MeshLoader")
            static FReturnedData LoadMesh(const FString& filepath, const FTransform& tran, EPathType type= EPathType:: Absolute );
    /**  */
        UFUNCTION( BlueprintCallable, Category = "MeshLoader", meta = ( HidePin = "WorldContextObject", DefaultToSelf = "WorldContextObject" )  )
            static UStaticMesh* LoadMeshToStaticMesh( UObject* WorldContextObject, 
                                                      const FString& filepath, 
                                                      const FTransform& tran,
                                                      EPathType type = EPathType::Absolute );
    
    };

    .cpp

    void FindMeshInfo(const aiScene* scene, aiNode* node, FReturnedData& result,const FTransform &tran = FTransform())
    {
        //transform...
        aiMatrix4x4 TranMat,tempMat;
        
        
        bool bTran = false;
        if ( !tran.GetLocation().Equals(FVector{ 0.0f }, 0.01f ) )
        {
            bTran = true;
            TranMat = TranMat.Translation(aiVector3D{ tran.GetLocation().X, tran.GetLocation().Y, tran.GetLocation().Z }, tempMat);
        }
    
        if ( !tran.GetScale3D().Equals( FVector{ 1.0f }, 0.01f ) )
        {
            bTran = true;
            TranMat = TranMat.Scaling(aiVector3D{ tran.GetScale3D().X, tran.GetScale3D().Y, tran.GetScale3D().Z }, tempMat);
        }
    
        if ( !tran.GetRotation().Equals( FRotator{ 0.0f }.Quaternion(), 0.01f ) )
        {
            bTran = true;
            TranMat = TranMat.RotationX( PI / 180.f * tran.GetRotation().Rotator().Roll    , tempMat  );
            TranMat = TranMat.RotationY( PI / 180.f * tran.GetRotation().Rotator().Yaw    , tempMat  );
            TranMat = TranMat.RotationZ( PI / 180.f * tran.GetRotation().Rotator().Pitch    , tempMat  );
        }
    
    
        // for (uint32 i = 0; i < node->)
        for (uint32 i = 0; i < node->mNumMeshes; i++)
        {
            std::string TestString = node->mName.C_Str();
    
            FString Fs = FString(TestString.c_str());
    
            UE_LOG(LogTemp, Warning, TEXT("FindMeshInfo. %s
    "), *Fs);
    
            int meshidx = *node->mMeshes;
    
            aiMesh      *mesh = scene->mMeshes  [ meshidx ];
            FMeshInfo &mi    = result.meshInfo [ meshidx ];
            aiMatrix4x4 tempTrans = node->mTransformation;
            //如果变换
            if (bTran)
            {
                tempTrans = tempTrans * TranMat;
            }
    
            FMatrix tempMatrix;
    
            // e.g
            // _______________
            // | A0,B0,C0,D0 |
            // | A1,B1,C1,D1 |
            // | A2,B2,C2,D2 |
            // | A3,B3,C3,D3 |
            // |_____________|
            // 
            tempMatrix.M[0][0]     =    tempTrans.a1; tempMatrix.M[0][1] = tempTrans.b1; tempMatrix.M[0][2] = tempTrans.c1; tempMatrix.M[0][3] = tempTrans.d1;
            tempMatrix.M[1][0]     =    tempTrans.a2; tempMatrix.M[1][1] = tempTrans.b2; tempMatrix.M[1][2] = tempTrans.c2; tempMatrix.M[1][3] = tempTrans.d2;
            tempMatrix.M[2][0]     =    tempTrans.a3; tempMatrix.M[2][1] = tempTrans.b3; tempMatrix.M[2][2] = tempTrans.c3; tempMatrix.M[2][3] = tempTrans.d3;
            tempMatrix.M[3][0]     =    tempTrans.a4; tempMatrix.M[3][1] = tempTrans.b4; tempMatrix.M[3][2] = tempTrans.c4; tempMatrix.M[3][3] = tempTrans.d4;
    
            // Mesh transform on scene
            mi.RelativeTransform =    FTransform(tempMatrix);
    
            // fill Mesh Vertices 填充Mesh顶点
            for (uint32 j = 0; j < mesh->mNumVertices; ++j)
            {
                FVector vertex = FVector (
                    mesh->mVertices[j].x ,
                    mesh->mVertices[j].y ,
                    mesh->mVertices[j].z );
    
                vertex = mi.RelativeTransform.TransformFVector4(vertex);
                // vertex = mi.RelativeTransform.Trans
                mi.Vertices.Push( vertex );
    
                //Normal
                if (mesh->HasNormals())
                {
                    FVector normal = FVector(
                        mesh->mNormals[j].x ,
                        mesh->mNormals[j].y ,
                        mesh->mNormals[j].z  );

              if (bTran)
              {
                normal = mi.RelativeTransform.TransformFVector4(normal);
              }

                    mi.Normals.Push(normal);
                }
                else
                {
                    mi.Normals.Push(FVector::ZeroVector);
                }
    
                // UV0 Coordinates - inconsistent coordinates
                if (mesh->HasTextureCoords(0))
                {
                    FVector2D uv = FVector2D(mesh->mTextureCoords[0][j].x, -mesh->mTextureCoords[0][j].y);
                    mi.UV0.Add(uv);
                }
                // UV1 Coordinates - inconsistent coordinates
                if (mesh->HasTextureCoords(1))
                {
                    FVector2D uv = FVector2D(mesh->mTextureCoords[1][j].x, -mesh->mTextureCoords[1][j].y);
                    mi.UV1.Add(uv);
                }
                // UV2 Coordinates - inconsistent coordinates
                if (mesh->HasTextureCoords(2))
                {
                    FVector2D uv = FVector2D(mesh->mTextureCoords[2][j].x, -mesh->mTextureCoords[2][j].y);
                    mi.UV2.Add(uv);
                }
    
                // UV3 Coordinates - inconsistent coordinates
                if (mesh->HasTextureCoords(3))
                {
                    FVector2D uv = FVector2D(mesh->mTextureCoords[3][j].x, -mesh->mTextureCoords[3][j].y);
                    mi.UV3.Add(uv);
                }
    
                // Tangent /切线
                if (mesh->HasTangentsAndBitangents())
                {
                    FProcMeshTangent meshTangent = FProcMeshTangent(
                        mesh->mTangents[j].x,
                        mesh->mTangents[j].y,
                        mesh->mTangents[j].z
                    );
    
                    mi.Tangents.Push(meshTangent);
                }
    
                //Vertex color
                if (mesh->HasVertexColors(0))
                {
                    FLinearColor color = FLinearColor(
                        mesh->mColors[0][j].r,
                        mesh->mColors[0][j].g,
                        mesh->mColors[0][j].b,
                        mesh->mColors[0][j].a
                    );
                    mi.VertexColors.Push(color);
                }
            }
        }
    }
    
    void FindMesh(const aiScene* scene, aiNode* node, FReturnedData& retdata, const  FTransform &tran)
    {
        FindMeshInfo(scene, node, retdata);
    
        // tree node 
        for ( uint32 m = 0; m < node->mNumChildren; ++m )
        {
            FindMesh(scene, node->mChildren[m], retdata, tran);
        }
    }
    /**
     *
     */
    TMap<UMaterialInterface*, FPolygonGroupID> BuildMaterialMapExchange(FReturnedData& ReturnedData, /* UProceduralMeshComponent* ProcMeshComp ,*/ FMeshDescription& MeshDescription)
    {
        TMap<UMaterialInterface*, FPolygonGroupID> UniqueMaterials;
        const int32 NumSections =  ReturnedData.meshInfo.Num(); //ProcMeshComp->GetNumSections();
        UniqueMaterials.Reserve(NumSections);
    
        FStaticMeshAttributes AttributeGetter(MeshDescription);
        TPolygonGroupAttributesRef<FName> PolygonGroupNames = AttributeGetter.GetPolygonGroupMaterialSlotNames();
    
        for ( int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++ )
        {
            FMeshInfo MeshInfo = ReturnedData.meshInfo[SectionIdx];
            // MeshInfo.Normals
            UMaterialInterface* Material = UMaterial::GetDefaultMaterial(MD_Surface);
            
            if ( !UniqueMaterials.Contains(Material) )
            {
                FPolygonGroupID NewPolygonGroup = MeshDescription.CreatePolygonGroup();
                
                UniqueMaterials.Add(Material, NewPolygonGroup);
                PolygonGroupNames[NewPolygonGroup] = Material->GetFName();
            }
        }
        
        return UniqueMaterials;
    }
    
    
    /**
     *
     */
    FMeshDescription BuildMeshDescriptionExtend( FReturnedData& MeshsData /* UProceduralMeshComponent* ProcMeshComp */)
    {
        FMeshDescription MeshDescription;
    
        FStaticMeshAttributes AttributeGetter(MeshDescription);
        AttributeGetter.Register();
    
        TPolygonGroupAttributesRef<FName> PolygonGroupNames = AttributeGetter.GetPolygonGroupMaterialSlotNames();
        TVertexAttributesRef<FVector> VertexPositions        = AttributeGetter.GetVertexPositions();
        TVertexInstanceAttributesRef<FVector> Tangents        = AttributeGetter.GetVertexInstanceTangents();
        TVertexInstanceAttributesRef<float> BinormalSigns    = AttributeGetter.GetVertexInstanceBinormalSigns();
        TVertexInstanceAttributesRef<FVector> Normals        = AttributeGetter.GetVertexInstanceNormals();
        TVertexInstanceAttributesRef<FVector4> Colors        = AttributeGetter.GetVertexInstanceColors();
        TVertexInstanceAttributesRef<FVector2D> UVs            = AttributeGetter.GetVertexInstanceUVs();
    
        // Materials to apply to new mesh
        
        const int32 NumSections = MeshsData.meshInfo.Num(); // ProcMeshComp->GetNumSections();
        int32 VertexCount = 0;
        int32 VertexInstanceCount = 0;
        int32 PolygonCount = 0;
    
        TMap<UMaterialInterface*, FPolygonGroupID> UniqueMaterials = BuildMaterialMapExchange(MeshsData, MeshDescription);
        TArray<FPolygonGroupID> PolygonGroupForSection;
        PolygonGroupForSection.Reserve(NumSections);
        // Calculate the totals for each ProcMesh element type
        for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
        {
            FMeshInfo MeshInfo = MeshsData.meshInfo[SectionIdx];
    
            VertexCount            +=  MeshInfo.Vertices.Num ()     ; // ProcSection->ProcVertexBuffer.Num();
            VertexInstanceCount +=  MeshInfo.Triangles.Num()     ; // ProcSection->ProcIndexBuffer.Num();
            PolygonCount        +=  MeshInfo.Triangles.Num() / 3 ; // ProcSection->ProcIndexBuffer.Num() / 3;
        }
    
        MeshDescription.ReserveNewVertices(VertexCount);
        MeshDescription.ReserveNewVertexInstances(VertexInstanceCount);
        MeshDescription.ReserveNewPolygons( PolygonCount );
        MeshDescription.ReserveNewEdges( PolygonCount * 2);
        UVs.SetNumIndices(4);
        
        // Create the Polygon Groups
        for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
        {
            FMeshInfo MeshInfo = MeshsData.meshInfo[SectionIdx];
    
            UMaterialInterface* Material = UMaterial::GetDefaultMaterial(MD_Surface);
    
            FPolygonGroupID* PolygonGroupID = UniqueMaterials.Find(Material);
            check( PolygonGroupID != nullptr );
            PolygonGroupForSection.Add(*PolygonGroupID);
        }
        
        // Add Vertex and VertexInstance and polygon for each section
        for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
        {
            FMeshInfo MeshInfo = MeshsData.meshInfo[SectionIdx];
            FPolygonGroupID PolygonGroupID = PolygonGroupForSection[SectionIdx];
            // Create the vertex
            int32 NumVertex = MeshInfo.Vertices.Num();
            TMap<int32, FVertexID> VertexIndexToVertexID;
            VertexIndexToVertexID.Reserve(NumVertex);
    
            for (int32 VertexIndex = 0; VertexIndex < NumVertex; ++VertexIndex)
            {
                FVector Vert = MeshInfo.Vertices[VertexIndex];
    
                const FVertexID VertexID = MeshDescription.CreateVertex();
                VertexPositions[VertexID] = Vert;
                VertexIndexToVertexID.Add(VertexIndex, VertexID);
            }
    
            // Create the VertexInstance
            int32 NumIndices = MeshInfo.Triangles.Num();
    
            int32 NumTri = NumIndices / 3;
            TMap<int32, FVertexInstanceID> IndiceIndexToVertexInstanceID;
            IndiceIndexToVertexInstanceID.Reserve(NumVertex);
            for (int32 IndiceIndex = 0; IndiceIndex < NumIndices; IndiceIndex++)
            {
                const int32 VertexIndex = MeshInfo.Triangles[IndiceIndex];
                const FVertexID VertexID = VertexIndexToVertexID[VertexIndex];
                const FVertexInstanceID VertexInstanceID = MeshDescription.CreateVertexInstance(VertexID);
                IndiceIndexToVertexInstanceID.Add(IndiceIndex, VertexInstanceID);
    
                FVector ProcVertex = MeshInfo.Vertices[VertexIndex];        // FProcMeshVertex& ProcVertex = ProcSection->ProcVertexBuffer[VertexIndex];
                FProcMeshTangent VertexTanents = MeshInfo.Tangents[VertexIndex];
                
                FLinearColor VertexColor = MeshInfo.VertexColors.Num() > VertexIndex ? MeshInfo.VertexColors[VertexIndex] : FLinearColor(1.0, 0.0, 0.0);
    
                Tangents[VertexInstanceID] = VertexTanents.TangentX;        // ProcVertex.Tangent.TangentX;
                Normals[VertexInstanceID] = MeshInfo.Normals[VertexIndex];    // ProcVertex.Normal;
                BinormalSigns[VertexInstanceID] = VertexTanents.bFlipTangentY ? -1.f : 1.f;
    
                Colors[VertexInstanceID] = VertexColor; //FLinearColor(ProcVertex.Color);
                
                if ( MeshInfo.UV0.Num() > VertexIndex)
                {
                    UVs.Set(VertexInstanceID, 0, MeshInfo.UV0[VertexIndex]);
                }
    
                if ( MeshInfo.UV1.Num() > VertexIndex)
                {
                    UVs.Set(VertexInstanceID, 1, MeshInfo.UV1[VertexIndex]);
                }
    
                if ( MeshInfo.UV2.Num() > VertexIndex )
                {
                    UVs.Set(VertexInstanceID, 2, MeshInfo.UV2[VertexIndex]);
                }
                
                if ( MeshInfo.UV3.Num() > VertexIndex )
                {
                    UVs.Set(VertexInstanceID, 3, MeshInfo.UV3[VertexIndex]);
                }
            }
    
            // Create the polygons for this section
            for (int32 TriIdx = 0; TriIdx < NumTri; TriIdx++)
            {
                FVertexID VertexIndexes[3];
                TArray<FVertexInstanceID> VertexInstanceIDs;
                VertexInstanceIDs.SetNum(3);
    
                for (int32 CornerIndex = 0; CornerIndex < 3; ++CornerIndex)
                {
                    const int32 IndiceIndex = (TriIdx * 3) + CornerIndex;
                    const int32 VertexIndex =  MeshInfo.Triangles[IndiceIndex]; //ProcSection->ProcIndexBuffer[IndiceIndex];
                    VertexIndexes[CornerIndex] = VertexIndexToVertexID[VertexIndex];
                    VertexInstanceIDs[CornerIndex] =
                        IndiceIndexToVertexInstanceID[IndiceIndex];
                }
    
                // Insert a polygon into the mesh
                MeshDescription.CreatePolygon(PolygonGroupID, VertexInstanceIDs);
            }
        }
        return MeshDescription;
    }
    UStaticMesh* ULoaderBPFunctionLibrary::LoadMeshToStaticMesh( UObject* WorldContextObject, 
                                                                 const FString& filepath, 
                                                                 const FTransform& tran,
                                                                 EPathType type /* = EPathType::Absolute */ 
                                                                  )
    {
        FReturnedData&& MeshInfo = ULoaderBPFunctionLibrary::LoadMesh(filepath,  tran, type);
    
        FString NewNameSuggestion = FString(TEXT("ProcMesh"));
        FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
        FString Name;
        FString UserPackageName = TEXT("");
        FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
    
        // Check if the user inputed a valid asset name, if they did not, give it the generated default name
        if (MeshName == NAME_None)
        {
            // Use the defaults that were already generated.
            UserPackageName = PackageName;
            MeshName = *Name;
        }
    
        FMeshDescription MeshDescription = BuildMeshDescriptionExtend(MeshInfo);
    
        UStaticMesh* StaticMesh = NewObject<UStaticMesh>(WorldContextObject, MeshName, RF_Public | RF_Standalone);
        
        StaticMesh->InitResources();
        StaticMesh->LightingGuid = FGuid::NewGuid();
    
        TArray<const FMeshDescription*> arr;
        arr.Add(&MeshDescription);
        StaticMesh->BuildFromMeshDescriptions(arr, false);
        
        //// MATERIALS
        TSet<UMaterialInterface*> UniqueMaterials;
        
        const int32 NumSections = 1;
        for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
        {
            UMaterialInterface* Material = UMaterial::GetDefaultMaterial(MD_Surface);
            UniqueMaterials.Add(Material);
            
        }
    
        // Copy materials to new mesh
        int32 MaterialID = 0;
        for (UMaterialInterface* Material : UniqueMaterials)
        {
            // Material
            FStaticMaterial&& StaticMat = FStaticMaterial(Material);
    
            StaticMat.UVChannelData.bInitialized = true;
            StaticMesh->StaticMaterials.Add(StaticMat);        
    
    #pragma region 模拟填充 FMeshSectionInfo
    
            FStaticMeshRenderData* const RenderData = StaticMesh->RenderData.Get();
    
            int32 LODIndex = 0;
            int32 MaxLODs = RenderData->LODResources.Num();
    
            for (; LODIndex < MaxLODs; ++LODIndex)
            {
                FStaticMeshLODResources& LOD = RenderData->LODResources[LODIndex];
    
                for (int32 SectionIndex = 0; SectionIndex < LOD.Sections.Num(); ++SectionIndex)
                {
                    FStaticMeshSection& Section = LOD.Sections[SectionIndex];
                    Section.MaterialIndex = MaterialID;
                    Section.bEnableCollision = true;
                    Section.bCastShadow = true;
                    Section.bForceOpaque = false;
                }
            }
    
    #pragma endregion     
            MaterialID++;
        }
    
        return StaticMesh;
    }
  • 相关阅读:
    步步为营-15-文件夹的操作
    步步为营-14-文件操作
    步步为营-13-日期转化
    步步为营-12-Dictionary-翻译
    步步为营-11-List<T>泛型的简单练习
    步步为营-10-string的简单操作
    步步为营-09-简单工厂类-计算器
    B. Fixed Points
    C. Cd and pwd commands
    Queries on a String
  • 原文地址:https://www.cnblogs.com/linqing/p/13970414.html
Copyright © 2011-2022 走看看