Is there a C++ CartographicPolygon tutorial?

In my project, I need to be able to dynamically remove 1 to many areas of a cesium tile set.
I need to be able to do this at runtime in C++.

Are there any tutorials on this?

I have a few questions that hopefully can get me going…

  • Do I only ever need 1 UCesiumPolygonRasterOverlay attached to the Cesium3DTileSet even if I need to have multiple holes?

  • If I add the UCesiumPolygonRasterOverlay to the Cesium3DTileSet in the editor as shown in the BP tutorial, how do I get access to it at runtime in C++? I don’t see any kind of function on the tile set that returns any kind of reference to the raster overlay.

  • As far as the algorithm for creating the holes themselves, does this seem complete?

for each hole that I need to create

  • Create a new ACesiumCartographicPolygon
  • Create a USplineComponent with the 4 FVector locations that I have already created.
  • Assign this new spline to the ACesiumCartographicPolygon->Polygon variable
  • Add this ACesiumCartographicPolygon to a TArray<ACesiumCartographicPolygon*>

When I’m done creating all the polygons and populated the TArray, I then assign this to the UCesiumPolygonRasterOverlay->Polygons array.

  • Do I need to mess with the Z axis at all when creating spline points? At runtime each FVector will have been populated with Cesium lat/lng/alt data to sit right on the surface of the terrain. I’m assuming I would want to modify the Z value a bit? Several hundred feet in the positive Z axis??? Or will adding this to the tileset just override the Z axis for me???

Is that pretty much it? Are there any steps that I’m missing? And if so, at runtime, will my hole(s) show up in the cesium terrain?

All here is a better explanation of what I’m trying to do and some of the issues that I’m facing…

I have a situation where I have two locations in a cesium map. After converting a longitude, latitude, altitude into a Cesium Vector I will end up with two locations that have this type of relationship.

What I really need, is to be able to rotate these points so they are pointing in the same direction like this.

Because, what I’m trying to do is hide terrain/punch a hole into cesium.

What I was hoping that I could do was align the directions of the vectors.
Then for each corner do basic math. Cut the width in half… Say my width is 100 meters.
Starting at the lower left corner and going clockwise.

Width = 100 (in meters)
HalfWidth = 100 / 2

Point1 = Vec1
Point1.Y = Vec1 + HalfWidth

Point2 = Vec1
Point2.Y = Vec1 - HalfWidth

Point3 = Vec2
Point3.Y = Vec2 - HalfWidth

Point4 = Vec2
Point3.Y = Vec2 + HalfWidth

Then use the 4 points to create by spline polygon and assign that polygon to the Cesium Cartographic Polygon object.

So, I am assuming that I can not apply typical Unreal vector manipulation to this, and if not, how do I go about creating a proper set of 4 points?

Any help will be greatly appreciated! I’m getting swallowed up by quicksand here.

Hello, I think what you want is to add to Point1 and Point2 not the HalfWidth but the (Left/Right direction) * HalfWidth.

You can find the direction using the cross product.

So you need two vectors, the UpDown vector, the normal vector, in order to find the LeftRight vector.

You can find the Normal vector by using the ComputeGeodeticSurfaceNormal function.

The UpDown vector is simply Vec2-Vec1.

Then LeftRight = UpDown X Normal.

Then you can get Point 1 by:

Point1 = Vec1
Point1.Y = Vec1 + UpDown * HalfWidth

I think that will work. I hope this helps!

Hi Joseph…

Actually I got all the debug balls where I want them now… With one exception, Rather than a nice rectangle, I end up with a trapezoid because my original vectors are not rotated properly at the very start of all my point manipulation.

I’ve tried a few different ways to align the start point xyz and the end point xyz, but I always end up with some wonkie locations.

So I need to figure out how to rotate the original two vectors to be aligned preferably down the X axis?? Actually not sure if it matters, either X or Y and then I can adjust how I’m building my debug spheres showing me where my rectangle is.

Also…

And this is a big issue for me, I have created my polygon (USplineComponent) for cutting the hole into the terrain…

In the editor, I have my Cesium Terrain. I gave it a CesiumPolygonRasterOverlay, but did not assign the polygon because I need to do that at runtime…

At runtime
I have my 4 point polygon.
I create a new CesiumCartographicPolygon object.
I assign it’s Polygon object to my newly runtime created polygon
I get a reference to the CesiumPolygonRasterOverlay
I assign the CesiumPolygonRasterOverlay->Polygons.Add( CesiumCartographicPolygon ) object.

I don’t get any errors, but I don’t get my hole punched out either.
Once I’ve assigned the polygons is there a function that I need to call to get it to basically activate???
Does the polygon have to be a certain height above the surface?

Have you tried RefreshTileset?

I tried Refresh but got an exception thrown. Just tried RefreshTileset and got the same exception

At a glance, that looks like you’re calling RefreshTileset on a null pointer, or something like that. Please share the complete call stack and the actual code you’ve written if you want help debugging this.

Hi Kevin…
Thanks for chiming in!

Here is my Cesium world terrain object and the Raster Overly added
image

This is the code where I am adding the vectors to a polygon that gets assigned to a ACesium CartographicPolygon

Here is where I’m at runtime creating an instance of a ACesiumCartographicPolygon, assigning the polygon I created to it. Getting the CesiumOverlay. If you look, I’m checking that each pointer is valid before I use the object. At the very top of the function, I’m checking the Cesium TileSet.

ACesiumUtility is a C++ utility class that has references to all the cesium objects that get placed in the scene via the Cesium editor window. Plus this class has a couple other helper functions that help my business logic. This function here, being one of them.
I have stepped through this function and it’s crashing after this function is done executing. In fact the use case for this particular execution run, PunchPolygonHole gets called twice and both times, the function succeeds. So, it would appear that whatever is failing, is failing after this function ends. Also, if I comment out CesiumTileSet->RefreshTileset(), the app does not throw any exceptions and the scene loads fine.

Probably not overly helpful, but here are is a stack trace and output window message.


You need to use SpawnActor to create Actors and add them to the world. A CesiumCartographicPolygon created with NewObject won’t be able to find its corresponding CesiumGeoreference.

Ah… Ok, I will change my code to do that… Thanks you for the tip… Can’t do it right now got company over, but will make the change as soon as I can.

Thanks Kevin!!!

Kevin, you are my hero!
I changed the code to this, and now it works!

FVector loc = CesiumGeoreference->GetActorLocation();
FRotator rot = CesiumGeoreference->GetActorRotation();
FActorSpawnParameters params;
ACesiumCartographicPolygon* runwayPoly = GetWorld()->SpawnActor<ACesiumCartographicPolygon>(loc, rot, params);

Good to hear it worked for you!

@RickB - I’m trying to do something similar - dynamically creating polygons and adding them to a raster overlay of a tile set. The difference is that I want each added polygon to be a different color. I’m trying to achieve that using a dynamic material instance - and this is the point at which I’m failing.

I’m trying to do it all in Blueprints (and it’s not working).

I’ve posted here: How to add colored polygons at runtime?

I may need to switch to C++. Is there any chance you could share your code as it will give me a great head start? (Of course, I fully understand if you can’t - but, hey, it doesn’t hurt to ask :slight_smile: )

Also do you know how to set the layer name of a Material Instance and/or to set parameters on a Material Layer Blend - at runtime.

Thanks.

@jdh2550

Hey there… Sure, but give me a few minutes… I’m about to go into a meeting… I may not be able to get back to you until a bit later this afternoon but I have no problem sharing what I’m doing… The code itself is really generic enough that I wouldn’t be giving away anything secret…

BUT… I still have an issue with vector rotation, which is why I came back to this thread, I need to nail down why my vectors are rotated the way they are, and figure out why normal vector rotation is not working…

I think if you want to set parameters, you need to create a material instance from a material. It’s been a while since I’ve played with materials, and to be honest, I don’t recall playing with them in C++ at runtime.

I’d go hit up the Unreal forum and look in their materials section on their board.

Here is where I am creating the polygon.
One thing to keep in mind is that I am still having a problem with my vector rotation.
I need vecLoc1 and vecLoc2 to be in line with one another… But other than that, this will take two vector location, a width and create a 4 point rectangle polygon. Right now I have debug code in place to draw spheres and lines between the points.

I JUST NOTICED ONE ISSUE WITH THIS
IF, at runtime, you change your CesiumGeoReference to a completely different geo location at runtime, those cuts in the terrain follow you. So, I need to figure out how to 100% refresh the cesium terrain. And that I am not sure about… Maybe it’s as simple as removing the ACesiumCartographicPolygon from the UCesiumPolygonRasterOverlay then calling Refresh on the tileset??? Not sure yet, will have to try it when I get a chance, but I can’t right this second, off to another meeting. :slight_smile:

bool ACesiumUtility::Generate4PtPolygon(FVector* vecLoc1, FVector* vecLoc2, USplineComponent* polygon, float widthMeters)
{
	bool bRetVal = false;

	// Ensure all objects are valid
	if (!vecLoc1 || !vecLoc2 || !polygon)
	{
		return bRetVal;
	}

	FVector run1Vec = *vecLoc1;
	FVector run2Vec = *vecLoc2;

	float halfWidth = (widthMeters / 2) * 100;

	FVector pt1 = FVector(run1Vec);
	FVector pt2 = FVector(run1Vec);
	FVector pt3 = FVector(run2Vec);
	FVector pt4 = FVector(run2Vec);

	// Bottom left
	pt1.X = run1Vec.X - halfWidth;
	// bottom right
	pt2.X = run1Vec.X + halfWidth;
	// top left
	pt3.X = run2Vec.X + halfWidth;
	// top right
	pt4.X = run2Vec.X - halfWidth;

	if (GetWorld())
	{
		// For debugging purposes only
		DrawDebugSphere(GetWorld(), pt1, 200.f, 12, FColor::Magenta, true);
		DrawDebugSphere(GetWorld(), pt2, 200.f, 12, FColor::Yellow, true);
		DrawDebugSphere(GetWorld(), pt3, 200.f, 12, FColor::Green, true);
		DrawDebugSphere(GetWorld(), pt4, 200.f, 12, FColor::Blue, true);

		DrawDebugLine(GetWorld(), pt1, pt2, FColor::Magenta, true);
		DrawDebugLine(GetWorld(), pt2, pt3, FColor::Yellow, true);
		DrawDebugLine(GetWorld(), pt3, pt4, FColor::Green, true);
		DrawDebugLine(GetWorld(), pt4, pt1, FColor::Blue, true);

	}
	polygon->ClearSplinePoints();

	polygon->AddSplinePoint(pt1, ESplineCoordinateSpace::Local);
	polygon->AddSplinePoint(pt2, ESplineCoordinateSpace::Local);
	polygon->AddSplinePoint(pt3, ESplineCoordinateSpace::Local);
	polygon->AddSplinePoint(pt4, ESplineCoordinateSpace::Local);
	polygon->SetClosedLoop(true);
	UE_LOG(LogTemp, Warning, TEXT("POLYGON SPLINE POINT COUNT = %d"), polygon->GetNumberOfSplinePoints());

	return polygon->GetNumberOfSplinePoints() == 4;
}

here is where I am actually punching a hole into the cesium terrain. It uses the polygon created in the function above.

// Accepts a 4 point polygon and punches a hole in the cesium terrain at that location.
bool ACesiumUtility::PunchPolygonHole( USplineComponent* polygon )
{
	bool bRetVal = false;
	if (!CesiumTileSet || !GetWorld() )
	{
		return bRetVal;
	}

	FVector loc = CesiumGeoreference->GetActorLocation();
	FRotator rot = CesiumGeoreference->GetActorRotation();
	FActorSpawnParameters params;
	ACesiumCartographicPolygon* rectPoly = GetWorld()->SpawnActor<ACesiumCartographicPolygon>(loc, rot, params);

	if( rectPoly )
	{
		rectPoly->Polygon = polygon;

		UCesiumPolygonRasterOverlay* runwayRasterOverlay = CesiumTileSet->GetComponentByClass<UCesiumPolygonRasterOverlay>();
		if (runwayRasterOverlay)
		{
			runwayRasterOverlay->Polygons.Add(rectPoly);
			CesiumTileSet->RefreshTileset();
			bRetVal = true;
		}
	}
	return bRetVal;
}

Many thanks - I appreciate it!

1 Like

@Kevin_Ring
I’m hoping you can help me out here… It deals with the vector rotation issue from above where I am showing how I am trying to take two points, rotate them so they are in the same orientation so I can build my polygon.

The two points, Vec1 and Vec2, have been generated via this code here.

FVector lnglatalt = FVector( longitude, latitude, altitude );
FVector retVector = CesiumGeoreference->TransformLongitudeLatitudeHeightToUnreal(lnglatalt);

I use them so build out a spline and the polygon as mentioned… However, their rotations are off.
I need it so that the on Vec1 and Vec2 X axis is pointing in the forward direction as indicated by the purple arrow. However, as it is now, it’s pointing off in a different direction as you can see with the gizmo and the yellow/magenta debug sphere and magenta line between the two…

If I take this code right here inside a normal UE level, place two Arrow static meshes in the scene and try to manipulate them, it works exactly as I want. It will align the two arrows to be pointing in the direction, however, why I try to apply this same logic in my UE cesium app, I get some pretty wonkie results.

// Rotate arrows in the same direction ----->   ----->
void AArrowController::RotateArrowsSameDirection(AArrow* A1, AArrow* A2)
{
	FVector A1Loc = A1->GetActorLocation();
	FVector A2Loc = A2->GetActorLocation();

	FRotator A1Rot = (A1Loc - A2Loc).Rotation();
	FRotator A2Rot = (A2Loc - A1Loc).Rotation();

	A1->SetActorRotation(A2Rot);
	A2->SetActorRotation(A2Rot);

Design time view

Runtime view after running the RotateArrowsSameDirection function.

So, how do I manipulate the cesium4Unreal vectors to alight properly?

I would really appreciate any help!!!

Thanks

FVector::Rotation effectively computes yaw and pitch, while assuming roll is zero. That’s sensible in a flat, Z-up world, but not in a round one. You need a better way to compute the direction you need. Probably with some cross products involving the ellipsoid surface normal. As a starting point, “ellipsoid surface normal” cross “A1Loc - A2Loc” (which is the direction of one of the diagonals of your rectangle) should give you the direction of the other diagonal in your rectangle.

Thanks Kevin…

I really appreciate the input, I’ll have to do some digging. My Vector math is not as stellar as I wish it was. I went thought some tutorials on the subject up on Khan Academy about a year ago, but it’s clearly one of those… You use it or you loose it type of subjects for me.