How do i get a function to return a pointer to the Custom Data Value float of an instance static mesh?

Is it not possible to return pointers to floats?

Im not being able to compile it. Getting the error:

error : Inappropriate '’ on variable of type ‘float’, cannot have an exposed pointer to this type.*

Is this specific to unreal?

TArray<float*> AHISM_Test::GetAllInstancesAddresses(UHierarchicalInstancedStaticMeshComponent* HISM_L, int32 InstanceIndex, int32 CustomDataIndex) {
	// Get the addresses of all the elements
	TArray<float*> Addresses;
	for (int32 i = 0; i < HISM_L->GetInstanceCount();++i) {
		float* Address = &HISM_L->PerInstanceSMCustomData[i * HISM_L->NumCustomDataFloats + CustomDataIndex];
		Addresses.Add(Address);
	}
	// Return the address
	return Addresses;
}

Shouldnt this return the addresses of the instances?

Though the following works, changing the float by pointer, thats not what i need. I need to get the pointers to all the units. So to work with them later:

void AHISM_Test::ChangeCustomValueByPointer(UHierarchicalInstancedStaticMeshComponent* HISM_L, int32 InstanceIndex, int32 CustomDataIndex) {
	// Get the address of the element
	float* Address = &HISM_L->PerInstanceSMCustomData[InstanceIndex * HISM_L->NumCustomDataFloats + CustomDataIndex];
	// Return the address
	*Address = 50;
}

Looking at the source of the function PerInstanceSMCustomData you can see it returns a TArray of floats.

A pointer is for when you need to reserve a specific amount of memory blocks for an instance of an object or array length.

There is no need you you to be using & before your HISM_L. The reference is useless if you are not planning on modifying it in-place.

You are not even passing it by reference in the function, so the variable is a local copy.

Try

TArray<float> AHISM_Test::GetAllInstancesAddresses(UHierarchicalInstancedStaticMeshComponent* HISM_L, int32 InstanceIndex, int32 CustomDataIndex) {
	// Get the addresses of all the elements
	TArray<float> Addresses;
	for (int32 i = 0; i < HISM_L->GetInstanceCount(); ++i) {
		float Address = HISM_L->PerInstanceSMCustomData[i * HISM_L->NumCustomDataFloats + CustomDataIndex];
		Addresses.Add(Address);
	}
	// Return the address
	return Addresses;
}

Or you could retrieve the address allocated for the whole TArray via

TArray<float>* Address = &HISM_L->PerInstanceSMCustomData;

Edit
The limit for TArray<float*> seems to be in enforced when you add blueprintCallable to the ufunctions. Seems it’s not exposeable to blueprints

2 Likes

Thanks for the reply.

This doesnt work. It still gives the same error:
error : Inappropriate '*' on variable of type 'float', cannot have an exposed pointer to this type.

And i dont know exactly what can i do with this:

TArray<float>* Address = &HISM_L->PerInstanceSMCustomData;

Perhaps this:

TArray<float>* Address = &HISM2->PerInstanceSMCustomData;
Address[0 * HISM2->NumCustomDataFloats + 5] = 122.f;

Though this is not working either. No operator “=” matches.
I dont understand why we can get pointers to the float address. But we cant return them from a function…

float* Address1 = &HISM2->PerInstanceSMCustomData[0 * HISM2->NumCustomDataFloats + 5];
	*Address1 = 50;

If this works^ . Then why cant we return an array of pointers to our floats from a function?
I need to store them in an array or map, so i can access them in different places.
If this is not possible what are my options to work with these values in a bunch of different functions?

There isn’t a variable in blueprints that could represent the float* that is why it’s not working. You could on the other hand just modify the data via passing in a reference and do the modifications in pure c++.

Explain maybe why you need the pointers to the specific data in your blueprints and there is another way to get around the problem?

2 Likes

I need to get all the instances of HISM components of different actors into 1 array, and control them that way. In my context controlling one HISM of each actor is causing issues. So the only solution is to have an array of all of them.

Is this an editor tool or something “In-Game”? Depending on the variant you will have different subsystems at your disposal.

1 Like

This is during the game. But im testing it in editor?

I also tried passing it by reference which seems what you were suggesting. But it gave me the same error:

	UFUNCTION(BlueprintCallable, Category = Combat)
		void GetAllInstancesAddresses(UHierarchicalInstancedStaticMeshComponent* HISM_L, int32 InstanceIndex, int32 CustomDataIndex, TArray<float*>& InstancesPtrs);

void AHISM_Test::GetAllInstancesAddresses(UHierarchicalInstancedStaticMeshComponent* HISM_L, int32 InstanceIndex, int32 CustomDataIndex, TArray<float*>& InstancesPtrs) {
	// Get the addresses of all the elements
	for (int32 i = 0; i < HISM_L->GetInstanceCount(); ++i) {
		float* Address = &HISM_L->PerInstanceSMCustomData[i * HISM_L->NumCustomDataFloats + CustomDataIndex];
		InstancesPtrs.Add(Address);
	}
	// Return the address
}

So this is also not an option.

I’m trying out a variant where the hism comp is passed by reference.

1 Like

Same with standard template library.

The TArray is guaranteed to be contiguous memory though, so it’s fine to treat it as a float* instead:

	float* myArrayAtCorrectPosition=&(HISM_L->PerInstanceSMCustomData[InstanceIndex*HISM_L->NumCustomDataFloats]);
	float float1=myArrayAtCorrectPosition[0];
	float float2=myArrayAtCorrectPosition[1];

So maybe it can be done without needing that method at all?

1 Like

That works to change the float right there. But not later. So im not being able to store the pointers anywhere.
Also you are using a reference to HISM_L, which means the function must pass a reference to the component, and that makes it drop in performance.

The only way to fix this is to get the addresses of all the instances and their floats stored in an array so to skip that.
And then change them on a tick directly in the addresses with just 1 loop.
Else, accessing each different HISM will cause a huge drop in performance. i tested this many times.
HISM is great. But only if you have 1 HISM type. The moment you need more HISM components, you will see its drop performance a lot by trying to animate in different hisms.
This will not be an issue if we get an array of pointers and change them directly in their address.
Which is something im capable of if necessary in assembly. But im trying to avoid that.

The base address of the PerInstanceSMCustomData TArray is not guaranteed to be consistent, storing pointers to them is probably not the best idea.

That’s a pointer to the HISM component, the float* is being set to the address of the TArray at that index. That should write/read directly into the TArray itself - it should be permanent.

I don’t think you could get it much faster than what I’ve put for setting the data - Visual Studio will optimize each of your references to those floats into a direct memory store/load - the ‘myArrayAtCorrectPosition’ variable will be optimized out.

Regarding the amount of HIMCs - maybe you could set the data on one set each tick rather than the lot at once?

1 Like

So you can basically use a

private:
TArray<float*> cachedTArrayAddresses;
and pass in the the hismcomp storing it’s

float* f = &HISM_L[j]->PerInstanceSMCustomData[i];

to cachedTArrayAddresses

Then you can mass update it via a function where you just pass in the new float and then itterate over cachedTArrayAddresses setting a value in the loop.

It will mass update

2 Likes

I wish that worked.

 error : Inappropriate '*' on variable of type 'float', cannot have an exposed pointer to this type.

Maybe there is no solution for this. I thought about going for assembly.

No you cannot expose it to blueprints. Delete UPROPERTY(EditAnywhere, BlueprintReadWrite)

it might compile with just UPROPERTY() but definitely not EditAnywhere or BlueprintReadWrite
Edit: no any type of uproperty exposes it to blueprints so it might get GC only way to keep it save would probably be a static var in a library

Made an example proj
massUpdate.zip (26.3 KB)

Might need some array size checking if the passed in index is greater than the array size, but too tired to check all of the edge cases today.

2 Likes

It worked. Thanks so much.
At some point i was about to quit. But finally your tips got me there.
There’s only one detail i was missing and thought it was not working.
Even though we are updating the Instance float directly by pointer it still needs the
HISM->MarkRenderStateDirty().
Just in case someone comes here with the same question.

3 Likes

Also if you are using a loop you only need to call MarkRenderStateDirty on the last instance in the HISM :slight_smile:

I tried a few more things with your solution. It turns out no matter what i do, it only works 1 time and only during BeginPlay().


That one works ^.


One piece moved by pointer ^ :slightly_smiling_face:

But the moment i try to do anything outside of the beginplay(), be it on the tick to animate it, or using a delay in the beginplay, then it wont work.

For example this one doesnt work. I dont know why.
I tried on a different project too, and debugged it and the address seems correct, but it doesnt update it, outside the beginplay.

It could be because like someone said the pointers to these floats are something temporary.

Or is there something im missing? Its just weird that it works on one place, but it doesnt work anywhere else.

I send you my project. It has a small sketch level with an actor that has HISM for test, its version 4.27.2 :

Could you please take a look at it?

Seems the link is sending me to the homepage of https://wetransfer.com. Is it restricted to logged in users?
If you are modifying custom data then it may be only for the zero index.

1 Like

I think wetransfer is public. Its not necessary to log in.
But i can send you just the source that is 4 kb and fits here:

Source.rar (4.2 KB)

But this doesnt have the material with 3 floats and the actor blueprint:
imagem

So the hism needs the 3 floats:

And the material is like this:

Now that i look at the material. That transform vector might not be necessary.