Posted: February 21st, 2012 | Author: genevieve | Filed under: Unity3d | No Comments »
Miguel and I worked together to generate financial data as a three-dimensional landscape. The goal was to see whether a 3D environment would be a new or interesting way to explore a familiar (to some more than others) view of financial data — the daily stock charts. Conceptually, we wanted to connect the idea that the financial system is really an environment that we are all living within, and feeling the effects of. The thought was that data that evoked a natural landscape, like mountains or canyons, would make people connect the financial system to a “natural” system. Other artists have already made this metaphorical connection, like Michael Najaar, but we were curious about what the effects of rendering this in a manufactured 3D environment might be.
We began with an OpenFrameworks program that Miguel wrote to parse Yahoo Financial Data, and render it as a heightmap. We chose a window of historical stock data to analyze: Jan 1, 2000 – Feb 7, 2012, which we hoped would give enough of a range of time, but not be too long before certain tech stocks were publicly traded.
Our first thought of how to render the data so that it would make a terrain was inputting it into a Perlin Noise function. Miguel found a good tutorial for generating noise in C++, which also provided a better understanding of how noise works, and then wrote a nice program that parsed the data csv, and fed values into noise. We determined that the closing price each day and the volume of trades were the values we should take into consideration the most, and fed these values into noise.
The result makes for good terrain, lots of peaks and valleys, but I started to worry that it was a bit too random – that the comparison of multiple stocks would cease to have meaning.

I tried going about generating a 3D mesh straight from the data itself. I finally plotted the points, but although they ended up in 3D space it looked pretty linear (or planar I should say). I needed something that had volume.
Just to illustrate, here’s the data graphed in 3D, with time as the x value, volume as y, and closing price as z:

It’s a lot truer to the data, but it doesn’t give me the same terrain to play with as the noise heightmap. So, I started thinking about various ways I could achieve a similar result while staying “truer” to the data. I tried to figure out if there was a way to start with a base plane mesh, and then extrude along the y axis for every point that aligned with the x and z values. It turns out that an image is a good way to generate a planar mesh (at least in OF), so after talking with Greg, I decided to write up a quick processing sketch that input 2 columns of data, and output a grayscale image. This combined the grayscale heightmapping with the mesh technique of mapping the data points to a shared grid system.
To generate the grayscale image, my process was:
1. Get data to read into processing
2. Map data to width and height of image. x = time, y = closing price
3. Map volume data (what we want to extrude by) to color, 0 – 255.
4. Step through x and y, but plot volume as grayscale circles, radius getting smaller as color gets lighter (taller)
Here is the image result from this quick test:

After testing it in OpenFrameworks to render a 3D mesh, I decided that this was the direction to move forward with in Unity.

With this in mind, Miguel and I sat down to start generating the terrain in Unity. Working from the HeightmapGenerator from the Procedural Examples, we set out to create a financial data terrain programmatically. There might have been a better use of our time than trying to do everything in code, but it definitely made us a lot more familiar with the Unity docs and general structure of writing programs for this environment.
Unsure how much would be visible if we didn’t generate it programmatically, we erred on that side, but underestimated the time it would take to implement. Saving the scene wouldn’t work as we tested out placement and textures. The end result is a bit obvious, and looks sort of like a candy raver emerald city in the clouds, but I like it. Here are some screenshots.



Though not interactive, or even animated, the final result does use an external input to generate imagery. The interaction is primarily camera flyaround. Next steps like changing the terrain based on other financial forces (overall market trends, Dow Jones Divisor) might be interesting. The stocks featured are the 30 stocks in the DJIA – Dow Jones Industrial Average.
Posted: February 19th, 2012 | Author: genevieve | Filed under: Appropriating New Technologies, Thesis, Unity3d | No Comments »
Continuing from my previous post, I was able to get OpenFrameworks working to extrude the brightness values into the Y axis. I tried first with the noise images that Miguel and I generated at the beginning of the week.
Here’s the noise image in 2D:

And here it is extruded into 3D:

Here you can see them both, so you get the idea of how the brightness maps to height:

So, the form looks pretty good. But it also looks pretty random. By feeding financial data into a noise function, we can create a great looking terrain, but it starts to be so far removed from the data itself that it loses meaning. So, I tried to think about how I could generate mesh straight from the data. I finally plotted the points, but although they ended up in 3D space it looked pretty linear (or planar I should say). I needed something that had volume.
Just to illustrate, here’s the data graphed in 3D, with time as the x value, volume as y, and closing price as z:

It’s a lot truer to the data, but it doesn’t give me the same terrain to play with as the noise heightmap. So, I started thinking about various ways I could achieve a similar result while staying “truer” to my data. I tried to figure out if there was a way to start with a base plane mesh, and then extrude along the y axis for every point that aligned with the x and z values. It turns out that an image is a good way to generate a planar mesh (at least in OF), so after talking with greg, I decided to write up a quick processing sketch that input 2 columns of data, and output a grayscale image. This combined the grayscale heightmapping with the mesh technique of mapping the data points to a shared grid system.
To generate the grayscale image, my process was:
1. Get data to read into processing
2. Map data to width and height of image. x = time, y = closing price
3. Map volume data (what we want to extrude by) to color, 0 – 255.
4. Step through x and y, but plot volume as grayscale circles, radius getting smaller as color gets lighter (taller)
Here is the image result I got from this quick test:

I opened it up in Photoshop to do a little blurring, not sure if it was necessary, but didn’t want jagged steps between gradient values. Oh, and I also cropeed, which I should probably implement in code but oh well:

Final step was importing the image back into OF and generating a heightmap there. I used code from James George which made this pretty simple. I ran into a memory error, but I believe that had to do with pixel referencing or something. Here’s a little screencap video that I set to music so it’s a bit more enjoyable to just hang out in dataland. The glitches are due to my computer not enjoying recording and running the app at the same time. Perhaps I need to ramp down the framerate for next time, not that I mind it much.
Posted: January 31st, 2012 | Author: genevieve | Filed under: Unity3d | No Comments »
So this week to get up and running with Unity3d and scripting, I played around a lot with nested for loops and rotation. Our assignment James George’s class Pixels to Polygons is: “On Being Memorious” == Recall an abstract space from your memory and recreate it using only programmed primitives, colors, and lights. Using the GeometryHelper class and example project, create a menu script that generates a static 3d scene without any hand editing/
The abstract space I created is based on an ampitheatre I went to as a child to see cirque du soleil. I mostly remember how amazing the the ceiling was in the theater, and how it made me think of stars, even though it had a regular patter. Anyway, here is the model.


I still have a ways to go figuring out how to apply color to multiple GameObjects (in a for loop), as well as figuring out lighting and moving the camera around. But I was really impressed with the immediate gratification that the GUI offers, and how I could move the lights and camera around manually to get a feel for how I’d like to program their location.
The code requires the GeometryHelper.js script that James made available. Stick it in the plugins folder.
/**
* Geometry Helpers
* by James George
* January 2011
*
* For
* Pixels to Polygons: Coding 3d Worlds ITP
*
* A simple plug-in to generating 3d geometry easy
* MIT LICENSE
*
*/
public class GeometryHelper {
private static var materials : Array = [];
private static var geometryParent : GameObject;
private static var mesh : Mesh;
private static var vertices : Array;
private static var indices : Array;
private static var uvs : Array;
private static var colors : Array;
//SPHERES
static function CreateSphere(pos : Vector3, radius : float) : GameObject {
return CreatePrimitive(pos, radius, PrimitiveType.Sphere);
}
static function CreateSphere(pos : Vector3, scale : Vector3) : GameObject {
return CreatePrimitive(pos,scale, PrimitiveType.Sphere);
}
//
//CUBES
static function CreateCube(pos : Vector3, radius : float) : GameObject {
return CreatePrimitive(pos,radius, PrimitiveType.Cube);
}
static function CreateCube(pos : Vector3, scale : Vector3) : GameObject {
return CreatePrimitive(pos,scale,PrimitiveType.Cube);
}
//
//CYLINDERS
static function CreateCylinder(pos : Vector3, scale : float) : GameObject {
return CreatePrimitive(pos,scale, PrimitiveType.Cylinder);
}
static function CreateCylinder(pos : Vector3, scale : Vector3) : GameObject {
return CreatePrimitive(pos,scale, PrimitiveType.Cylinder);
}
//
//CAPSULES
static function CreateCapsule(pos : Vector3, scale : float) : GameObject {
return CreatePrimitive(pos,scale, PrimitiveType.Capsule);
}
static function CreateCapsule(pos : Vector3, scale : Vector3) : GameObject {
return CreatePrimitive(pos,scale,PrimitiveType.Capsule);
}
//
//GENERIC PRIMITIVE
private static function CreatePrimitive(pos : Vector3, scale : float, type : PrimitiveType) : GameObject {
return CreatePrimitive(pos, Vector3(scale,scale,scale), type);
}
private static function CreatePrimitive(pos : Vector3, scale : Vector3, type : PrimitiveType) : GameObject {
var prim = GameObject.CreatePrimitive(type);
prim.transform.position = pos;
prim.transform.localScale = scale;
prim.transform.parent = GetGeometryParent();
return prim;
}
//
//PLANES
static function CreateVerticlePlaneAlongX(center : Vector3, width : float, height : float) : GameObject {
var plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
plane.transform.position = center;
plane.transform.localScale = Vector3(height, 1, width);
plane.transform.Rotate(-Vector3.forward, 90);
plane.transform.parent = GetGeometryParent();
return plane;
}
static function CreateVerticlePlaneAlongZ(center : Vector3, width : float, height : float) : GameObject {
var plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
plane.transform.position = center;
plane.transform.localScale = Vector3(width, 1, height);
plane.transform.Rotate(Vector3.left, 90);
plane.transform.parent = GetGeometryParent();
return plane;
}
static function CreateHorizontalPlane(center : Vector3, width : float, depth : float) : GameObject {
var plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
plane.transform.position = center;
plane.transform.localScale = Vector3(width, 1, depth);
plane.transform.parent = GetGeometryParent();
return plane;
}
//
//POLYGONS
static function CreatePoly(a : Vector3, b : Vector3, c : Vector3) : GameObject {
BeginPolys();
AddPoly(a,b,c);
EndPolys();
}
static function BeginPolys(){
if(mesh != null){
Debug.LogWarning("Forcing end polys. check that begin/end is matched");
EndPolys();
}
mesh = new Mesh();
vertices = [];
indices = [];
uvs = [];
colors = [];
}
static function AddPoly(a : Vector3, b : Vector3, c : Vector3){
if(mesh == null){
Debug.LogWarning("Forcing begin polys. Check that begin/end is matched");
BeginPolys();
}
vertices.Push(a);
vertices.Push(b);
vertices.Push(c);
indices.Push(vertices.length-3);
indices.Push(vertices.length-2);
indices.Push(vertices.length-1);
uvs.Push(Vector2(0,0));
uvs.Push(Vector2(1,0));
uvs.Push(Vector2(0,1));
// colors.Push(Color(Random.value,Random.value,Random.value));
// colors.Push(Color(Random.value,Random.value,Random.value));
// colors.Push(Color(Random.value,Random.value,Random.value));
}
static function EndPolys() : GameObject {
mesh.vertices = vertices;
mesh.triangles = indices;
mesh.uv = uvs;
mesh.colors = colors;
mesh.RecalculateNormals();
mesh.RecalculateBounds();
var meshContainer : GameObject = GameObject("TriMesh");
meshContainer.AddComponent(MeshFilter);
meshContainer.GetComponent(MeshFilter).mesh = mesh;
meshContainer.AddComponent(MeshRenderer);
meshContainer.renderer.sharedMaterial = GetMaterialWithColor(Color.gray);
meshContainer.transform.parent = GetGeometryParent();
mesh = null;
return meshContainer;
}
//LIGHTS
static function CreatePointLight(position : Vector3, range : float, intensity : float, color : Color) : GameObject {
var lightContainer = CreateLight(position, intensity, color, LightType.Point);
var lightComponent = lightContainer.GetComponent(Light);
lightComponent.range = range;
return lightContainer;
}
static function CreateDirectionalLight(position : Vector3, direction : Vector3, intensity : float, color : Color) : GameObject {
var lightContainer = CreateLight(position, intensity, color, LightType.Directional);
lightContainer.transform.LookAt(position + direction);
return lightContainer;
}
static function CreateSpotLight(position : Vector3, direction : Vector3, range : float, angle : float, intensity : float, color : Color) : GameObject {
var lightContainer = CreateLight(position, intensity, color, LightType.Spot);
lightContainer.transform.LookAt(position + direction);
var lightComponent = lightContainer.GetComponent(Light);
lightComponent.spotAngle = angle;
lightComponent.range = range;
return lightContainer;
}
private static function CreateLight(position : Vector3, intensity : float, color : Color, type : LightType) : GameObject {
var lightContainer : GameObject = new GameObject("Light");
lightContainer.AddComponent(Light);
var lightComponent = lightContainer.GetComponent(Light);
lightComponent.type = type;
lightComponent.intensity = intensity;
lightComponent.color = color;
lightContainer.transform.parent = GetGeometryParent();
lightContainer.transform.position = position;
return lightContainer;
}
//TODO: applying colors
static function ApplyColor(object : GameObject, c : Color){
object.renderer.sharedMaterial = GetMaterialWithColor(c);
}
//TODO: applying rotations
static function Flip(object : GameObject, axis : Vector3){
object.transform.Rotate(axis, 180);
}
private static function GetMaterialWithColor( c : Color) : Material{
for(var i = 0; i < materials.length; i++){
if(materials[i].color == c){
mat = materials[i];
break;
}
}
if(mat == null){
mat = new Material(Shader.Find("Diffuse"));
mat.SetColor("_Color", c);
materials.Push(mat);
}
return mat;
}
private static function GetGeometryParent() : Transform{
if(geometryParent == null){
geometryParent = GameObject("GeneratedGeometry");
}
return geometryParent.transform;
}
}
And here is my messyish code – Don’t mind all the things commented out.
@MenuItem ("Assignment1-2ndtry/HelloWorld")
static function HelloWorld () {
Debug.Log ("Hello World");
}
/**
* Test functions for all the geometry rendering
*
*/
@MenuItem ("Assignment1-2ndtry/Test Geometry")
static function TestAllGeometry() {
var g : GameObject;
var j : int;
for (j = 0; j < 50 ; j ++) {
var degrees : float = 360 * j / 50.0;
var distance : float = 50;
var pos : Vector3 = Vector3(Mathf.Cos(degrees * Mathf.Deg2Rad), 0, Mathf.Sin(degrees * Mathf.Deg2Rad)) * distance;
var i : int;
//GeometryHelper.CreateSpotLight(Vector3(pos.x*j, (pos.y + 500), pos.z*j), -Vector3.up, 100.0, 1, 1, Color.white);
for (i = 0; i < 20; i++) {
g = GeometryHelper.CreateCube(Vector3(pos.x*i, pos.y, pos.z*i), Vector3(i*1, i*1, (i*i)/2));
g.transform.rotation.eulerAngles = Vector3(90,-degrees + 90,0);
g = GeometryHelper.CreateSphere(Vector3(pos.x*i, pos.y + 500 - (i*i), pos.z*i), 10);
//g = GeometryHelper.CreateSphere(Vector3(pos.x*i, pos.y - 500 + (i*i), pos.z*i), 10);
if (i == 15) {
GeometryHelper.CreateSpotLight(Vector3(pos.x*i, (pos.y + 800) - (i*i), pos.z*i), -Vector3.up, 800.0, 100.0, 1, Color.white);
}
}
GeometryHelper.CreateSpotLight(Vector3(0, 800, 0), -Vector3.up, 800.0, 115.0, 1.5, Color.yellow);
/*
var i : int;
var g2 : GameObject;
for (i = 0; i < 40; i++) {
g2 = GeometryHelper.CreateCube(Vector3(i*30, 0, 10), Vector3(10, i*10, 10));
g2.name = "Cube" + i;
}
for (i = 0; i < 40; i++) {
g2 = GeometryHelper.CreateCube(Vector3(i*-30, 0, 10), Vector3(10, i*10, 10));
g2.name = "Cube2_" + i;
//GeometryHelper.Flip(g, Vector3(0,0,0));
}
*/
}
}
/*
@MenuItem ("Assignment1-2ndtry/Move Camera")
static function MoveCamera(){
Camera.main.transform.position = Vector3(-275, -25, -700); //move it somewhere;
Camera.main.transform.LookAt(Vector3(335, 0, 360)); // point it at something!;
}
*/
//how do I set mulitple camera views without null pointer exceptions happening?
@MenuItem ("Assignment1-2ndtry/Wide")
static function MoveCamera(){
Camera.main.transform.position = Vector3(-100, 500, -1400); //move it somewhere;
Camera.main.transform.LookAt(Vector3(25, 0, 0)); // point it at something!;
}