Visual Scripting – Sorting things out

Before I am able to place vegetation on the terrain I need to know the slope angle at each point so I can determine whether it is too steep or what kind of plants i want to appear in those circumstances. I could get the tangent/normal data from the FRawMesh from last week however i that would require removing duplicates again which would be both slow and I could become a pain if they don’t match up after the sorting process. I decided to go the easier route and calculate them by sampling the adjacent points.

The first issue is that blueprints only support 1D arrays but i need to address elements with 2D Coordinates, To help with this i wrote a macro that uses the formula i = y * width + x which will do just that.

Sorting_2DindexingSorting_Indexing0

To test that it worked I set the cube at that index to a red material, sadly I do not have a screenshot of this but there were red cubes.

Sorting_ColouringTest

In the testing, the cubes at (0, 0) and (width, height) were in the correct positions in the array; however, due to the way I was removing the duplicate vertices, they were not all sorted causing some rows to be jumbled with the next. I changed the debug cube to also have a Text Renderer that I show the position in the array on.

Sorting_UnsortedDefaultOrder.PNG

My original instinct was to implement my own sorting algorithm in blueprint or C++, in theory i would be able to use the same indexing formula as my comparison for checking, however this would be very time consuming. Instead i found that TArray already has the function TArray::Sort() so I wrote a C++ function that would use it and return the sorted array.
Sorting_UsingSortFunction

TArray UMyFunctionLibrary::Sort(TArray points)
{
    // use TArray::Sort using my predicate to sort the array
    points.Sort(SortPredicate);
    return points;
}

// Function that TArray::Sort can use to compare FVectors
bool UMyFunctionLibrary::SortPredicate(const FVector & a, const FVector & b)
{
    if (a.Y < b.Y)
    {
        // if a's y is less we know it has to be before b
        return true;
    }
    else if (a.Y == b.Y && a.X < b.X)
    {
        // if they're at equal y, if a's x is less than b's x, true
        return true;
    }
    else
    {
        // else b is before a
        return false;
    }
}

It’s worth noting that for simplicity’s sake I used multiple if statements in my predicate function and not the more elegant formula. It is possible that using the formula could be faster but it was quicker to write and is more easily read when using this solution.

Sorting_sorted

Here you can see the successfully sorted array, ready for me to start calculating the slope data.

Optimising The Debug Cubes

As I mentioned in the last post, Spawning cubes using the Vertex list was very slow at the higher detail levels due to there being multiple vertices overlapping, such is the nature of meshes. I Will likely not be using the raw vertices for very long however as a small exercise I wrote a small function to remove duplicate vectors from the array, and the results were very successful.

The function runs through all of the vectors in the input array, Checks whether the new array already contains it, if not it gets added to the new list. The new list is then returned.

VertListOptimisation

I also tidied up the level blueprint when adding in the new function by using a sequence, this serves no purpose other than to make the code look nicer and easier to read. I also added a “Print Text” node to write the number of vectors in the list to the screen.

OptimisedCubeSpawning

Firstly I ran the level with the optimisation node disconnected so that I could see both how long it took to run and note down the number of vectors, Then I reconnected the node and ran again, taking note of the number of vectors.

The results were really impressive, Without the optimisation it took ages to start the level and it was incredibly laggy, this was down to the 55,296 cubes that were spawned in, many of which were overlapping each other. With the optimisation it started almost immediately with only a little frame lag and an impressive 9,409 cubes spawned. I will also make it clear that there were the same number of cubes visible so I had only removed duplicate cubes.

Unreal 4 Custom Terrain Node

A Brief Introduction

This week I started work on creating the custom nodes I need in Unreal Engine 4 to create my procedural vegetation tool.
UE4’s documentation is well none for being lacking however when it comes to creating custom blueprint nodes it is exceptionally more difficult to find useful information on the subject and I spent many hours banging my head against a brick wall getting nowhere.

My goal for this week was somehow getting access to the data contained within the Landscape class, as this is not possible to do with blueprints I had to look into custom node creation, which in Unreal engine is called a Blueprint Function Library.

Writing a custom node in C++

You can create a Blueprint Function Library by right clicking in the content browser, selecting “New C++ Class…” and selecting “Blueprint Function Library”.

When Writing a custom node you must put UFUNCTION() before your function, in this you can define how the blueprint editor will see your node, name it and define various keywords for searching. Blueprint functions must all be public and static.

Pins

What I have gathered so far is that pins are assigned as the parameters in the function signature; Pointers and Variables are inputs, References and the return type are outputs (if not void). I want to take in the Landscape object, an int to use as the level of detail for exporting the mesh and to return an array of vectors for the vertices, the function parameters are as follows:
ALandscape * landscape, int sampleLod, TArray &amp; points

The documentation shows that the parent class of a landscape actor(ALandscape) is ALandscapeProxy which has the functionality to export the mesh data to an FRawMesh struct so I added an include for “Developer/RawMesh/Public/RawMesh.h”. The RawMesh struct is not available in Blueprints so I use the points referance, I set it to the vertex array in the raw mesh.

Problems

An important point that is missing from the documentation is that you have to manually add modules to the .build.cs file for the linker to actually link the various libraries that the RawMesh and Landscape classes are in. This is done by adding the module names to this line in the build file:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "RawMesh", "Landscape" });
I assume that the module names are the same as the header files that you include like they were in my case however I do not know as the documentation surrounding the build system is very complicated.

Finished Code

Here’s my finished code:

// in Header
UFUNCTION(BlueprintPure, DisplayName = "Get Landscape Stuff", Category = "Landscape Helpers")
static void GetMesh(ALandscape * landscape, int sampleLod, TArray & points);

// in Cpp
void UMyFunctionLibrary::GetMesh(ALandscape * landscape, int sampleLod, TArray & points)
{
// Raw mesh structure we'll export to
FRawMesh rawMesh;
// Export the landscape to the Raw Mesh using the supplied LOD
// Higher number means lower detail, less points (faster to demo with cubes)
landscape->ExportToRawMesh(sampleLod, rawMesh);
// Finally put the vertex positions in the raw mesh to the points array referance
points = rawMesh.VertexPositions;
}

It is important to note that the Editor must be restarted after compiling a custom node for it to show up in the blueprint editor, this can be time consuming and the docs do not mention it but if you are unable to fund your nodes or they are not showing the right pins, try restarting the editor.

Here’s my node in the blueprint editor:
Node

To check that I was getting the correct data I spawned a cube at each point in the array, This is very slow when using the lower LOD value, this is because the vertices are per face, in theory meaning that all vertices are duplicated 8 times (not including on the edges of the terrain, however here’s the node setup:

terrainLevelBP.PNG

This gave the following render, a successful result.

CubesOnLandscape