HISMC's lightmap data not loaded with LightingScenario

steps to reproduce (should succeed in 4.20, 4.21, and 4.22-preview7):

  • create a default level with a plane
    floor;
  • paint some foliage to the
    floor;
  • add a sub-level to the persistent level above, move all
    lights to the sub-level;
  • change the sub-level to a lighting scenario,
    set it to always loaded;
  • build the light;
  • start the game in a
    Standalone window or launch it.

I have traced the problem with RenderDoc to notice the value of LightMapBias and ShadowMapBias is -1.
And in UInstancedStaticMeshComponent::BuildRenderData, MeshMapBuildData is a nullptr because no active lighting scenario is loaded when calling GetMeshMapBuildData.
PropagateLightingScenarioChange should be called after the lighting scenario is loaded, and this override function was added to HISMC in 4.21, but it’s only working in Editor, not in Game.

What would be the proper way to call SetInstanceLightMapData in PropagateLightingScenarioChange in Game?

I got it to work by adding these codes:

void UHierarchicalInstancedStaticMeshComponent::PropagateLightingScenarioChange()
{
	if (GIsEditor)
	{
		if (PerInstanceRenderData.IsValid())
		{
			FComponentRecreateRenderStateContext Context(this);

			const FMeshMapBuildData* MeshMapBuildData = nullptr;
			if (LODData.Num() > 0)
			{
				MeshMapBuildData = GetMeshMapBuildData(LODData[0]);
			}

			if (MeshMapBuildData != nullptr)
			{
				for (int32 InstanceIndex = 0; InstanceIndex < PerInstanceSMData.Num(); ++InstanceIndex)
				{
					int32 RenderIndex = InstanceReorderTable.IsValidIndex(InstanceIndex) ? InstanceReorderTable[InstanceIndex] : InstanceIndex;

					InstanceUpdateCmdBuffer.SetLightMapData(RenderIndex, MeshMapBuildData->PerInstanceLightmapData[InstanceIndex].LightmapUVBias);
					InstanceUpdateCmdBuffer.SetShadowMapData(RenderIndex, MeshMapBuildData->PerInstanceLightmapData[InstanceIndex].ShadowmapUVBias);
				}
			}
		}
	}
	else
	{
		check(IsInGameThread());
		if (PerInstanceRenderData.IsValid())
		{
			FComponentRecreateRenderStateContext Context(this);

			const FMeshMapBuildData* MeshMapBuildData = nullptr;
			if (LODData.Num() > 0)
			{
				MeshMapBuildData = GetMeshMapBuildData(LODData[0]);
			}

			if (MeshMapBuildData != nullptr)
			{
				FRandomStream RandomStream = FRandomStream(InstancingRandomSeed);

				int32 NumRenderInstances = PerInstanceRenderData->InstanceBuffer_GameThread->GetNumInstances();
				check(PerInstanceSMData.Num() == NumRenderInstances);
				
				FStaticMeshInstanceData BuiltInstanceData;
				BuiltInstanceData.AllocateBuffers(NumRenderInstances);
				BuiltInstanceData.AllocateInstances(NumRenderInstances, GIsEditor ? EResizeBufferFlags::AllowSlackOnGrow | EResizeBufferFlags::AllowSlackOnReduce : EResizeBufferFlags::None, true); // In Editor always permit overallocation, to prevent too much realloc

				FVector2D LightmapUVBias = FVector2D(-1.0f, -1.0f);
				FVector2D ShadowmapUVBias = FVector2D(-1.0f, -1.0f);

				// we loop over all instances to ensure that render instances will get same RandomID regardless of density settings
				for (int32 InstanceIndex = 0; InstanceIndex < NumRenderInstances; ++InstanceIndex)
				{
					float RandomID = RandomStream.GetFraction();
					int32 RenderIndex = InstanceReorderTable.IsValidIndex(InstanceIndex) ? InstanceReorderTable[InstanceIndex] : InstanceIndex;

					FMatrix Transform;
					PerInstanceRenderData->InstanceBuffer_GameThread->GetInstanceTransform(RenderIndex, Transform);
					LightmapUVBias = MeshMapBuildData->PerInstanceLightmapData[InstanceIndex].LightmapUVBias;
					ShadowmapUVBias = MeshMapBuildData->PerInstanceLightmapData[InstanceIndex].ShadowmapUVBias;
					if (RenderIndex >= 0)
					{
						BuiltInstanceData.SetInstance(RenderIndex, Transform, RandomID, LightmapUVBias, ShadowmapUVBias);
					}
					// correct light/shadow map bias will be setup on game thread side if needed
				}
				PerInstanceRenderData->UpdateFromPreallocatedData(BuiltInstanceData);
			}
		}
	}
}

basically, it rebuilt the instances render data when lighting scenario is loaded.

I think the proper way to solve this problem is to make lighting scenario loaded before anything else in the persistent level, but that would require official support.

there is an issue ticket UE-72154 for this problem.