The Journal

This journal is a development blog for my Final Year Project at the University of Kent. The project is to create an interactive 3D map of the University of Kent campus, using Three.js and WebGL. The application will allow users to explore a scale model of the campus while interacting with elements to reveal lots of different information.

I will use this journal to keep track of my research, design ideas and technical progress throughout the project. This will help me to organise my thoughts and get feedback from possible users of the application.

The final completed project can be seen at 3d.eda.kent.ac.uk

dashboard-v3-filters

Filters inspiration

📷

Loading XML and adding nodes to a HTML element

In order to display information for each building when the user interacts with it, I have written code to load an XML file with AJAX and pull content from it into a HTML element. The following code snippet simply loads the xml file and stores it in a variable called ‘xml’.

var xml;

$.ajax({
    type: "GET",
    url: "assets/slides.xml",
    dataType: "xml",
    success: function(data) { xml = data;}
});

Within the click event listener I then clear the HTML element with the id of ‘slidepanel’. This makes sure that the data will change each time a building is clicked.

$('#slidepanel').empty();

Once this is cleared I search through the XML file and retrieve the content of a node based on the name of the clicked object. The content is then appended to the freshly cleared div.

var node = intersects[0].object.name;

$(xml).find(node).each(function(){
    var content = $(this).find('content').text();
    $(content).appendTo('#slidepanel');
});

Adding buttons to control the camera

I wanted to make it very easy for users to be able to switch to a 2D view which will allow them to easily find the building they are looking for in a more traditional format. I created a simple function to move the camera above the current center point when a button is pressed. It checks whether the camera is positive or negative along the z-axis and then orients the map accordingly. The function is shown below:

function view2D() {
	if (camera.position.z >= controls.center.z){
		new TWEEN.Tween( camera.position ).to( {
			x: controls.center.x,
			y: 500,
			z: controls.center.z + 1 }, 1000 )
		.easing( TWEEN.Easing.Quadratic.Out).start();
	}else{
		new TWEEN.Tween( camera.position ).to( {
			x: controls.center.x,
			y: 500,
			z: controls.center.z - 1 }, 1000 )
		.easing( TWEEN.Easing.Quadratic.Out).start();
	}
}

I also created a very simple function to move the camera very low down the y-axis to give a zoomed-in view.

function tilt() {
	new TWEEN.Tween( camera.position ).to( {
			y: 80 }, 1000 )
		.easing( TWEEN.Easing.Quadratic.InOut).start();
}

Initial wireframe

This is just a quick mock-up of the general layout of the website. Most of the information will be displayed in a sliding panel and as icons either on the map itself or on the sidebar.

Wacom

Inspiration: Wacom (http://www.wacom.com)

📷

Function for creating sprite labels above each building

I have developed a function to create a 2D sprite label above each building geometry, using the filename of its json file. The sprite is positioned in 3D space and always faces the camera.

First I set the parameters of the sprite material which will be created in a 2D canvas and then applied to the sprite as a texture map:

var fontface = "Arial";
var fontsize = 12;
var borderColor = { r:255, g:255, b:255, a:1.0 };
var backgroundColor = { r:255, g:255, b:255, a:1.0 };
var spriteAlignment = THREE.SpriteAlignment.topLeft;

This means that I can easily change these parameters later to change the styling of the sprite. Next I create the 2D canvas and build the sprite based on the parameters:

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = "Normal " + fontsize + "px " + fontface;

// get size data (height depends only on font size)
var metrics = context.measureText( message );
var textWidth = metrics.width;

// background color
context.fillStyle   = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")";
// border color
context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")";

context.lineWidth = 5;
roundRect(context, 5/2, 5/2, textWidth + 5, fontsize * 1.4 + 5, 6); //uses a simple function to draw a rounded rectangle
context.fillStyle = "rgba(0, 0, 0, 1.0)";

Read more

Snowbird

Inspiration: Snowbird (http://www.snowbird.com)

📷

Google Maps Cube

Google Maps Cube (http://www.playmapscube.com)

📷

The loop for loading all building meshes

This is possibly one of the most important blocks of code in the project so far. I want to be able to load each of my building models so that they can be interacted with separately. However I don’t want to create a new json loader for each model as this will greatly affect performance and loading time. Therefore I decided to build a function factory within a loop which loads each model in the array of filenames.

Below is the array of filenames and the loop for loading the meshes. I set a material and the loader outside the loop so they are only created once. Within the loop I split the file names to remove the unnecessary parts of the path and then call the loader for each index in the array.

var jsonFileNames = [
    'assets/models/Keynes_College.js',
    'assets/models/Templeman_Library.js'
    // More files will be added here
];

var material = new THREE.MeshLambertMaterial({ color: 0xcccccc });

var loader = new THREE.JSONLoader();

for(var i = 0; i < jsonFileNames.length; i++){
    var meshName = jsonFileNames[i].split("/")[2].split(".")[0];
    loader.load(jsonFileNames[i], makeHandler(meshName));
}

Read more

Le Tour de France

Inspiration: Le Tour de France (http://100ansdetour.fr)

📷

GlacierWorks

Inspiration: GlacierWorks (http://glacierworks.org)

📷

Thank you for taking the time to look over my most recent work. Please feel free to contact me for any further information, to hire me for a project or to simply have a good old chat.

Click here to hire me