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 Influences,FSkinWeightVertexBuffer中使用FSkinWeightLookupVertexBuffer来存储骨骼权重数据,否则使用FSkinWeightDataVertexBuffer来存储
注2:勾选Support 16-bit Bone Index,最多只能使用65536根骨骼,但可以有效降低骨骼IndexBuffer显存的占用
SkeletalMesh渲染数据结构(FSkeletalMeshObject)