diff --git a/NOMADVRLib/ConfigFile.cpp b/NOMADVRLib/ConfigFile.cpp
index 196528fea930895e46ee8f6343e7fbb3861a4535..7ba4e32e5ed358fd2dc42bbfa4a329362577aeda 100644
--- a/NOMADVRLib/ConfigFile.cpp
+++ b/NOMADVRLib/ConfigFile.cpp
@@ -25,10 +25,9 @@
 #include "eprintf.h"
 #include "polyhedron.h"
 
-const char * PATH;
-const char * SCREENSHOT;
+char * PATH;
+char * SCREENSHOT;
 int ISOS;
-int TIMESTEPS;
 float **isocolours; // [ISOS][4];
 const char **plyfiles;
 float **translations;
@@ -113,7 +112,7 @@ const char * loadConfigFileErrors[] =
 };
 
 void updateTIMESTEPS (int timesteps)
-{
+{ 
 if (TIMESTEPS==0)
 	TIMESTEPS=timesteps;
 else
@@ -164,16 +163,64 @@ while (*file!='\0') {
 }
 }
 
+void cleanConfig()
+{
+	for (int i = 0; i < ISOS; i++) {
+		delete[] isocolours[i];
+		delete[] translations[i];
+	}
+	delete[] isocolours;
+	isocolours=nullptr;
+	delete[] translations;
+	translations=nullptr;
+	if (plyfiles) {
+		for (int i=0;i<ISOS;i++)
+			free ((void*)(plyfiles[i])); //strdup
+		delete[] plyfiles;
+	}
+	plyfiles=nullptr;
+	free(PATH);
+	PATH=nullptr;
+	atomtrajectoryrestarts.clear();
+	free(SCREENSHOT);
+	SCREENSHOT=nullptr;
+
+	if (markers) {
+		for (int i=0;i<TIMESTEPS;i++) {
+			delete[] markers[i];
+			delete[] markercolours[i];
+		}
+		delete[] markers;
+		delete[] markercolours;
+		markers=nullptr;
+		markercolours=nullptr;
+	}
+	for (int i=0;i<info.size();i++) {
+		free(info[i].filename);
+	}
+	info.clear();
+
+	if (numAtoms) {
+		for (int i=0;i<getAtomTimesteps();i++) {
+			delete[] atoms[i];
+		}
+		delete[] atoms;
+		delete[] numAtoms;
+		numAtoms=nullptr;
+		atoms=nullptr;
+	}
+}
+
 void initState()
 {
 	BACKGROUND[0] = 0.95f;
 	BACKGROUND[1] = 0.95f;
 	BACKGROUND[2] = 0.95f;
-	SCREENSHOT="C:\\temp\\frame";
+	SCREENSHOT=strdup("C:\\temp\\frame");
 	ISOS = 0;
 	TIMESTEPS=0;
 	PATH=strdup("");
-	numAtoms=0;
+	numAtoms=nullptr;
 	atomScaling=1;
 	clonedAtoms=0;
 	fixedAtoms=false;
@@ -357,6 +404,7 @@ int loadConfigFile(const char * f)
 			r=readString(F, s);
 			if (r!=0)
 				return -14;
+			free (SCREENSHOT);
 			SCREENSHOT = strdup(s);
 		}
 		else if (!strcmp(s, "xyzfile")||!strcmp(s, "atomfile")) {
diff --git a/NOMADVRLib/ConfigFile.h b/NOMADVRLib/ConfigFile.h
index 27a7ce2f800b37b67003c47ae456866712b02a92..8c9d967f9a92f930630cf4cfa0198112b707ea58 100644
--- a/NOMADVRLib/ConfigFile.h
+++ b/NOMADVRLib/ConfigFile.h
@@ -21,10 +21,9 @@
 #include "polyhedron.h"
 #include "ConfigFileAtoms.h"
 
-extern const char * PATH;
-extern const char * SCREENSHOT;
+extern char * PATH;
+extern char * SCREENSHOT;
 extern int ISOS;
-extern int TIMESTEPS;
 extern float **isocolours; // [ISOS][4];
 extern const char **plyfiles;
 extern float **translations;
@@ -81,13 +80,14 @@ extern float ** markercolours;
 
 extern const char * loadConfigFileErrors[];
 
+void cleanConfig();
 int loadConfigFile(const char * f);
 
 struct information {
 	float pos[3];
 	float size;
 	int atom; //-1=do not draw line
-	const char* filename;
+	char* filename;
 	GLuint tex;
 };
 
diff --git a/NOMADVRLib/atoms.cpp b/NOMADVRLib/atoms.cpp
index 086f016109cb446f3fc351a0734dcd406e1df1fe..18df5eb89b22d5ba400bc29579f0978b1a47ca33 100644
--- a/NOMADVRLib/atoms.cpp
+++ b/NOMADVRLib/atoms.cpp
@@ -44,10 +44,19 @@ const char * TMPDIR;//filled by main
 
 bool inv_abc_init=false;
 float inv_abc[3][3];
+int TIMESTEPS;
 
 std::vector<const char*> extraAtomNames;
 std::vector<float*> extraAtomData;
 
+int getAtomTimesteps() 
+{
+	if (fixedAtoms)
+		return 1;
+	else
+		return TIMESTEPS;
+}
+
 const char * const atomNames[] =
 
 {
@@ -208,6 +217,8 @@ do {
 } while (c != EOF && c != '\n');
 }
 
+
+
 int findAtom(const char *const s)
 {
 	//discard number at end
@@ -252,6 +263,17 @@ const char * readAtomsXYZErrors[] = {
 	"Corrupt xyz file" //-4
 };
 
+void cleanAtoms (int **numatoms, int timesteps, float ***pos)
+{
+	for (int i=0;i<timesteps;i++) {
+		delete[] ((*pos)[i]);
+	}
+	delete[] (*numatoms);
+	*numatoms=nullptr;
+	delete[] (*pos);
+	*pos=nullptr;
+}
+
 int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float ***pos) 
 {
 	int mynumatoms;
diff --git a/NOMADVRLib/atoms.hpp b/NOMADVRLib/atoms.hpp
index 750bb6723a1d2f9797f39481f39705a2591286ec..96a8c25eb87ab23424e60cc6c46fb0eeaa7f7be6 100644
--- a/NOMADVRLib/atoms.hpp
+++ b/NOMADVRLib/atoms.hpp
@@ -31,6 +31,8 @@ const int atomsInPeriodicTable=118;
 
 extern const char * TMPDIR;
 
+int getAtomTimesteps();
+void cleanAtoms (int **numatoms, int timesteps, float ***pos);
 int readAtomsXYZ(const char *const file, int **numatoms, int *timesteps, float ***pos);
 int readAtomsCube(const char *const file, int **numatoms, int *timesteps, float ***pos);
 int readAtomsJson (const char *const file, int **numatoms, int *timesteps, float ***pos, float abc[3][3],  std::vector<float>** clonedAtoms, const char *const token=0);
@@ -66,4 +68,6 @@ extern const char * const atomNames[];
 extern std::vector<const char*> extraAtomNames;
 extern std::vector<float*> extraAtomData;
 
+extern int TIMESTEPS;
+
 #endif //__ATOMS_H
diff --git a/NOMADVRLib/atomsGL.cpp b/NOMADVRLib/atomsGL.cpp
index e2e061e515eaabbabf4da8396c4a736ad3914290..546257dbcc5269544e1b973a3700a85ff491098a 100644
--- a/NOMADVRLib/atomsGL.cpp
+++ b/NOMADVRLib/atomsGL.cpp
@@ -28,14 +28,6 @@
 #include "polyhedron.h"
 #include "Grid.h"
 
-int getAtomTimesteps() 
-{
-	if (fixedAtoms)
-		return 1;
-	else
-		return TIMESTEPS;
-}
-
 GLenum atomTexture(GLuint t)
 {
 	GLenum e;
@@ -230,6 +222,26 @@ if (!solid) {
 	return e;
 } //SetupAtomsNoTess
 
+void CleanAtoms (GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLuint *BondIndices)
+{
+	if (!numAtoms)
+		return;
+
+	glDeleteVertexArrays(4, *AtomVAO);
+	glDeleteBuffers(3, *AtomVertBuffer);
+	glDeleteBuffers(1, BondIndices);
+
+	delete[] (*AtomVAO);
+	delete[] (*AtomVertBuffer);
+	*AtomVAO=nullptr;
+	*AtomVertBuffer=nullptr;
+
+
+	if (numBonds)
+		delete[] numBonds;
+	numBonds=nullptr;
+	bonds.clear();
+}
 
 GLenum SetupAtoms(GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLuint *BondIndices)
 {
@@ -245,7 +257,7 @@ GLenum SetupAtoms(GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLu
 	for (int i=0;i<getAtomTimesteps() ;i++) {
 		totalatoms += numAtoms[i];
 	}
-	eprintf("SetupAtoms: totalatoms=%d", totalatoms);
+	//eprintf("SetupAtoms: totalatoms=%d", totalatoms);
 
 	*AtomVAO = new GLuint[4]; //atoms, cloned atoms, bonds, trajectories
 	*AtomVertBuffer = new GLuint[3]; //atoms, cloned atoms, trajectories
@@ -437,6 +449,13 @@ GLenum SetupAtoms(GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLu
 	return e;
 }
 
+void CleanInfoCube  (GLuint *VAO, GLuint *VertBuffer, GLuint *IndexBuffer)
+{
+	glDeleteVertexArrays(1, VAO);
+	glDeleteBuffers(1, VertBuffer);
+	glDeleteBuffers(1, IndexBuffer);
+}
+
 GLenum SetupInfoCube (GLuint *VAO, GLuint *VertBuffer, GLuint *IndexBuffer)
 {
 	glGenVertexArrays(1, VAO);
@@ -606,6 +625,12 @@ GLenum SetupMarkerNoTess(GLuint *MarkerVAO, GLuint *MarkerVertBuffer, GLuint *Ma
 	return e;
 }
 
+void CleanMarker (GLuint *MarkerVAO, GLuint *MarkerVertBuffer)
+{
+	glDeleteVertexArrays(1, MarkerVAO);
+	glDeleteBuffers(1, MarkerVertBuffer);
+}
+
 GLenum SetupMarker(GLuint *MarkerVAO, GLuint *MarkerVertBuffer)
 {//requires tesselation
 	if (!markers)
@@ -647,6 +672,13 @@ GLenum SetupMarker(GLuint *MarkerVAO, GLuint *MarkerVertBuffer)
 	return glGetError();
 }
 
+void CleanUnitCell (GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *UnitCellIndexBuffer)
+{
+	glDeleteVertexArrays(1, UnitCellVAO);
+	glDeleteBuffers(1, UnitCellVertBuffer);
+	glDeleteBuffers(1, UnitCellIndexBuffer);
+}
+
 GLenum SetupUnitCell(GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *UnitCellIndexBuffer)
 {
 	//add here both unit cell and supercell
diff --git a/NOMADVRLib/atomsGL.h b/NOMADVRLib/atomsGL.h
index f01262f2ef9a794c08c8deeae578a537f18686d9..8d6e1b96bc735e5388f9207f8b3fdc95c23dc04f 100644
--- a/NOMADVRLib/atomsGL.h
+++ b/NOMADVRLib/atomsGL.h
@@ -28,10 +28,14 @@
 
 GLenum atomTexture(GLuint t);
 GLenum SetupAtoms(GLuint **AtomVAO, GLuint **AtomVertBuffer, GLuint *BondIndices);
+void CleanAtoms (GLuint **AtomVAO /*[4]*/, GLuint **AtomVertBuffer /*[3]*/, GLuint *BondIndices);
 GLenum SetupAtomsNoTess (GLuint **AtomVAO, GLuint **AtomVertBuffer, GLuint **AtomIndexBuffer);
+void CleanUnitCell (GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *UnitCellIndexBuffer);
 GLenum SetupUnitCell(GLuint *UnitCellVAO, GLuint *UnitCellVertBuffer, GLuint *UnitCellIndexBuffer);
+void CleanMarker (GLuint *MarkerVAO, GLuint *MarkerVertBuffer);
 GLenum SetupMarker(GLuint *MarkerVAO, GLuint *MarkerVertBuffer);
 GLenum SetupMarkerNoTess(GLuint *MarkerVAO, GLuint *MarkerVertBuffer, GLuint *MarkerIndexBuffer);
+void CleanInfoCube  (GLuint *VAO, GLuint *VertBuffer, GLuint *IndexBuffer);
 GLenum SetupInfoCube (GLuint *VAO, GLuint *VertBuffer, GLuint *IndexBuffer);
 
 bool PrepareUnitCellAtomShader (GLuint *AtomP, GLuint *cellP, GLuint *MarkerP, 
@@ -44,7 +48,6 @@ bool PrepareMarkerShader (GLuint *MP, GLint *MMatrixLocation);
 
 void GetDisplacement(int p[3], float f[3]);
 
-int getAtomTimesteps();
 inline int getTotalAtomsInTexture()
 {
 	return atomsInPeriodicTable+extraAtomNames.size();
diff --git a/OpenVR/README.md b/OpenVR/README.md
index d768b7c12e5ac5c3d3fc767758d815d2bed1447c..026863b38477f13ec17eb4c486b2bda59670e67e 100644
--- a/OpenVR/README.md
+++ b/OpenVR/README.md
@@ -1,7 +1,10 @@
 This repository contains the demos for HTC vive using openvr version 0.9.19
 https://github.com/ValveSoftware/openvr
 
-Clone that repository and add the content of this one inside the samples directory.
+Clone that repository and add the content of this one inside the samples 
+directory.
+After compilation, copy ..\textures\digits_64x7_l_blank.png to the exe's 
+location.
 
 List of directories
 TimestepData: 
diff --git a/OpenVR/TimestepData/hellovr_opengl_main.cpp b/OpenVR/TimestepData/hellovr_opengl_main.cpp
index 0decf6efe839f4333771ebb3fa69b498346e08a9..284461e02865bef4a3d0e4dcaf2c2e8f1fb726b6 100644
--- a/OpenVR/TimestepData/hellovr_opengl_main.cpp
+++ b/OpenVR/TimestepData/hellovr_opengl_main.cpp
@@ -5,7 +5,7 @@
 
 /*This code is therefore licensed under Apache 2.0*/
 /*
- # Copyright 2016-2018 Ruben Jesus Garcia Hernandez
+ # Copyright 2016-2018 Ruben Jesus Garcia-Hernandez
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
@@ -36,7 +36,6 @@
 #include <SDL.h>
 #include <GL/glew.h>
 #include <SDL_opengl.h>
-//#include <SDL_ttf.h>
 #include <gl/glu.h>
 #include <stdio.h>
 #include <string>
@@ -70,25 +69,13 @@
 
 #define TESSSUB 16
 
-//TTF_Font* font;
-
-//#define LOCALTEST
-
-/*#ifdef LOCALTEST
-#define PATH "Y:\\v2t\\datasets\\mpcdf_CO2_CaO\\cubes-bigger\\IsosurfacesFixed\\"
-#else
-#define PATH "data\\CO2-CaO-B\\"
-//#define PATH "C:\\Users\\mobile\\Desktop\\openvrDemos\\win64\\data\\CO2-CaO-B\\"
-#endif
-*/
 #define ZLAYERS transparencyquality
 
-
+#define NUMBERTEXTURE "digits_64x7_l_blank.png" 
+//png
 
 #define MAXGPU 300
 
-//shown on https://www.vbw-bayern.de/vbw/Aktionsfelder/Innovation-F-T/Forschung-und-Technologie/Zukunft-digital-Big-Data.jsp
-
 #define GRID 1
 
 #define NUMLODS 1
@@ -147,11 +134,14 @@ public:
 	bool SetupDepthPeeling();
 
 	void SetupScene();
+	void CleanScene();
 	void SetupIsosurfaces();
 	void SetupAtoms();
 	void SetupUnitCell();
 	void SetupMarker();
 	void SetupInfoCube();
+	void SetupInfoBoxTexture();
+
 
 	void DrawControllers();
 
@@ -220,8 +210,9 @@ private: // OpenGL bookkeeping
 	int m_iValidPoseCount;
 	int m_iValidPoseCount_Last;
 	bool m_bShowCubes;
-	bool buttonPressed[2][vr::k_unMaxTrackedDeviceCount]; //grip, application menu
-	bool AtomsButtonPressed[2];
+	bool buttonPressed[3][vr::k_unMaxTrackedDeviceCount]; 
+		//grip, application menu, nextConfig (up/down in circle)
+	//bool AtomsButtonPressed[3];
 	bool showAtoms;
 	std::string m_strPoseClasses;                            // what classes we saw poses for this frame
 	char m_rDevClassChar[vr::k_unMaxTrackedDeviceCount];   // for each device, a character representing its class
@@ -320,6 +311,7 @@ private: // OpenGL bookkeeping
 	GLint m_nUnitCellMatrixLocation, m_nUnitCellColourLocation;
 	GLint m_nMarkerMatrixLocation;
 	GLint m_nTotalatomsLocation;
+	GLint m_nSelectedAtomLocation;
 
 	struct FramebufferDesc
 	{
@@ -343,6 +335,19 @@ private: // OpenGL bookkeeping
 	char *pixels, *pixels2; //for saving screenshots to disk
 	int framecounter;
 	bool savetodisk;
+
+	int selectedAtom;
+
+	GLuint numbersTexture;
+
+	bool PrepareControllerGlyph (const vr::Hmd_Eye nEye, const int controller, Vector3* pos);
+	void RenderControllerGlyph (const vr::Hmd_Eye nEye, const int controller);
+	void RenderControllerGlyphs(vr::Hmd_Eye nEye);
+	int LoadConfigFile (const char *c);
+
+	int myargc;
+	char **myargv;
+	int currentConfig;
 };
 
 const float CMainApplication::videospeed = 0.01f;
@@ -393,6 +398,40 @@ void eprintf( const char *fmt, ... )
 	MessageBoxA(0, buffer, "Warning", 0);
 }
 
+int CMainApplication::LoadConfigFile (const char *c)
+{
+		
+	//change cwd so that relative paths work
+	const char *myc=c;
+	std::string s(c);
+	std::string::size_type l=s.find_last_of("\\/");
+	std::string mys;
+	if (l!=s.npos) {
+		SetCurrentDirectoryA(s.substr(0, l).c_str());
+		mys=s.substr(l+1);
+		myc=mys.c_str();
+	}
+	int r;
+	if ((r=loadConfigFile(myc))<0) {
+		if (-100<r)
+			MessageBoxA(0, loadConfigFileErrors[-r], "Config file reading error", 0);
+		else if (-200<r)
+			MessageBoxA(0, readAtomsXYZErrors[-r-100], "XYZ file reading error", 0);
+		else if (-300<r)
+			MessageBoxA(0, readAtomsCubeErrors[-r-200], "Cube file reading error", 0);
+		else if (-400<r) 
+			MessageBoxA(0, readAtomsJsonErrors[-r-300], "Encyclopedia Json reading error", 0);
+		else
+			MessageBoxA(0, readAtomsAnalyticsJsonErrors[-r-400], "Analytics Json reading error", 0);
+		return -100+r;
+	}
+
+	if (solid)
+		MessageBoxA(0, "Only spheres implemented as atom glyphs in HTC Vive", "Atom Glyph", 0);
+
+	return r;
+}
+
 //-----------------------------------------------------------------------------
 // Purpose: Constructor
 //-----------------------------------------------------------------------------
@@ -456,8 +495,14 @@ CMainApplication::CMainApplication(int argc, char *argv[])
 	, pixels(0)
 	, framecounter(0)
 	, savetodisk(false)
+	, numbersTexture(0)
+	, selectedAtom(-1)
+	, myargc(argc)
+	, myargv(argv)
+	, currentConfig(1)
 {
-	for (int j=0;j<2;j++)
+	LoadConfigFile(argv[currentConfig]);
+	for (int j=0;j<3;j++)
 		for (int i = 0; i < vr::k_unMaxTrackedDeviceCount; i++) {
 			buttonPressed[j][i] = false;
 		}
@@ -672,7 +717,8 @@ bool CMainApplication::BInitGL()
 
 	GLenum e;
 
-	SetupTexturemaps();
+	if (!SetupTexturemaps())
+		eprintf ("Problem loading textures");
 	e=glGetError();
 	if (e!=GL_NO_ERROR)
 		eprintf ("gl error %d, %s %d", e, __FILE__, __LINE__);
@@ -747,32 +793,12 @@ void CMainApplication::Shutdown()
 	}
 	m_vecRenderModels.clear();
 	
-	if (vertdataarray) {
-		for (int i = 0; i < NUMLODS; i++)
-			if (vertdataarray[i])
-				delete[] vertdataarray[i];
-		delete[]vertdataarray;
-		vertdataarray = 0;
-	}
-	if (vertindicesarray){
-		for (int i = 0; i < NUMLODS; i++)
-			if (vertindicesarray[i])
-				delete[] vertindicesarray[i];
-		delete[] vertindicesarray;
-		vertindicesarray = 0;
-	}
+	CleanScene();
+
 	if (m_pContext)
 	{
 		glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_FALSE);
 		glDebugMessageCallback(nullptr, nullptr);
-		if (m_glSceneVertBuffer) {
-			for (int i = 0; i < NUMLODS; i++) {
-				glDeleteBuffers(NUMPLY, m_glSceneVertBuffer[i]);
-				delete[] (m_glSceneVertBuffer[i]);
-			}
-			delete[] m_glSceneVertBuffer;
-			m_glSceneVertBuffer = nullptr;
-		}
 		glDeleteBuffers(1, &m_glIDVertBuffer);
 		glDeleteBuffers(1, &m_glIDIndexBuffer);
 
@@ -846,14 +872,7 @@ void CMainApplication::Shutdown()
 		{
 			glDeleteVertexArrays( 1, &m_unControllerVAO );
 		}
-		if (m_unSceneVAOIndices!=0)
-		{
-			for (int i=0;i<NUMLODS;i++) {
-				glDeleteBuffers(NUMPLY, m_unSceneVAOIndices[i]);
-				delete[] m_unSceneVAOIndices[i];
-			}
-			delete[] m_unSceneVAOIndices;
-		}
+
 		if (m_iTexture != 0) {
 			glDeleteTextures(3+ZLAYERS+1, m_iTexture);
 			delete[] m_iTexture;
@@ -873,22 +892,6 @@ void CMainApplication::Shutdown()
 		m_pWindow = NULL;
 	}
 
-	if (m_uiVertcount!=0) {
-		for (int i=0;i<NUMLODS;i++) {
-			delete[] m_uiVertcount[i];
-		}
-		delete[] m_uiVertcount;
-	}
-
-	if (numAtoms!=0) {
-		for (int i=0;i<getAtomTimesteps();i++) {
-			delete[] atoms[i];
-		}
-		delete[] atoms;
-		delete[] numAtoms;
-		numAtoms=0;
-		atoms=0;
-	}
 	if (pixels !=0)
 		delete [] pixels;
 	SDL_Quit();
@@ -977,17 +980,56 @@ bool CMainApplication::HandleInput()
 			//this hides the controllers when buttons are pressed. Why?! -> 
 			//rgh: the name of the variable seems to make it so :o) Possibly so that a different model can be used with the correct deformation
 			//m_rbShowTrackedDevice[unDevice] = state.ulButtonPressed == 0;
-
+			bool x=(state.ulButtonPressed & (vr::ButtonMaskFromId(vr::k_EButton_Axis0) |
+					vr::ButtonMaskFromId(vr::k_EButton_ApplicationMenu)|
+					vr::ButtonMaskFromId(vr::k_EButton_Grip)))!=0ull;
+			if (x) {
+				if (firstdevice==-1)
+					firstdevice=unDevice;
+				else if(unDevice !=firstdevice && seconddevice==-1)
+					seconddevice=unDevice;
+			}
+			if (unDevice==firstdevice) {
+				if (buttonPressed[2][unDevice] && 
+					/*(state.ulButtonTouched&vr::ButtonMaskFromId(vr::k_EButton_Axis0)) == 0 &&*/
+					(state.ulButtonPressed&vr::ButtonMaskFromId(vr::k_EButton_Axis0)) == 0)
+					buttonPressed[2][unDevice]=false;
+				if (!buttonPressed[2][unDevice] && 
+					(/*state.ulButtonTouched&vr::ButtonMaskFromId(vr::k_EButton_Axis0) || */
+					 state.ulButtonPressed&vr::ButtonMaskFromId(vr::k_EButton_Axis0) 
+					)){
+					buttonPressed[2][unDevice]=true;
+					if (state.rAxis[0].y > -0.4 && state.rAxis[0].y < 0.4 && state.rAxis[0].x<-0.7) {
+						selectedAtom=-1;
+					} else if (state.rAxis[0].y > 0.7 && state.rAxis[0].x > -0.4 && state.rAxis[0].x < 0.4) {
+						//next config file
+						currentConfig++;
+						if (currentConfig>=myargc)
+							currentConfig=1;
+						CleanScene();
+						LoadConfigFile(myargv[currentConfig]);
+						SetupScene();
+					} else if (state.rAxis[0].y < -0.7 && state.rAxis[0].x > -0.4 && state.rAxis[0].x < 0.4) {
+						//prev config file
+						currentConfig--;
+						if (currentConfig<=0)
+							currentConfig=myargc-1;
+						CleanScene();
+						LoadConfigFile(myargv[currentConfig]);
+						SetupScene();
+					}
+				}
+			}
 			if (!buttonPressed[1][unDevice] && 
 				(state.ulButtonTouched&vr::ButtonMaskFromId(vr::k_EButton_ApplicationMenu) ||
 				state.ulButtonPressed&vr::ButtonMaskFromId(vr::k_EButton_ApplicationMenu))
 				) {
 				buttonPressed[1][unDevice] = true;
-				if (firstdevice == -1)
+				/*if (firstdevice == -1)
 					firstdevice = unDevice;
 				else if(unDevice !=firstdevice && seconddevice==-1)
 					seconddevice=unDevice;
-
+*/
 				if (firstdevice==unDevice)
 					savetodisk = !savetodisk;
 				else
@@ -1009,11 +1051,11 @@ bool CMainApplication::HandleInput()
 				)
 			{
 				buttonPressed[0][unDevice] = true;
-				if (firstdevice == -1)
+				/*if (firstdevice == -1)
 					firstdevice = unDevice;
 				else if(unDevice !=firstdevice && seconddevice==-1)
 					seconddevice=unDevice;
-
+*/
 				if (unDevice == firstdevice) {
 					currentset+=sidebuttontimestep;
 					if (currentset < 0)
@@ -1127,12 +1169,9 @@ void CMainApplication::ProcessVREvent( const vr::VREvent_t & event )
 	}
 }
 
-
 void CMainApplication::HapticFeedback(){
-	if (hapticFeedback) {
-		HapticFeedback(firstdevice);
-		HapticFeedback(seconddevice);
-	}
+	HapticFeedback(firstdevice);
+	HapticFeedback(seconddevice);
 }
 //-----------------------------------------------------------------------------
 // Purpose: Haptic feedback if controller near an atom
@@ -1146,6 +1185,10 @@ void CMainApplication::HapticFeedback(int device)
 		vr::TrackedDevicePose_t dp;
 		m_pHMD->GetControllerStateWithPose( vr::TrackingUniverseStanding, device, &cs, &dp );
 		if (dp.bPoseIsValid) {
+			bool clicked = (cs.ulButtonPressed&vr::ButtonMaskFromId(vr::k_EButton_Axis0) &&
+				(cs.rAxis[0].y > -0.4 && cs.rAxis[0].y < 0.4)) && (cs.rAxis[0].x>0.7);
+			if (!hapticFeedback && !clicked)
+				return;
 			int mycurrentset;
 			if (fixedAtoms)
 				mycurrentset=0;
@@ -1176,7 +1219,11 @@ void CMainApplication::HapticFeedback(int device)
 							pos=Vector3 (pos.x, pos.y, -pos.z);
 							float l=(pos - controllerPos).length();
 							if (l<atomr*atomScaling) {
-								m_pHMD->TriggerHapticPulse(device, 0, 3000);
+								if(clicked) {
+									selectedAtom=i;
+								}
+								if (hapticFeedback)
+									m_pHMD->TriggerHapticPulse(device, 0, 3000);
 								return;
 							}
 						}
@@ -1341,7 +1388,7 @@ bool CMainApplication::CreateAllShaders()
 	}
 
 	if (!PrepareUnitCellAtomShader (&m_unAtomsProgramID, &m_unUnitCellProgramID, &m_unMarkerProgramID,
-		&m_nAtomMatrixLocation, &m_nUnitCellMatrixLocation,  &m_nUnitCellColourLocation, &m_nMarkerMatrixLocation, &m_nTotalatomsLocation))
+		&m_nAtomMatrixLocation, &m_nUnitCellMatrixLocation,  &m_nUnitCellColourLocation, &m_nMarkerMatrixLocation, &m_nTotalatomsLocation, &m_nSelectedAtomLocation))
 		return false;
 
 
@@ -1396,7 +1443,12 @@ bool CMainApplication::CreateAllShaders()
 		&& m_unLensProgramID != 0;
 }
 
-
+void CMainApplication::SetupInfoBoxTexture()
+{
+	for (int i=0;i<info.size();i++) {
+		info[i].tex=LoadPNG(info[i].filename);
+	}
+}
 //-----------------------------------------------------------------------------
 // Purpose:
 //-----------------------------------------------------------------------------
@@ -1422,7 +1474,6 @@ bool CMainApplication::SetupTexturemaps()
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
 	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
-
 	GLenum e;
 	if ((e = glGetError()) != GL_NO_ERROR)
 		dprintf("opengl error %d, SetupTextureMaps 1\n", e);
@@ -1430,27 +1481,17 @@ bool CMainApplication::SetupTexturemaps()
 	e=atomTexture( m_iTexture[3+ZLAYERS]);
 
 	glBindTexture( GL_TEXTURE_2D, 0 );
-
 	if ((e = glGetError()) != GL_NO_ERROR)
 		dprintf("opengl error %d, SetupTextureMaps 2\n", e);
 
-	//sdl text http://stackoverflow.com/questions/5289447/using-sdl-ttf-with-opengl/5289823#5289823
-	//http://stackoverflow.com/questions/28880562/rendering-text-with-sdl2-and-opengl
-	//FIXME TODO
-	/*int size=10;
-	=TTF_OpenFont("", size);
-	axisTextures=new SDL_Texture*[6];
-
-	const wchar_t axis[6]={'a', 'b', 'c', '\u03B1', '\u03B2', '\u03B3'};
-	for (int i=0;i<6;i++) {
-		axisTextures[i]=TTF_RenderUTF8_Blended();
-	}
-	*/
-
-	for (int i=0;i<info.size();i++) {
-		info[i].tex=LoadPNG(info[i].filename);
-	}
-
+	std::string s(myargv[0]);
+	int l=s.find_last_of("\\/");
+	std::string path;
+	if (l==s.npos)
+		path=std::string(NUMBERTEXTURE);
+	else
+		path=s.substr(0, l)+"\\"+NUMBERTEXTURE;
+	numbersTexture=LoadPNG(path.c_str(), nearest);
 	return ( m_iTexture != 0 && e==GL_NO_ERROR);
 }
 
@@ -1464,6 +1505,49 @@ bool CMainApplication::SetupDepthPeeling()
 	return (e);
 }
 
+void CMainApplication::CleanScene()
+{ //delete, opposite order from creation
+	//isos
+	if (ISOS) {
+		for (int i=0;i<NUMLODS;i++) {
+			glDeleteBuffers(NUMPLY, m_glSceneVertBuffer[i]);
+			glDeleteBuffers(NUMPLY, m_unSceneVAOIndices[i]);
+			glDeleteVertexArrays(NUMPLY, m_unSceneVAO[i]);
+			delete[] m_uiVertcount[i];
+			delete[] m_glSceneVertBuffer[i];
+			delete[] m_unSceneVAO[i];
+			delete[] m_unSceneVAOIndices[i];
+			delete[] vertdataarray[i];
+			delete[] vertindicesarray[i];
+		}
+		delete[] m_uiVertcount;
+		delete[] m_glSceneVertBuffer;
+		delete[] m_unSceneVAOIndices;
+		delete[] m_unSceneVAO;
+		delete[] vertdataarray;
+		delete[] vertindicesarray;
+		m_uiVertcount=nullptr;
+		m_glSceneVertBuffer=nullptr;
+		m_unSceneVAOIndices=nullptr;
+		m_unSceneVAO=nullptr;
+		vertdataarray=nullptr;
+		vertindicesarray=nullptr;
+		ISOS=0;
+	}
+	//atoms
+	::CleanAtoms(&m_unAtomVAO, &m_glAtomVertBuffer, &BondIndices);
+	::cleanAtoms(&numAtoms, TIMESTEPS, &atoms);
+	//unit cell
+	::CleanUnitCell(&m_unUnitCellVAO, &m_glUnitCellVertBuffer, &m_glUnitCellIndexBuffer);
+	//marker
+	if (markers) {
+		CleanMarker(&m_unMarkerVAO, &m_glMarkerVertBuffer);
+	}
+	//infocube
+	::CleanInfoCube(&m_unInfoVAO, &m_unInfoVertBuffer, &m_unInfoIndexBuffer);
+	cleanConfig();
+}
+
 //-----------------------------------------------------------------------------
 // Purpose: Load the scene into OpenGL
 //-----------------------------------------------------------------------------
@@ -1477,6 +1561,7 @@ void CMainApplication::SetupScene()
 	SetupMarker();
 	SetupInfoCube();
 	movementspeed/=scaling;
+	SetupInfoBoxTexture();
 }
 
 void CMainApplication::SetupInfoCube()
@@ -1515,7 +1600,195 @@ void CMainApplication::SetupAtoms()
 		dprintf("opengl error %d, SetupAtoms, l %d\n", e, __LINE__);
 }
 
+bool CMainApplication::PrepareControllerGlyph (const vr::Hmd_Eye nEye, const int controller, Vector3* pos)
+{
+Matrix4 & matDeviceToTracking = m_rmat4DevicePose[controller];
+Matrix4 i = matDeviceToTracking;
+*pos= Vector3 (i[12], i[13], i[14]);
+
+Matrix4 trans;
+
+
+Vector3 iPos = (*pos)+Vector3(0,0.02,0); //raise glyph
+
+int e;
+trans.scale(0.05).translate(iPos); //translate(0,0.1,0);
+Matrix4 transform = GetCurrentViewProjectionMatrix(nEye)*trans;
+
+//render point grid
+if ((e = glGetError()) != GL_NO_ERROR)
+	dprintf("Gl error: %d, %s, l %d f %s\n", e, gluErrorString(e), __LINE__, __FUNCTION__);
+glDisable(GL_BLEND);
+//glDisable(GL_DEPTH_TEST);
+if ((e = glGetError()) != GL_NO_ERROR)
+	dprintf("Gl error: %d, %s, l %d f %s\n", e, gluErrorString(e), __LINE__, __FUNCTION__);
+glPointSize(1);
+glUseProgram(m_unRenderModelProgramID);
+glUniformMatrix4fv(m_nRenderModelMatrixLocation, 1, GL_FALSE, transform.get());
+
+return true;
+}
+
+void FillVerticesGlyph (float * const vert, const int i, const float u)
+{
+for (int j=0;j<4;j++) {
+			vert[i*9*4+j*9+2]=0; //z
+			vert[i*9*4+j*9+3]=1; //w
+			vert[i*9*4+j*9+4]=0; //nx
+			vert[i*9*4+j*9+5]=0; //ny
+			vert[i*9*4+j*9+6]=-1; //nz
+		}
+		vert [i*9*4+0*9+0]=i+0; //x
+		vert [i*9*4+0*9+1]=0; //y
+		vert [i*9*4+0*9+7]=u; //u
+		vert [i*9*4+0*9+8]=1; //v
+
+		vert [i*9*4+1*9+0]=i+1; //x
+		vert [i*9*4+1*9+1]=0; //y
+		vert [i*9*4+1*9+7]=u+1.0f/16.0f; //u
+		vert [i*9*4+1*9+8]=1; //v
+
+		vert [i*9*4+2*9+0]=i+0; //x
+		vert [i*9*4+2*9+1]=1; //y
+		vert [i*9*4+2*9+7]=u; //u
+		vert [i*9*4+2*9+8]=0; //v
+
+		vert [i*9*4+3*9+0]=i+1; //x
+		vert [i*9*4+3*9+1]=1; //y
+		vert [i*9*4+3*9+7]=u+1.0f/16.0f; //u
+		vert [i*9*4+3*9+8]=0; //v
+}
+
+short int *FillIndicesGlyph (int l)
+{
+	short int *ind = new short int [6*l];
+	for (int i=0;i<l;i++) {
+		ind[6*i + 0] = i*4 + 0;
+		ind[6*i + 1] = i*4 + 1;
+		ind[6*i + 2] = i*4 + 2;
+
+		ind[6*i + 3] = i*4 + 2;
+		ind[6*i + 4] = i*4 + 3;
+		ind[6*i + 5] = i*4 + 1;
+	}
+	return ind;
+}
+
+void RenderNumbersControllerGlyph (int l, float *vert, short int *ind, GLuint texture)
+{
+	GLuint e;
+	GLuint myvao;
+	glGenVertexArrays(1, &myvao);
+	glBindVertexArray(myvao);
+	GLuint testBuf;
+	glGenBuffers(1, &testBuf);
+	glBindBuffer(GL_ARRAY_BUFFER, testBuf);
+	glBufferData(GL_ARRAY_BUFFER, l * 4 * 9 * sizeof(GLfloat), vert, GL_STATIC_DRAW);
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+
+	delete vert;
+
+	glEnableVertexAttribArray(0);
+	glEnableVertexAttribArray(1);
+	glEnableVertexAttribArray(2);
+	
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)0);
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(4 * sizeof(float)));
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+	glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(7 * sizeof(float)));
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+
+	GLuint indexBufferID;
+	glGenBuffers(1, &indexBufferID);
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);
+	if ((e = glGetError()) != GL_NO_ERROR)
+		dprintf("Gl error after zlayer: %d, %s, l %d\n", e, gluErrorString(e), __LINE__);
+	glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6*l*sizeof(short int), ind, GL_STATIC_DRAW);
+	delete[] ind;
+	glBindTexture(GL_TEXTURE_2D, texture);
+	glDrawElements(GL_TRIANGLES, 6*l, GL_UNSIGNED_SHORT, 0);
+	glDeleteBuffers(1, &testBuf);
+	glDeleteVertexArrays(1, &myvao);
+}
+
+float GetTextureCoordinate (char c)
+{
+	float u;
+			switch (c) {
+				case '-':
+					u=10.0f; break;
+				case '.':
+					u=11.0f; break;
+				case ' ':
+					u=12.0f; break;
+				case 'a':
+					u=14.0f; break;
+				default:
+					u=(float)(c-'0');
+			} //switch
+			u/=16;
+	return u;
+}
+
+void CMainApplication::RenderControllerGlyph (const vr::Hmd_Eye nEye, const int controller)
+{
+	if (selectedAtom==-1)
+		return;
+	if (controller == seconddevice) {
+		Vector3 pos; 
+		PrepareControllerGlyph(nEye, controller, &pos);
+		pos /=scaling;
+		pos-=UserPosition;
+		pos=Vector3(pos[0], -pos[2], pos[1]);
+		
+		pos-=Vector3(atoms[currentset][selectedAtom*4+0], atoms[currentset][selectedAtom*4+1], atoms[currentset][selectedAtom*4+2]);
+
+		char dis [200];
+		sprintf (dis, "%0.2fa", pos.length());
+		int l=strlen (dis);
+		float *vert;
+		vert=new float[l*4*(4+3+2)];
+		for (int i=0;i<l;i++) {
+			float u=GetTextureCoordinate(dis[i]);
+			FillVerticesGlyph (vert, i, u);
+		} //for
+		short int *ind=FillIndicesGlyph(l);
+		RenderNumbersControllerGlyph (l, vert, ind, numbersTexture);
+		return;
+	} // if
+	
+	if (selectedAtom==-1)
+		return;
+
+	Vector3 pos; 
+	PrepareControllerGlyph(nEye, controller, &pos);
 
+	//display atom number
+	char atom [200];
+	sprintf (atom, "%d", selectedAtom+1);
+	//sprintf (atom, "%d %.2f %.2f %.2f", selectedAtom+1, atoms[currentset][selectedAtom*4+0], atoms[currentset][selectedAtom*4+1], atoms[currentset][selectedAtom*4+2]);
+	int l=strlen (atom);
+
+	float *vert;
+	vert=new float[l*4*(4+3+2)];
+	for (int i=0;i<l;i++) {
+		float u=GetTextureCoordinate(atom[i]);
+		FillVerticesGlyph (vert, i, u);
+	}
+
+	short int *ind=FillIndicesGlyph(l);
+	RenderNumbersControllerGlyph (l, vert, ind, numbersTexture);
+
+}
 
 //-----------------------------------------------------------------------------
 // Purpose: Load the isosurfaces into OpenGL
@@ -1524,6 +1797,9 @@ void CMainApplication::SetupIsosurfaces()
 {
 	if (!m_pHMD)
 		return;
+
+	if (ISOS==0)
+		return;
 	//rgh: add scene loading here
 	vertdataarray = new std::vector<float>*[NUMLODS];
 #ifndef INDICESGL32
@@ -1580,7 +1856,7 @@ void CMainApplication::SetupIsosurfaces()
 	glDisable(GL_CULL_FACE);
 	float z = 0.0f; 
 	float points[] = {//pos [4], color [3]
-		0, 0, z, 1, 1,1,1,
+		0, 0, z, 1, 1, 1, 1,
 		0, 1, z, 1, 1, 1, 1,
 		1, 1, z, 1, 1, 1, 1,
 		1, 0, z, 1, 1, 1, 1, };
@@ -1715,8 +1991,6 @@ void CMainApplication::SetupIsosurfaces()
 			if (GL_NO_ERROR!=PrepareGLiso(m_unSceneVAO[currentlod][p], m_glSceneVertBuffer[currentlod][p], 
 				vertdataarray[currentlod][p], m_unSceneVAOIndices[currentlod][p], vertindicesarray[currentlod][p]))
 				eprintf ("PrepareGLiso, GL error");
-			
-
 
 			//FIXME: after we go to 64 bits, keep the data in ram
 			vertdataarray[currentlod][p].clear();
@@ -1724,16 +1998,13 @@ void CMainApplication::SetupIsosurfaces()
 			vertdataarray[currentlod][p].resize(0);
 			vertindicesarray[currentlod][p].resize(0);
 
-
 			if (p % ISOS == ISOS - 1)
 				time++;
-
 		}
 		delete[] vertdataarray[currentlod];
 		delete[] vertindicesarray[currentlod];
 		vertdataarray[currentlod] = nullptr;
 		vertindicesarray[currentlod] = nullptr;
-
 	} // for each lod
 	//glEnable(GL_DEPTH_TEST);
 	if ((e = glGetError()) != GL_NO_ERROR)
@@ -2289,11 +2560,13 @@ if (numAtoms && showAtoms) {
 	//glUniformMatrix4fv(m_nAtomMVLocation, 1, GL_FALSE, mv.get());
 	if ((e = glGetError()) != GL_NO_ERROR)
 		dprintf("Gl error 4 timestep =%d: %d, %s\n", currentset, e, gluErrorString(e));
-	if (currentset==0 ||fixedAtoms)
+	if (currentset==0 ||fixedAtoms) {
+		glUniform1i(m_nSelectedAtomLocation, selectedAtom);
 		glDrawArrays(GL_PATCHES, 0, numAtoms[0]);
-	else
+	} else {
+		glUniform1i(m_nSelectedAtomLocation, selectedAtom+numAtoms[currentset-1]);
 		glDrawArrays(GL_PATCHES, numAtoms[currentset-1], numAtoms[currentset]-numAtoms[currentset-1]);
-	
+	}
 	if ((e = glGetError()) != GL_NO_ERROR)
 		dprintf("Gl error after RenderAtoms timestep =%d: %d, %s\n", currentset, e, gluErrorString(e));
 }
@@ -2473,6 +2746,14 @@ glBindVertexArray(0);
 }
 
 
+void CMainApplication::RenderControllerGlyphs(vr::Hmd_Eye nEye)
+{
+if (firstdevice!=-1)
+	RenderControllerGlyph(nEye, firstdevice);
+if (seconddevice!=-1)
+	RenderControllerGlyph(nEye, seconddevice);
+}
+
 //-----------------------------------------------------------------------------
 // Purpose:
 //-----------------------------------------------------------------------------
@@ -2499,6 +2780,7 @@ void CMainApplication::RenderScene(vr::Hmd_Eye nEye)
 		RenderUnitCell(nEye);
 		if (showcontrollers)
 			RenderAllTrackedRenderModels(nEye);
+		RenderControllerGlyphs(nEye);
 		if (showAtoms) {
 			glDisable(GL_BLEND);
 		}
@@ -2541,6 +2823,7 @@ void CMainApplication::RenderScene(vr::Hmd_Eye nEye)
 				}
 				if (showcontrollers)
 					RenderAllTrackedRenderModels(nEye);
+				RenderControllerGlyphs(nEye);
 			} // for zl
 
 			glBindFramebuffer(GL_FRAMEBUFFER, dfb);
@@ -2639,6 +2922,7 @@ void CMainApplication::RenderScene(vr::Hmd_Eye nEye)
 				RenderInfo(nEye);
 			if (showcontrollers)
 				RenderAllTrackedRenderModels(nEye);
+			RenderControllerGlyphs(nEye);
 		} //else currentiso =isos
 
 
@@ -3058,70 +3342,40 @@ void CGLRenderModel::Draw()
 
 
 
-void cleanConfig()
-{
-	for (int i = 0; i < ISOS; i++) {
-		delete[] isocolours[i];
-		delete[] translations[i];
-	}
-	delete[] isocolours;
-	delete[] translations;
-	if (plyfiles) {
-		for (int i=0;i<ISOS;i++)
-			free ((void*)(plyfiles[i])); //strdup
-		delete[] plyfiles;
-	}
-	
-	free((void*)PATH);
-}
+
 
 //-----------------------------------------------------------------------------
 // Purpose:
 //-----------------------------------------------------------------------------
 char * MainErrors [] = {
 	"No error, successful exit",
-	"Exactly one parameter should be provided, please drag the .ncfg over the exe file",
+	"At least one parameter should be provided, please drag the .ncfg over the exe file",
 	"Out of memory starting application",
 	"Could not init application"
 };
 int main(int argc, char *argv[])
 {
+	//https://stackoverflow.com/questions/8544090/detected-memory-leaks
+	/*_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+	 _CrtSetBreakAlloc(89);
+	 _CrtSetBreakAlloc(88);
+	 _CrtSetBreakAlloc(87);
+	 _CrtSetBreakAlloc(86);
+	 _CrtSetBreakAlloc(85);
+	 _CrtSetBreakAlloc(84);
+	 */
 	TMPDIR=".\\";
 	//http://stackoverflow.com/questions/4991967/how-does-wsastartup-function-initiates-use-of-the-winsock-dll
 	WSADATA wsaData;
     if(WSAStartup(0x202, &wsaData) != 0)
 		return -10;
 	
-	if (argc != 2) {
-		fprintf(stderr, "Use: %s <config file>\n", argv[0]);
+	if (argc < 2) {
+		fprintf(stderr, "Use: %s <config file> [<config file>]*\n", argv[0]);
 		MessageBoxA(0, MainErrors[1], nullptr, 0);
 		return -1;
 	}
 
-	{
-	//change cwd so that relative paths work
-	std::string s(argv[1]);
-	SetCurrentDirectoryA(s.substr(0, s.find_last_of("\\/")).c_str());
-	}
-
-	int r;
-	if ((r=loadConfigFile(argv[1]))<0) {
-		if (-100<r)
-			MessageBoxA(0, loadConfigFileErrors[-r], "Config file reading error", 0);
-		else if (-200<r)
-			MessageBoxA(0, readAtomsXYZErrors[-r-100], "XYZ file reading error", 0);
-		else if (-300<r)
-			MessageBoxA(0, readAtomsCubeErrors[-r-200], "Cube file reading error", 0);
-		else if (-400<r) 
-			MessageBoxA(0, readAtomsJsonErrors[-r-300], "Encyclopedia Json reading error", 0);
-		else
-			MessageBoxA(0, readAtomsAnalyticsJsonErrors[-r-400], "Analytics Json reading error", 0);
-		return -100+r;
-	}
-
-	if (solid)
-		MessageBoxA(0, "Only spheres implemented as atom glyphs in HTC Vive", "Atom Glyph", 0);
-
 	CMainApplication *pMainApplication = new CMainApplication( argc, argv );
 
 	if (pMainApplication == nullptr) {
diff --git a/textures/CONTRIBUTORS b/textures/CONTRIBUTORS
new file mode 100644
index 0000000000000000000000000000000000000000..020d3d87269de58d673fcc55e998de78f7f26fa1
--- /dev/null
+++ b/textures/CONTRIBUTORS
@@ -0,0 +1,4 @@
+digits_64x7_l_blank.png:
+Copyright 2018 Matthias Albert
+Copyright 2018 Ruben Jesus Garcia-Hernandez
+Licensed under the Apache License, Version 2.0
\ No newline at end of file
diff --git a/textures/digits_64x7_l_blank.png b/textures/digits_64x7_l_blank.png
new file mode 100644
index 0000000000000000000000000000000000000000..62b7c3d90cdab94ac873dc1e88f0b520ac664d62
Binary files /dev/null and b/textures/digits_64x7_l_blank.png differ