6th June 2015

Big Data VR Challenge - Part 1 - Epic & The Wellcome Trust

Game view with 10,000 cubes

I’m participating in the Big Data VR challenge organised by Epic Games and The Wellcome Trust. I’ve teamed up with Masters of Pie to form team LumaPie and we’re paired with researchers from ALSPAC, the Avon Longitudinal Study of Parents and Children. Their study is following thousands of children born in the 90s and their parents, tracking data on a variety of health indicators.

As this challenge is organised by Epic we’re using Unreal Engine 4 (UE4) to power the VR environment. I’m much more used to Unity as a 3d engine, so it will be interesting to compare the development process as I go. Epic provide a very handy Unreal Engine 4 for Unity Developers page and Oculus support is built-in so getting set up was quite easy.

The researchers gave us a sample dataset of 10,000 records to get us going and I started off by seeing if I could get Unreal to dynamically create some geometry from it.

The Data

After a bit of reading I found that UE4 has the concept of DataTables and provides an easy way to import csv files into them.

All you have to do is create a struct in header file detailing the column names in your csv:


USTRUCT(blueprintType)
struct FBig_Data : public FTableRowBase
{
	GENERATED_USTRUCT_BODY()
public:

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		float LAB_TSC;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		float LAB_TRIG;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		float LAB_HDL;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		float LAB_GLUC_ADJUSTED;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		float PM_BMI_CONTINUOUS;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 DIS_CVA;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 MEDI_LPD;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 DIS_DIAB;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 DIS_AMI;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 GENDER;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item")
		int32 PM_BMI_CATERGORIAL;
};

Add the csv to your project folder and UE4 asks which struct to use.

Import data table screenshot

Procedural Geometry

I decided to start by generating a cube for each record and arranging them in a grid. It didn’t take long to find some Procedural geometry source code and I integrated this into my project.


// setup sizes
float cubeSize = 10.f;
float cubeSpacing = 10.f;

// create a new mesh
mesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("ProceduralCube"));

// find the material and add it to the mesh
static ConstructorHelpers::FObjectFinder<UMaterialInterface> Material(TEXT("Material'/Game/DataMaterial.DataMaterial'"));
mesh->SetMaterial(0, Material.Object);

// define some triangles
TArray<FProceduralMeshTriangle> triangles;

// get the data
UDataTable* DataLookupTable;
static ConstructorHelpers::FObjectFinder<UDataTable>Big_Data_BP(TEXT("DataTable'/Game/samepledata.sampledata'"));
DataLookupTable = Big_Data_BP.Object;

if (DataLookupTable)
{
	static const FString ContextString(TEXT("GENERAL"));

	uint32 RecordCount = DataLookupTable->GetTableData().Num();

	int edgeSize = ceil(sqrt((float)RecordCount));

	float sizePlusSpacing = (cubeSize + cubeSpacing);

	float startX = floor(edgeSize / -2.f)*sizePlusSpacing;
	float startY = startX;

	for (uint32 i = 0; i < RecordCount; i++)
	{
		FString IndexString = FString::FromInt((int32)i);
		FName IndexName = FName(*IndexString);

		FBig_Data* GOLookupRow = DataLookupTable->FindRow<FBig_Data>(IndexName, ContextString);

		// skip bad rows
		if (!GOLookupRow)
			continue;
		
		// get the row data
		int32 gender = GOLookupRow->GENDER;
		int32 dis_ami = GOLookupRow->DIS_AMI;
		int32 dis_cva = GOLookupRow->DIS_CVA;
		int32 dis_diab = GOLookupRow->DIS_DIAB;
		float lab_gluc_adjusted = GOLookupRow->LAB_GLUC_ADJUSTED;
		float lab_hdl = GOLookupRow->LAB_HDL;
		float lab_trig = GOLookupRow->LAB_TRIG;
		float lab_tsc = GOLookupRow->LAB_TSC;
		int32 medi_lpd = GOLookupRow->MEDI_LPD;
		int32 pm_bmi_categorial = GOLookupRow->PM_BMI_CATERGORIAL;
		float pm_bmi_continuous = GOLookupRow->PM_BMI_CONTINUOUS;

		// Create a Color (4 ints) and UV (2 floats) from the data to be store in each vertex
		FColor col = FColor(gender, pm_bmi_categorial, medi_lpd, dis_diab);
		float float1 = lab_hdl;
		float float2 = lab_trig;

		// position in a grid
		float x = startX + (i % edgeSize) * sizePlusSpacing;
		float y = startY + floor(i / (float)edgeSize) * sizePlusSpacing;

		// build the cube
		GenerateCube(cubeSize, x, y, 0.f, float1, float2, col, triangles);

	}

}

mesh->SetProceduralMeshTriangles(triangles);

RootComponent = mesh;

Dynamic Material values

From experience in other projects, I wanted to show different factors of the data via shader code, as that would allow me to change things in real-time even when the amount of data significantly increased.

For this first test, I encoded four of the integer variables in the data into the R,G,B & A channels of the vertex colour and 2 of the floating point variables into the U & V coordinates normally used for texture mapping.


FProceduralMeshVertex v0;
FProceduralMeshVertex v1;
FProceduralMeshVertex v2;
FProceduralMeshVertex v3;

v0.Color = col;
v1.Color = col;
v2.Color = col;
v3.Color = col;

v0.U = float1; v0.V = float2;
v1.U = float1; v1.V = float2;
v2.U = float1; v2.V = float2;
v3.U = float1; v3.V = float2;

Now data was in the cube vertices, I needed to dynamically change properties of the cube’s Material in the shader.

Coming from a Unity background, UE4’s Material editing system is quite strange at first. I’m used to directly writing Cg/HLSL code and so going to a node-based interface took some getting my head around. After a little while though I managed to use a few snippets of code in Material Expressions to do what I needed.

Dynamic material node

What this is doing is looking at the Red channel of the vertex colour, if it’s 0 (for female) it sets the base colour to one value, if it’s 1 (for male) it uses another.

Next it’s looking at the U value in the vertex (which maps to the LAB_HDL field) and adjusts the cube’s vertical position based upon that. I actually made the cubes into columns and added a scale multiplier to make the differences more noticeable.

Next Steps

Even though this is just a test scene, you do get a much better sense of the number of people in the study in VR compared to a 2d screen.

Masters of Pie have been busy thinking about how the data could be represented and searched, so once we get the next batch of data i’ll get to work on building the next prototype. More updates to follow soon!

comments powered by Disqus