Random Placement of Actors on a Non-Convex Polygonal Area


I am currently beginning my learning process in Unreal Engine to employ it for a research project of mine.

This project requires me to place actors on an area which is planar, but not necessarily convex. This area is given through a specific face of the mesh of an already existent actor.

To solve this problem, I will need to know how to pass the information on what face should be used to the function and I will need a way to sample a point on that face randomly with equal chance for all possible points.

It is fine for now if the face needs to be specified by the user before execution of the script, though I am not familiar with how I would work with such a low level element of a mesh in Unreal.

The question on how to sample a point from such an area is already stumping me. Performance is somewhat important, as that function may be called thousands of times and this needs to happen in the timeframe of at most a few seconds. As the shape can be non-convex, no simple rectilinear formulas will work, but are all that I am familiar with.

You could triangulate the polygon into triangles and sample points inside those. You can also weight them by their area for an uniform distrubution. See this article for how to sample points in a triangle uniformly.

The good news is that if you’re working with a static mesh, the engine will triangulate the mesh for you anyway, so you just have to look at the index and vertex position buffers on your (I’m assuming) static mesh. You can get to them like so

const FStaticMeshLODResources& LODResources{ StaticMesh->GetRenderData()->LODResources[ LODIndex ] }; 
// Where StaticMesh is your static mesh and LODIndex is the index of the LOD whose buffers you want. 
// If performance is important, you may want to use LODs with lower triangle counts here.

// The positions vertex buffer.
const FPositionVertexBuffer& PositionVertexBuffer{ LODResources.VertexBuffers.PositionVertexBuffer };

// The index buffer.
const FRawStaticIndexBuffer& IndexBuffer{ LODResources.IndexBuffer };

// Store the triangles in an array.
TArray<TTuple<FVector, FVector, FVector>> Triangles{};
for ( int32 Index{ 0 }; Index < IndexBuffer.GetNumIndices(); Index += 3 )
	const uint32 IndexA{ IndexBuffer.GetIndex( Index ) };
	const uint32 IndexB{ IndexBuffer.GetIndex( Index + 1 ) };
	const uint32 IndexC{ IndexBuffer.GetIndex( Index + 2 ) };

	const FVector PositionA{ PositionVertexBuffer.VertexPosition( IndexA ) };
	const FVector PositionB{ PositionVertexBuffer.VertexPosition( IndexB ) };
	const FVector PositionC{ PositionVertexBuffer.VertexPosition( IndexC ) };

	Triangles.Add( TTuple<FVector, FVector, FVector>( PositionA, PositionB, PositionC ) );

// Now you have an array of triangles.

// Note: There is no need to normalize the data in such a way. This is just an example.

// Note: CPU access must be enabled on the mesh resources for this to work.

@Owlree That solves my problem almost completely. The only thing I am lacking is that I still need to be able to select an entire face (or, as you have said, set of faces that together make up what appears as a face to the user, as Unreal triangulates the meshes) to work on. Using your code I an easily get all the tris on a mesh, but I want to only sample from the ones that are coplanar to the tri that the user happened to click on and contiguous with it too.

Can I figure out where exactly the hit of a raycast happened? I could then maybe find a tri that is closest to that point in some way and build my full area from there.

You could do a trace against the complex collision, which essentially trace against the triangles of the mesh unless you changed the settings of the mesh in the static mesh editor. You can checkout the documentation for the various traces available in UE on this page (just search for LineTrace on the page). The important bit is when you create the query parameters structure, you must enabled the complex collision like so

FCollisionQueryParams CollisionQueryParams{};
CollisionQueryParams.bTraceComplex = true;

The line trace query will give you back one (or more, or zero) FHitResult objects, which contains a property called FaceIndex. Pretty sure this should match the index of the triangle in you mesh.

You may also want to checkout Niagara. Not sure about the details of you endeavour but it does sound like it could come in handy if you want to spawn lots of particles on that mesh. This video is a good starting point and may be useful for your use case.