Download

[Question] Convert a static mesh vertex coordinate to texture UV coordinate?

Dear community,

I have a static mesh with a texture applied. If I generate a collision/overlap event on the static mesh, I can get the vertex closest to the overlap point using code from Rama:
https://wiki.unrealengine.com/Mesh_Collision,_Obtain_Closest_Point_on_Mesh_Surface_To_Other_Point

However, I now want to determine what is the corresponding UV coordinate on the texture. Does anyone know of a good way to achieve this? Is this even the right approach? Or is there a more direct way of knowing where in the texture the overlap event happened?

Thanks!

Here’s a video demonstrating what I’m trying to accomplish:

The waves are created by a dynamic texture. I’m redrawing the texture every frame. So far, this is all on cpu and suprisingly, it does not drop my frame rate. The raindrops are random and are being generated by choosing random uv coordinates every 1 second.

Now I want the uv coordinates that correspond to where the character overlaps the mesh so I can generate waves from that point.

Actually, maybe a better phrasing would be: I want the pixel coordinate of the texture where the overlap happens. In the video, my texture is 128x128, but the dimensions are configurable by blueprints.

That function isn’t returning all the vertex data (just the position and distance) so you’d need to go into the source and modify that function. You’d also have to find all three vertices in the triangle your character is currently in, and then interpolate between them to calculate the UV position. That seems like it might be fiddly to get working, but in theory you should be able to get all that data you need.

I might try doing a custom UV projection. Do a custom UV node in the material that creates a planar projection based on the vertex world position; just define a UV of (0,0) at one position and a UV of (1,1) at somewhere else and interpolate between those. Then to calculate the corresponding UV position for your character you can figure it out from his world position. Of course, you’d have to define your UV volume and then move your water geometry into it.

Or, since I assume your plane mesh already has a planar UV projection on it anyway, you could just find its bounding box and calculate the UVs of the character position based on that. Of course, that won’t work if you have a really complicated UV projection–like rippling along the surface of a character–but 99% of the time your water is just going to be flat on the ground. Any kind of regular UV mapping should be fine (even spherical or something).

Thanks for this suggestion! Simple yet perfect. Here’s what it looks like now:

For anyone interested, here’s how I got it working:
.h


FVector meshOrigin, meshExtent;

UFUNCTION(BlueprintCallable, Category = "Oasis")
void setGridDimensions(int32 sizeX, int32 sizeY);

UFUNCTION(BlueprintCallable, Category = "Oasis")
TArray<float> DistanceOfActorToThisMeshSurface(TArray<AActor*> TargetActor, TArray<FVector> &ClosestSurfacePoint) const;

UFUNCTION(BlueprintCallable, Category = "Oasis")
void WS2Texture(float InX, float InY, float &outX, float &outY);

.cpp


void AOasisInteractiveWater::setGridDimensions(int32 sizeX, int32 sizeY)
{
	SizeX = sizeX;
	SizeY = sizeY;
	setOasisTexture();
}
void AOasisInteractiveWater::WS2Texture(float InXWS, float InYWS, float &outXTS, float &outYTS)
{
	//convert coordinate system to lower left corner as origin
	float OriginX = meshOrigin.Component(0) - meshExtent.Component(0);
	float OriginY = meshOrigin.Component(1) - meshExtent.Component(1);

	//figure out relative location of the hit as a ratio for x and y
	float hitX = (InXWS - OriginX) / (2 * meshExtent.Component(0));
	float hitY = (InYWS - OriginY) / (2 * meshExtent.Component(1));

	//use the ratio to convert the hit to texture coordinates
	outXTS = hitX * SizeX;
	outYTS = hitY * SizeY;
}
void AOasisInteractiveWater::PostInitializeComponents()
{
	Super::PostInitializeComponents();
	this->GetActorBounds(false, meshOrigin, meshExtent);
}
TArray<float> AOasisInteractiveWater::DistanceOfActorToThisMeshSurface(TArray<AActor*> TargetActors, TArray<FVector> &ClosestSurfacePoints) const
{
	//Dist of Actor to Surface, retrieve closest Surface Point to Actor
	ClosestSurfacePoints.Empty();
	TArray<float> Distances;
	for (auto it(TargetActors.CreateIterator()); it; ++it)
	{
		if (!(*it)->IsValidLowLevel()) continue;
		ClosestSurfacePoints.Add(FVector(0.0f, 0.0f, 0.0f));
		Distances.Add(SurfaceMesh->GetDistanceToCollision((*it)->GetActorLocation(), ClosestSurfacePoints.Last()));
	}
	return Distances;
}

Here’s how the functions get hooked up in Blueprints:

https://dl.dropboxusercontent.com/u/33133319/Oasis2.jpg

That looks really pretty!

Thanks! Just wait-- I plan on adding ability for the class to store a second height map to represent ground height which will allow the formation of puddles on the ground that have interactive rippling water. Then it will have a parameter to control how wet the surface is so you can evaporate the water. After that, I’ll add ability to introduce floating/sinking debris.

Once I get that working, I guess it’ll be time to learn how to write usf files to optimize the implementation.