Sample spline on interior dispare behaviours

using unreal 5.5 the sample spline is giving me diferent behaviours on diferent splines.


in the image, there are 3 splines defined by 4 points each, linear, and marked as closed loop.
they are clamped to the grid with cells size 500X500.


this is the setup on the sampler.
I’ve hardcoded the interior spline spacing to 500 to match the cell size.

the behaviour I’m expecting is what spline 1 is showing, a point at the center of each cell.
but then later spline 2, looks like the points are offseted half the cell size on Y axis.

and spline 3 the points start generating on the spline.

any reason for this diferent behaviours?, I’m I understanding wrong the workings of the spline sampler on interior.

and how woild you go by to fill the interior of the spline with a tiled mesh to act as a floor? take into acount the sistem should suport non rectangular splines like

Wondering what this is as I’ve not seen it in the engine before? Is it in there somewhere? A plugin? In return I’ll bump this post up the list :slight_smile: .

If you’d have to implement it yourself, that can be pretty complex. Assuming we’d convert the spline points to vertices there (just point position vectors), you could use the Procedural Mesh Component to build a vertice based mesh out of that. The tris can be calculated with Delaunay

Math in Delaunay triangulation algorithm, too many triangles appearing - #4 by Roy_Wierer.Seda145

Although I am not sure how that handles the corner part. Ages ago I was researching something in procedural interior generation. I might at some point have used a ray trace technique or “floor area” position tests.. or floodfill test to see what areas should actually be floor or “nothing” while building things procedurally. Based on complexity (like a rocket at times on architecture) try to keep things as simple as possible. Or use what you have.

Snippets:

Getting a spline point position, what is going to be the vertex:

MySplineComponent->GetWorldLocationAtSplinePoint(PointIndex);

Or over “time”. USplineComponent has a duration property from 0 to X. This is “duration” reflects somewhere between its start and end point, by default 0 is start and 1 is the end. So you could create vertices along its entire path and specify the detail level at once:

MySplineComponent->GetWorldLocationAtTime(InFloatWithinDuration);

See my Delaunay code linked for this. This generates triangle indexes for point positions.

FDelaunay Delaunay = FDelaunay();
const TArray<FIntVector> Triangles = Delaunay.Triangulate2D(Points);

This generates a mesh. Use the delaunay data.

AMyActor::AMyActor() {
	ProceduralMeshComponent = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("ProceduralMeshComponent"), true);
	ProceduralMeshComponent->SetupAttachment(RootComponent);
}


AMyActor::DoStuff() {
	ProceduralMeshComponent->ClearAllMeshSections();
        // Use the delaunay data, can probably copy paste it here on the arrays.
	// TArray<FVector> MyVerts;
	// TArray<int32> MyIndices;
	ProceduralMeshComponent->CreateMeshSection_LinearColor(0, MyVerts, MyIndices, TArray<FVector>(), TArray<FVector2D>(), TArray<FLinearColor>(), TArray<FProcMeshTangent>(), true);
}

If you are sure tiles of a fixed size are all you need, you could also go with a floodfill and spawn static mesh (pre modeled plane of fixed size). You will probably get the best performance if using the instanced static mesh actor to spawn them.

AMyActor::AMyActor() {
	RootComponent->SetMobility(EComponentMobility::Type::Static);
	InstancedStaticMeshComponent = CreateDefaultSubobject<UInstancedStaticMeshComponent>(TEXT("InstancedStaticMeshComponent"), true);
	InstancedStaticMeshComponent->SetupAttachment(RootComponent);

	// Other optimizations
	InstancedStaticMeshComponent->SetCastShadow(false);
#if WITH_EDITORONLY_DATA
	UBlueprint* Blueprint = Cast<UBlueprint>(GetClass()->ClassGeneratedBy);
	if (IsValid(Blueprint)) {
		Blueprint->bRunConstructionScriptOnDrag = false;
	}
#endif // WITH_EDITORONLY_DATA

	//InstancedStaticMeshComponent->SetCollisionEnabled(ECollisionEnabled::Type::NoCollision);
	InstancedStaticMeshComponent->SetCanEverAffectNavigation(false);

	PrimaryActorTick.bCanEverTick = false;
	PrimaryActorTick.bStartWithTickEnabled = false;
}

void AMyActor::DoStuff() {
	// Clean
		InstancedStaticMeshComponent->SetStaticMesh(nullptr);
		InstancedStaticMeshComponent->ClearInstances();

  // build
	InstancedStaticMeshComponent->SetStaticMesh(TileMesh);

// optimize
	InstancedStaticMeshComponent->SetCullDistances(StartCullDistance, EndCullDistance);
}

A floodfill test is simple to program, there are many variants as well (find them and the algo on wikipedia), but optimally you set up some utilities to work with it like I’ve done. The grid argument on this method provides spaces that are accessable (and those that are not), basically your floor design surface fit within a square grid of 0 and 1 cells.

TArray<FS_FloodFillArea> UFloodFillUtils::GetAllAreasXY_FourWay(const UGrid* InGrid) {
	TArray<FIntPoint> Flooded;
	TArray<FS_FloodFillArea> Areas;

	if (!IsValid(InGrid)) {
		UE_LOG(LogAlgorithmsPlugin, Error, TEXT("Invalid InGrid."));
		return Areas;
	}

	for(int x = 0; x < InGrid->GetSize().X; x++) {
		for(int y = 0; y < InGrid->GetSize().Y; y++) {
			/* 
			If cell at(x, y) is a passage, and it has not already been flooded (i.e.is not in a passage that's already been found)
			, Then find the passage it's connected to.
			*/
			if (InGrid->HasPassage(FIntVector(x, y, 0)) && !Flooded.Contains(FIntPoint(x, y))) {
				FS_FloodFillArea Area;
				Area.Container = GetAreaAtXY_FourWay(InGrid, FIntPoint(x, y));
				// If there is a passage at (x, y), then update flooded list and add to passages list.
				if(Area.Container.Num() > 0) {
					Flooded += Area.Container;
					Areas.Add(Area);
				}
			}
		}
	}
	return Areas;
}

I hope that those examples gave some insight in the possibilities, but of course there are plenty of ways to do things. I can’t tell you what behavior your spline tool is supposed to have (I do not recognize it), but perhaps more important is to know what results you want regardless of tools used. And I expect this is something architecture like, who should be able to edit it (some techniques run great as scripts or tools, but people not used to them can’t use them.)

Hi.

not sure what you mean, since the spline sampler node comes with the basic pcg plugin

THe only code part relevant to this, is when I create the actual spline based on the FBox that defines the basic shape of the room , and an array of FBox that defines the overlapping sections with other rooms(negative spaces), with this I create a corsssection of the room and create the spline points.

I then use this spline to create the walls, and wanted to use the same spline to create the floor.
previously I used to create points on a grid for the base fbox shape, and a grid for each of the negative spaces. then just remove from the first grid what was on the negative spaces grids. but feels like passing redundant data.

ah thanks for letting me know. No I didn’t see it before :slight_smile: seems it only went from experimental into beta in 5.4. So I can’t tell what exactly that plugin offers out of the box for floor generation.