With UE5.7's Mass Entity, how can I modify the material of the ISM of a specific Entity?

Like if I have a Mass Entity with a Static mesh (using CrowdVisualization), and inside a Processor I want to modify the specific Entity’s Material Instance (Like a basic float), is it possible to modify this instanced material? If yes, how?

Thanks in advance.

use the ISMComponent->SetCustomDataValue function, using the instance index (from your visualizer fragment).

Then, in your material, use the PerInstanceCustomData, which will pass that custom data for that instance at the data index you provided.

I tried to do this, but I don’t know how to retrive the Instance Index, you have any idea how to do that specifically?

Nevermind, I found out how to make it, so for anyone that wants to do it just copy and paste this Processor, and just change the “FISMCustomData” with your own fragment that ONLY has floats.

include “ISMCustomDataUpdaterProcessor.h”
include <MassRepresentationFragments.h>
include <MassRepresentationSubsystem.h>
include “MassLODFragments.h”

UISMCustomDataUpdaterProcessor::UISMCustomDataUpdaterProcessor() : EntityQuery(*this)
{
ExecutionFlags = (int32)EProcessorExecutionFlags::All;
ProcessingPhase = EMassProcessingPhase::PrePhysics;
bAutoRegisterWithProcessingPhases = true;

ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::Representation);

}

void UISMCustomDataUpdaterProcessor::ConfigureQueries(const TSharedRef& EntityManager)
{
EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly);
EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly);
EntityQuery.AddSharedRequirement(EMassFragmentAccess::ReadOnly);

RegisterQuery(EntityQuery);

}

void UISMCustomDataUpdaterProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{

EntityQuery.ForEachEntityChunk(EntityManager, Context, [](FMassExecutionContext& Context)
    {

        UMassRepresentationSubsystem* RepresentationSubsystem = Context.GetSharedFragment<FMassRepresentationSubsystemSharedFragment>().RepresentationSubsystem;
        const TConstArrayView<FMassRepresentationFragment> RepresentationFragments = Context.GetFragmentView<FMassRepresentationFragment>();
        const TConstArrayView<FISMCustomData> CustomDataFragments = Context.GetFragmentView<FISMCustomData>();

        const int32 NumEntities = Context.GetNumEntities();

        for (int32 EntityIndex = 0; EntityIndex < NumEntities; EntityIndex++)
        {
            const FMassRepresentationFragment& Representation = RepresentationFragments[EntityIndex];

            const FMassISMCSharedData* SharedData = RepresentationSubsystem->GetISMCSharedDataForDescriptionIndex(Representation.StaticMeshDescHandle.ToIndex());
            if (!SharedData)
            {
                continue;
            }

            UInstancedStaticMeshComponent* ISM = const_cast<FMassISMCSharedData*>(SharedData)->GetMutableISMComponent();
            if (!ISM)
            {
                continue;
            }

            const FPrimitiveInstanceId* PrimitiveId = SharedData->GetEntityPrimitiveToIdMap().Find(Context.GetEntity(EntityIndex));
            if (!PrimitiveId)
            {
                continue;
            }

            const int32 InstanceIndex = ISM->GetInstanceIndexForId(*PrimitiveId);
            if (InstanceIndex == INDEX_NONE)
            {
                continue;
            }

            const FISMCustomData& CustomData = CustomDataFragments[EntityIndex];

            ISM->SetCustomDataValue(InstanceIndex, 0, CustomData.Color, true);
        }
    });

}

Also make sure, that in the Material, you actually use “PerInstanceCustomData” rather than a normal parameter with CustomData set on.

Nevermind, I tested it some more, and it’s not actually working, I’m trying to see if there is any other way to do this.

At the end I couldn’t find a fix, so until than I’ll try to make my own Visualization, and see if that helps, because I’ve a feeling that my SetCustomDataValue is working, but because there is another ISMProcessor from CrowdVisualization, they are fighting and nothing is showing up.

I’ll update if I can find a way to work around this.

I think you’re close… Look at MassUpdateISMProcessor, the execute function. Notice the transform update batching (this is necessary, otherwise you’re creating the churn mass is designed to avoid). Then search for the AddBatchedCustomData… functions, do your processor’s execute in a similar way. Also, you need to make sure the ISM you’re using for your visualizer is set up with the correct number of Custom Data Channels (this happens on init of the ISM), otherwise the data has nowhere to go.

Great new, I was able to make it work without having to create my own visualization, I literally went on every Public Mass projects on Github and copied and pasted stuff until this one (from MassSample) finally worked.

#include "ISMCustomDataUpdaterProcessor.h"
#include "MassRepresentationFragments.h"
#include "MassRepresentationSubsystem.h"
#include "MassRepresentationTypes.h"

UISMCustomDataUpdaterProcessor::UISMCustomDataUpdaterProcessor()
{
    ExecutionFlags = (int32)(EProcessorExecutionFlags::Client | EProcessorExecutionFlags::Standalone | EProcessorExecutionFlags::Editor);

    ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::Representation);
}

void UISMCustomDataUpdaterProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager)
{
    EntityQuery.Initialize(EntityManager);

    EntityQuery.AddRequirement<FISMCustomData>(EMassFragmentAccess::ReadWrite);
    EntityQuery.AddRequirement<FMassRepresentationFragment>(EMassFragmentAccess::ReadWrite);
    EntityQuery.AddRequirement<FMassRepresentationLODFragment>(EMassFragmentAccess::ReadOnly);
    EntityQuery.AddSharedRequirement<FMassRepresentationSubsystemSharedFragment>(EMassFragmentAccess::ReadWrite);

    EntityQuery.RegisterWithProcessor(*this);
}

void UISMCustomDataUpdaterProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
    EntityQuery.ForEachEntityChunk(Context,
        [](FMassExecutionContext& Context)
        {
            UMassRepresentationSubsystem* RepresentationSubsystem = Context.GetMutableSharedFragment<FMassRepresentationSubsystemSharedFragment>().RepresentationSubsystem;

            check(RepresentationSubsystem);

            const FMassInstancedStaticMeshInfoArrayView ISMInfos = RepresentationSubsystem->GetMutableInstancedStaticMeshInfos();

            const int32 NumEntities = Context.GetNumEntities();

            const TArrayView<FMassRepresentationFragment> RepresentationList = Context.GetMutableFragmentView<FMassRepresentationFragment>();
            const TConstArrayView<FMassRepresentationLODFragment> RepresentationLODList = Context.GetFragmentView<FMassRepresentationLODFragment>();
            const TArrayView<FISMCustomData> CustomDatas = Context.GetMutableFragmentView<FISMCustomData>();

            for (int32 EntityIdx = 0; EntityIdx < NumEntities; EntityIdx++)
            {
                const FMassRepresentationFragment& Representation = RepresentationList[EntityIdx];

                if (Representation.CurrentRepresentation != EMassRepresentationType::StaticMeshInstance)
                {
                    continue;
                }

                FMassInstancedStaticMeshInfo& ISMInfo = ISMInfos[Representation.StaticMeshDescHandle.ToIndex()];

                const FMassRepresentationLODFragment& RepresentationLOD = RepresentationLODList[EntityIdx];

                const FISMCustomData& CustomData = CustomDatas[EntityIdx];

                ISMInfo.AddBatchedCustomData(CustomData.Color, RepresentationLOD.LODSignificance);
            }
        });
}

I’ve tested it with only one float and it works (If anything else happens I’ll come back), so worst case scenario you just have to make like one processor for each CustomData, which sounds nasty (and it is), but whatever gets the jobs done.
Remember that this is done in 5.7, and remember to use CrowdMember +CrowdVisualization and to set the CustomDataFloast to at least 1 on the StaticMesh section.
Also remember to actually use PerInstanceCustomData on your material.

PS. Also remember to switch this fragments here “FISMCustomData” with whatever fragment you have, BUT it can only contain floats (especially if you give it directly to AddBatchedCustomData).