可可西

UE4之StaticMesh和SkeletalMesh类关系图

StaticMesh类关系图

StaticMesh渲染数据结构

 

SkeletalMesh类关系图

 

USkinnedMeshComponent::CreateRenderState_Concurrent函数

void USkinnedMeshComponent::CreateRenderState_Concurrent(FRegisterComponentContext* Context)
{
    LLM_SCOPE(ELLMTag::SkeletalMesh);

    if( SkeletalMesh )
    {
        // Attempting to track down UE-45505, where it looks as if somehow a skeletal mesh component's mesh has only been partially loaded, causing a mismatch in the LOD arrays
        checkf(!SkeletalMesh->HasAnyFlags(RF_NeedLoad | RF_NeedPostLoad | RF_NeedPostLoadSubobjects | RF_WillBeLoaded), TEXT("Attempting to create render state for a skeletal mesh that is is not fully loaded. Mesh: %s"), *SkeletalMesh->GetName());

        // Initialize the alternate weight tracks if present BEFORE creating the new mesh object
        InitLODInfos();

        // No need to create the mesh object if we aren't actually rendering anything (see UPrimitiveComponent::Attach)
        if ( FApp::CanEverRender() && ShouldComponentAddToScene() )
        {
            ERHIFeatureLevel::Type SceneFeatureLevel = GetWorld()->FeatureLevel;
            FSkeletalMeshRenderData* SkelMeshRenderData = SkeletalMesh->GetResourceForRendering();
            int32 MinLODIndex = ComputeMinLOD();
            
#if DO_CHECK
            for (int LODIndex = MinLODIndex; LODIndex < SkelMeshRenderData->LODRenderData.Num(); LODIndex++)
            {
                FSkeletalMeshLODRenderData& LODData = SkelMeshRenderData->LODRenderData[LODIndex];
                const FPositionVertexBuffer* PositionVertexBufferPtr = &LODData.StaticVertexBuffers.PositionVertexBuffer;
                if (!PositionVertexBufferPtr || (PositionVertexBufferPtr->GetNumVertices() <= 0))
                {
                    UE_LOG(LogSkinnedMeshComp, Warning, TEXT("Invalid Lod %i for Rendering Asset: %s"), LODIndex, *SkeletalMesh->GetFullName());
                }
            }
#endif
    
            // Also check if skeletal mesh has too many bones/chunk for GPU skinning.
            if (bRenderStatic)
            {
                // GPU skin vertex buffer + LocalVertexFactory
                MeshObject = ::new FSkeletalMeshObjectStatic(this, SkelMeshRenderData, SceneFeatureLevel); 
            }
            else if(ShouldCPUSkin())
            {
                MeshObject = ::new FSkeletalMeshObjectCPUSkin(this, SkelMeshRenderData, SceneFeatureLevel);
            }
            // don't silently enable CPU skinning for unsupported meshes, just do not render them, so their absence can be noticed and fixed
            else if (!SkelMeshRenderData->RequiresCPUSkinning(SceneFeatureLevel, MinLODIndex)) 
            {
                MeshObject = ::new FSkeletalMeshObjectGPUSkin(this, SkelMeshRenderData, SceneFeatureLevel);
            }
            else
            {
                int32 MaxBonesPerChunk = SkelMeshRenderData->GetMaxBonesPerSection(MinLODIndex);
                int32 MaxSupportedGPUSkinBones = FMath::Min(GetFeatureLevelMaxNumberOfBones(SceneFeatureLevel), FGPUBaseSkinVertexFactory::GetMaxGPUSkinBones());
                int32 NumBoneInfluences = SkelMeshRenderData->GetNumBoneInfluences(MinLODIndex);
                FString FeatureLevelName; GetFeatureLevelName(SceneFeatureLevel, FeatureLevelName);

                UE_LOG(LogSkinnedMeshComp, Warning, TEXT("SkeletalMesh %s, is not supported for current feature level (%s) and will not be rendered. MinLOD %d, NumBones %d (supported %d), NumBoneInfluences: %d"), 
                    *GetNameSafe(SkeletalMesh), *FeatureLevelName, MinLODIndex, MaxBonesPerChunk, MaxSupportedGPUSkinBones, NumBoneInfluences);
            }

            //Allow the editor a chance to manipulate it before its added to the scene
            PostInitMeshObject(MeshObject);
        }
    }

    Super::CreateRenderState_Concurrent(Context);

    if (SkeletalMesh)
    {
        // Update dynamic data

        if(MeshObject)
        {
            // Clamp LOD within the VALID range
            // This is just to re-verify if LOD is WITHIN the valid range
            // Do not replace this with UpdateLODStatus, which could change the LOD 
            //    without animated, causing random skinning issues
            // This can happen if your MinLOD is not valid anymore after loading
            // which causes meshes to be invisible
            {
                int32 MinLodIndex = ComputeMinLOD();
                int32 MaxLODIndex = MeshObject->GetSkeletalMeshRenderData().LODRenderData.Num() - 1;
                PredictedLODLevel = FMath::Clamp(PredictedLODLevel, MinLodIndex, MaxLODIndex);
            }

            // If we have a valid LOD, set up required data, during reimport we may try to create data before we have all the LODs
            // imported, in that case we skip until we have all the LODs
            if(SkeletalMesh->IsValidLODIndex(PredictedLODLevel))
            {
                const bool bMorphTargetsAllowed = CVarEnableMorphTargets.GetValueOnAnyThread(true) != 0;

                // Are morph targets disabled for this LOD?
                if (bDisableMorphTarget || !bMorphTargetsAllowed)
                {
                    ActiveMorphTargets.Empty();
                }

                MeshObject->Update(PredictedLODLevel, this, ActiveMorphTargets, MorphTargetWeights, EPreviousBoneTransformUpdateMode::UpdatePrevious);  // send to rendering thread
            }
        }

        // scene proxy update of material usage based on active morphs
        UpdateMorphMaterialUsageOnProxy();
    }
}

 

勾选Render Static,会走FSkeletalMeshObjectStatic逻辑:

 

SkeletalMesh渲染数据结构(SkeletalMesh)

 

注1:勾选了Use Unlimited Bone InfluencesFSkinWeightVertexBuffer中使用FSkinWeightLookupVertexBuffer来存储骨骼权重数据,否则使用FSkinWeightDataVertexBuffer来存储

注2:勾选Support 16-bit Bone Index,最多只能使用65536根骨骼,但可以有效降低骨骼IndexBuffer显存的占用

 

SkeletalMesh渲染数据结构(FSkeletalMeshObject)

 

posted on 2024-04-27 22:05  可可西  阅读(504)  评论(0编辑  收藏  举报

导航