CPD values in Instanced Static Meshes and HISMs in a Packed Level Actor are not editable

Hello,

We are using CPD values of our primitives to affect when certain primitives obstruct the player to the camera so they fade out. We group them under this FadeGroupActor, which then gathers the primitives and sets the values to match. It works as expected for Static Meshes, level instances, most primitives that group under it. However, we are running into an issue when attempting to modify a primitives CPD values that is ISM or HISM, inside of a Packed Level Actor.

I’ve seen some bug reports about ISM/HISM responding strangely to CPD changes. Any advice would be much appreciated.

--David

The setting func:
 
 
void AFadeGroupActor::SetCPDValues(const TArray<UPrimitiveComponent*>& Primitives) const
{
    for (UPrimitiveComponent* Primitive : Primitives)
    {
       if (Primitive)
       {
          if (UNiagaraComponent* FXComponent = Cast<UNiagaraComponent>(Primitive))
          {
             FXComponent->SetVariableBool(FName("User.Fade_UseCameraDitherFade"), static_cast<bool>(bEnableFade));
             FXComponent->SetVariableBool(FName("User.Fade_EnableFloorCheck"), static_cast<bool>(bEnableFloorCheck));
             FXComponent->SetVariableFloat(FName("User.Fade_FloorOffset"), FloorOffset);
 
             const FTransform Transform = GetTransform();
 
             if (Transform.IsValid())
             {
                FXComponent->SetVariableVec3(FName("User.Fade_PivotOverride"), Transform.GetLocation());
             }
          }
          else
          {
             int32 TestIndex = Primitive->GetCustomPrimitiveDataIndexForScalarParameter(CustomPrimitiveData_FadeEnabled);
 
             if (TestIndex >= 0)
             {
                Primitive->SetScalarParameterForDefaultCustomPrimitiveData(CustomPrimitiveData_FadeEnabled, bEnableFade);
                Primitive->SetScalarParameterForDefaultCustomPrimitiveData(CustomPrimitiveData_EnableFloorCheck, bEnableFloorCheck);
                Primitive->SetScalarParameterForDefaultCustomPrimitiveData(CustomPrimitiveData_FloorOffset, FloorOffset);
 
                const FTransform Transform = GetTransform();
 
                if (Transform.IsValid())
                {
                   Primitive->SetVectorParameterForDefaultCustomPrimitiveData(CustomPrimitiveData_PivotOverride, Transform.GetLocation());
                }
             }
          }
       }
    }
}

Steps to Reproduce

  • Attempt to modify a primitives CPD values with SetScalarParameterForDefaultCustomPrimitiveData() of an Instanced or HISM mesh, inside of a Packed Level Actor.

  • Observe no edits being applied

The gathering function. Its a little messy since Ive been break pointing.

TArray<UPrimitiveComponent*> AFadeGroupActor::GetAllPrimitives()
{
	TArray<UPrimitiveComponent*> Primitives;
 
	ForEachAttachedActors(
		[&Primitives](AActor* Actor)
		{
			bool bIsLevelInstance = Actor->IsA(ALevelInstance::StaticClass());
			TInlineComponentArray<UPrimitiveComponent*> ActorPrimitives(Actor, true);
 
			if (bIsLevelInstance && ActorPrimitives.Num() == 0)
			{
				if (const ALevelInstance* LevelInstanceActor = Cast<ALevelInstance>(Actor))
				{
					if (const ULevel* InstanceLevel = LevelInstanceActor->GetLoadedLevel())
					{
						TArray<AActor*> LevelActors = InstanceLevel->Actors;
 
						for (const AActor* LevelActor : LevelActors)
						{
							if (LevelActor && LevelActor != Actor)
							{
								TInlineComponentArray<UPrimitiveComponent*> LevelPrimitives(LevelActor, true);
 
								for (UPrimitiveComponent* Prim : LevelPrimitives)
								{
									if (Prim && (Cast<UStaticMeshComponent>(Prim) ||
												Cast<UInstancedStaticMeshComponent>(Prim) ||
												Cast<UHierarchicalInstancedStaticMeshComponent>(Prim) ||
												Cast<UNiagaraComponent>(Prim)))
									{
										Primitives.Add(Prim);
									}
								}
							}
						}
					}
				}
				else
				{
					UE_LOG(LogTemp, Warning, TEXT("Skipping LevelInstance actor: %s - not a proper LevelInstance"), *Actor->GetName());
				}
			}
			else if (Actor->IsA(APackedLevelActor::StaticClass()))
			{
				// Also need a special check for ISM/HISM.
				if(APackedLevelActor* PLA = Cast<APackedLevelActor>(Actor))
				{
					TArray<UActorComponent*> AllComponents;
					PLA->GetComponents(AllComponents);
 
					//All the PLA getters are wrapped in IF_EDITORS so we have to a hard string check, but its what those getters do anyways
					const FName PackedComponentTag("PackedComponent");
					for(UActorComponent* ActorComp : AllComponents)
					{
						if(ActorComp && ActorComp->ComponentHasTag(PackedComponentTag))
						{
							if(UInstancedStaticMeshComponent* Instanced = Cast<UInstancedStaticMeshComponent>(ActorComp))
							{
								Primitives.Add(Instanced);
							}
							else if(UHierarchicalInstancedStaticMeshComponent* HISM = Cast<UHierarchicalInstancedStaticMeshComponent>(ActorComp))
							{
								Primitives.Add(HISM);
							}
						}
					}
				}
			}
			else
			{
				// Normal actor - just add its primitives
				Primitives.Append(ActorPrimitives);
			}
 
			return true;
		});
 
	return Primitives;
}

Hi,

I started investigating this issue and found some UDN cases which seem related, some providing a workaround:

[Content removed]

[Content removed]

[Content removed]

You can try porting the solution provided in the first two cases from Blueprint to C++. If that doesn’t work, would you be able to provide a minimal repro project as that would speed up the debugging process?

Thanks,

Sam

Thank you for posting these links. I will try these material setups. I had ran into the first link and attempted it with no results before, I think it may be due to other factors in a complicated material. Ill setup a testing material and report back. Appreciate it.

Okay, I was able to solve. The problem was not the code I actually posted. We were collecting the data correctly. Nor was it the materials. It was how we we’re then using the data that had some incorrect checks in it. The material now fades as expected.

We have a fade component that lives on our player character. It takes the CPD values and does a bunch of other checks on the primitives before it considers them viable for fading. The code was clobbering itself at a certain point and skipping ISM/HISMs.

Relevant code for those curious:

if (Primitive->IsA(UInstancedStaticMeshComponent::StaticClass()))
		{
			ISMComponent = Cast<UInstancedStaticMeshComponent>(Primitive);
			if (AActor* OwnerActor = ISMComponent->GetOwner())
			{
				if (AFadeGroupActor* FadeGroup = Cast<AFadeGroupActor>(OwnerActor->GetAttachParentActor()))
				{
					bIsFadeRelevant = FadeGroup->bEnableFade;
				}
				else
				{
					int32 Index = ISMComponent->GetCustomPrimitiveDataIndexForScalarParameter(CustomPrimitiveData_FadeEnabled);
					const TArray<float>& CustomData = ISMComponent->GetCustomPrimitiveData().Data;
					if (Index < 0 || CustomData.Num() <= Index)
					{
						continue;
					}
					bIsFadeRelevant = CustomData[Index] > 0.0f;
				}
 
				FTransform Transform;
				PrimitivePosition = ISMComponent->GetComponentLocation();
				ISMComponent->GetInstanceTransform(ISMIndex, Transform, true);
 
				if (Transform.IsValid())
				{
					PrimitivePosition = Transform.GetLocation();
				}
			}
		}
 
//// more checks here ///
///then 
 
// Player is roughly farther away from the camera than the object, so assume it's obscuring.
		if (DistCameraToPlayerSq > DistCameraToEnvObjSq)
		{
			// PackedLevelActor Component (UHierarchicalInstancedStaticMeshComponent) - MUST come before ISM handling
			if (Primitive->IsA(UInstancedStaticMeshComponent::StaticClass()))
			{
				ViableEnv.Add(Primitive);
			}
			else if (ISMComponent)
			{
				if (ISMComponent->IsValidInstance(ISMIndex))
				{
					ViableEnvISM.Add(Elem);
				}
			}
			else
			{
				ViableEnv.Add(Primitive);
			}
		}

Thanks for the rubber duck :baby_chick: It prompted me to look into the rest of the code base for answers. The joys of inheriting code that you’re not familiar with.

-- David

Hi,

great you found the solution to your issue and thanks for sharing the relevant code. I’m glad I could be of help. I’ll go ahead and close this case. Feel free to open a new one if you encounter more issues.

Best regards,

Sam