Index: trunk/src/gui/gui_element.cc
===================================================================
--- trunk/src/gui/gui_element.cc	(revision 125)
+++ trunk/src/gui/gui_element.cc	(revision 126)
@@ -27,5 +27,5 @@
 		}
 	}
-	//((environment*)m_root)->update( this, elapsed );
+	((environment*)m_root)->update( this, elapsed );
 }
 
Index: trunk/src/gui/gui_environment.cc
===================================================================
--- trunk/src/gui/gui_environment.cc	(revision 125)
+++ trunk/src/gui/gui_environment.cc	(revision 126)
@@ -7,9 +7,77 @@
 #include "nv/gui/gui_environment.hh"
 
+#include "nv/gui/gui_renderer.hh"
+
+
+	/*
+
+	TODO: parse a lua stylesheet as per Trac wiki
+
+	IDEA: Store everything in std::unordered_maps, with lua_value's?
+
+	A lua_value is a variant stores strings as const char* that deletes them on destructor?
+	Question is that there might not be much gained on speed anyway, due to Lua's speed.
+	Special function field allows delayed per parse execution?
+
+	*/
+
+#include "nv/interface/mesh.hh"
+#include "nv/gfx/cached_buffer.hh"
+#include "nv/gfx/texture_atlas.hh"
+
+#include <vector>
+
 using namespace nv;
 using namespace nv::gui;
 
+environment::environment( window* w )
+	: m_renderer( nullptr ), m_window( w ), m_screen( nullptr )
+{
+	m_area.dim( dimension( w->get_width(), w->get_height() ) );
+	m_screen = new screen( this, m_area );
+	m_renderer = new renderer( w );
+	root::add_child( m_screen );
+}
+
+void nv::gui::environment::load_style( const std::string& filename )
+{
+	m_renderer->load_style( filename );
+}
+
+void environment::update( element* e, uint32 elapsed )
+{
+	if ( e->is_dirty() || e->m_render_data == nullptr )
+	{
+		m_renderer->redraw( e, elapsed );
+		e->set_dirty( false );
+	}
+}
+
 void environment::draw( element* e )
 {
-	
+	m_renderer->draw( e );
 }
+
+void environment::update()
+{
+	m_screen->on_update( 0 );
+}
+
+void environment::draw()
+{
+	m_screen->on_draw();
+	m_renderer->draw();
+}
+
+void environment::add_child( object* child )
+{
+	// TODO: check if element
+	m_screen->add_child( child );
+}
+
+environment::~environment()
+{
+	destroy_children();
+	delete m_renderer;
+}
+
Index: trunk/src/gui/gui_renderer.cc
===================================================================
--- trunk/src/gui/gui_renderer.cc	(revision 126)
+++ trunk/src/gui/gui_renderer.cc	(revision 126)
@@ -0,0 +1,295 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/gui/gui_renderer.hh"
+
+#include <glm/gtc/matrix_transform.hpp>
+
+#include "nv/interface/device.hh"
+#include "nv/interface/context.hh"
+
+using namespace nv;
+using namespace nv::gui;
+
+struct gui_quad
+{
+	vertex vtx[6];
+	gui_quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::vec4& color )
+	{
+		set_color( color );
+		vtx[0].coord = coorda;
+		vtx[1].coord = nv::ivec2( coorda.x, coordb.y );
+		vtx[2].coord = coordb;
+		vtx[3].coord = coordb;
+		vtx[4].coord = nv::ivec2( coordb.x, coorda.y );
+		vtx[5].coord = coorda;
+	}
+	gui_quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::vec4& color, const nv::vec2& tcoorda, const nv::vec2& tcoordb )
+	{
+		set_color( color );
+		vtx[0].coord = coorda;
+		vtx[1].coord = nv::ivec2( coorda.x, coordb.y );
+		vtx[2].coord = coordb;
+		vtx[3].coord = coordb;
+		vtx[4].coord = nv::ivec2( coordb.x, coorda.y );
+		vtx[5].coord = coorda;
+		vtx[0].tcoord = tcoorda;
+		vtx[1].tcoord = nv::vec2( tcoorda.x, tcoordb.y );
+		vtx[2].tcoord = tcoordb;
+		vtx[3].tcoord = tcoordb;
+		vtx[4].tcoord = nv::vec2( tcoordb.x, tcoorda.y );
+		vtx[5].tcoord = tcoorda;
+	}
+	gui_quad( const nv::ivec2& coorda, const nv::ivec2& coordb, const nv::ivec2& coordc, const nv::ivec2& coordd, const nv::vec4& color )
+	{
+		set_color( color );
+		vtx[0].coord = coorda;
+		vtx[1].coord = coordb;
+		vtx[2].coord = coordc;
+		vtx[3].coord = coordc;
+		vtx[4].coord = coordd;
+		vtx[5].coord = coorda;
+	}
+	inline void set_color( const nv::vec4& color )
+	{
+		vtx[0].color = color; vtx[1].color = color;	vtx[2].color = color;
+		vtx[3].color = color; vtx[4].color = color;	vtx[5].color = color;
+	}
+};
+
+
+class screen_render_data : public render_data
+{
+public:
+	screen_render_data( device* dev, size_t initial_size )
+		: buffer( dev, nv::DYNAMIC_DRAW, initial_size ), varray( nullptr ), shader(nullptr), texture(nullptr) 
+	{
+
+	}
+	~screen_render_data() 
+	{ 
+		delete shader; 
+		delete varray; 
+		delete texture;
+	}
+
+	nv::cached_buffer<gui_quad> buffer;
+	nv::vertex_array* varray;
+	nv::program*      shader;
+	nv::texture2d*    texture;
+
+	int loc_coord;
+	int loc_color;
+	int loc_tcoord;
+};
+
+class element_render_data : public render_data
+{
+public:
+	element_render_data( nv::cached_buffer<gui_quad>* cbuffer )
+		: buffer( cbuffer ) {}
+
+	nv::buffer_slice< gui_quad > buffer;
+};
+
+renderer::renderer( window* w )
+	: m_window(w)
+	, m_style()
+	, m_atlas( glm::ivec2( 1024, 1024 ), 4 )
+	, m_reupload( true )
+{
+	m_area.dim( dimension( w->get_width(), w->get_height() ) );
+	region white = m_atlas.get_region( ivec2(3,3) );
+	size_t wsize = m_atlas.get_depth()*4*4;
+	uint8* wfill = new uint8[m_atlas.get_depth()*4*4];
+	std::fill( wfill, wfill + wsize, 255 );
+	white.pos = ivec2();
+	m_atlas.set_region( white, wfill );
+	delete wfill;
+
+	screen_render_data* sr = new screen_render_data( w->get_device(), 1024 );
+	m_render_data = sr;
+	// ** EXTREMELY TEMPORARY!
+	sr->varray     = m_window->get_device()->create_vertex_array();
+	sr->shader     = m_window->get_device()->create_program( nv::slurp( "gui.vert" ), nv::slurp( "gui.frag" ) );
+	sr->loc_coord  = sr->shader->get_attribute("coord")->get_location();
+	sr->loc_tcoord = sr->shader->get_attribute("tcoord")->get_location();
+	sr->loc_color  = sr->shader->get_attribute("color")->get_location();
+	sr->shader->set_uniform( "tex", 0 );
+	glm::mat4 projection = glm::ortho( 0.0f, float( m_window->get_width() ), float( m_window->get_height() ), 0.0f, -1.0f, 1.0f );
+	sr->shader->set_uniform( "projection", projection );
+
+	vertex_buffer* vb = (vertex_buffer*)sr->buffer.get_buffer();
+	sr->varray->add_vertex_buffer( sr->loc_coord,  vb, nv::INT,   2, 0, sizeof( vertex ), false );
+	sr->varray->add_vertex_buffer( sr->loc_tcoord, vb, nv::FLOAT, 2, offset_of( &vertex::tcoord ), sizeof( vertex ), false );
+	sr->varray->add_vertex_buffer( sr->loc_color,  vb, nv::FLOAT, 4, offset_of( &vertex::color ), sizeof( vertex ), false );
+
+	nv::sampler sampler( nv::sampler::LINEAR, nv::sampler::CLAMP_TO_EDGE );
+	sr->texture = m_window->get_device()->create_texture2d( m_atlas.get_size(), nv::RGBA, nv::UBYTE, sampler, nullptr );
+
+	m_render_state.depth_test.enabled = false;
+	m_render_state.culling.enabled    = false;
+	m_render_state.blending.enabled   = true;
+	m_render_state.blending.src_rgb_factor   = blending::SRC_ALPHA;
+	m_render_state.blending.dst_rgb_factor   = blending::ONE_MINUS_SRC_ALPHA;
+	m_render_state.blending.src_alpha_factor = blending::SRC_ALPHA;
+	m_render_state.blending.dst_alpha_factor = blending::ONE_MINUS_SRC_ALPHA;
+}
+
+texture_font* renderer::get_font( size_t name ) const
+{
+	if ( name >= m_fonts.size() ) return nullptr;
+	return m_fonts[ name ];
+}
+
+nv::vec4 renderer::get_image( size_t name ) const
+{
+	if ( name >= m_images.size() ) return nv::vec4();
+	return m_images[ name ];
+}
+
+size_t renderer::load_font( const std::string& filename, size_t size )
+{
+	std::string id_name( filename );
+	id_name.append( to_string( size ) );
+	auto i = m_font_names.find( id_name );
+	if ( i != m_font_names.end() )
+	{
+		return i->second;
+	}
+	size_t result = (size_t)m_fonts.size();
+	texture_font* f = new texture_font( &m_atlas, filename.c_str(), (float)size );
+	f->load_glyphs( "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ " );
+	m_fonts.push_back( f );
+	m_reupload = true;
+	m_font_names[ id_name ] = result;
+	return result;
+}
+
+size_t renderer::load_image( const std::string& filename )
+{
+	auto i = m_image_names.find( filename );
+	if ( i != m_image_names.end() )
+	{
+		return i->second;
+	}
+	size_t result = m_images.size();
+	image_data* data = m_window->get_device()->create_image_data( filename );
+	// TODO: Repitching
+	assert( data->get_depth() == 4 );
+	region r = m_atlas.get_region( data->get_size() );
+	m_atlas.set_region( r, data->get_data() );
+	delete data;
+	m_reupload = true;
+	m_image_names[ filename ] = result;
+	return result;
+}
+
+void renderer::load_style( const std::string& filename )
+{
+	m_style.load_style( filename );
+}
+
+void renderer::redraw( element* e, uint32 )
+{
+	screen_render_data* sr = (screen_render_data*)m_render_data;
+	if ( e->m_render_data == nullptr )
+	{
+		e->m_render_data = new element_render_data( &sr->buffer );
+	}
+	element_render_data* er = (element_render_data*)(e->m_render_data);
+	size_t size_before = er->buffer.data().size();
+
+	std::vector< gui_quad >& qvec = er->buffer.lock();
+
+	qvec.clear();
+	rectangle abs = e->get_absolute();
+	if ( e->get_absolute() != m_area )
+	{
+		int border;
+		vec4 color;
+		std::string path;
+		std::string text;
+		if ( m_style.get( e, "border", border ) && m_style.get( e, "border_color", color ) )
+		{
+			rectangle inner = abs.shrinked( border );
+			qvec.emplace_back( abs.ul, inner.ul, inner.ur(), abs.ur(), color );
+			qvec.emplace_back( abs.ul, abs.ll(), inner.ll(), inner.ul, color );
+			qvec.emplace_back( inner.ur(), inner.lr, abs.lr, abs.ur(), color );
+			qvec.emplace_back( inner.ll(), abs.ll(), abs.lr, inner.lr, color );
+			abs = inner;
+		}
+
+		if ( m_style.get( e, "background_color", color ) )
+		{
+			qvec.emplace_back( abs.ul, abs.lr, color );
+		}
+
+		text = e->get_text();
+		if ( !text.empty() )
+		{
+			if ( m_style.get( e, "text_color", color ) && m_style.get( e, "text_font", path ) && m_style.get( e, "text_size", border ) )
+			{
+				size_t font_id = load_font( path, (uint16)border );
+				texture_font* font = get_font( font_id );
+				position p = abs.ul + position( 0, border );
+				for ( char c : text )
+				{
+					const texture_glyph* g = font->get_glyph( static_cast<uint16>(c) );
+					if (g)
+					{
+						position gp = position( g->offset.x, -g->offset.y );
+						position p2 = p + g->size + gp;
+						qvec.emplace_back( p + gp, p2, color, g->tl, g->br );
+						p += g->advance;
+					}
+				}
+			}
+		}
+	}
+
+	if ( size_before != er->buffer.data().size() )
+	{
+		sr->buffer.reset();
+	}
+}
+
+void renderer::draw( element* e )
+{
+	element_render_data* er = (element_render_data*)(e->m_render_data);
+	er->buffer.commit();
+}
+
+void renderer::draw()
+{
+	screen_render_data* sr = (screen_render_data*)m_render_data;
+
+	if ( m_reupload )
+	{
+		sr->texture->assign( (void*)m_atlas.get_data() );
+		m_reupload = false;
+	}
+
+	if ( sr->buffer.commit() )
+	{
+		nv::vertex_buffer* vb = (nv::vertex_buffer*)sr->buffer.get_buffer();
+		sr->varray->update_vertex_buffer( sr->loc_coord,  vb, false );
+		sr->varray->update_vertex_buffer( sr->loc_tcoord, vb, false );
+		sr->varray->update_vertex_buffer( sr->loc_color,  vb, false );
+	}
+	sr->texture->bind( 0 );
+	sr->shader->set_uniform( "tex", 0 );
+	m_window->get_context()->draw( TRIANGLES, m_render_state, sr->shader, sr->varray, sr->buffer.get_size() * 6 );
+}
+
+renderer::~renderer()
+{
+	for ( auto p : m_fonts )
+	{
+		delete p;
+	}
+	delete m_render_data;
+}
Index: trunk/src/io/c_file_system.cc
===================================================================
--- trunk/src/io/c_file_system.cc	(revision 125)
+++ trunk/src/io/c_file_system.cc	(revision 126)
@@ -32,5 +32,5 @@
 stream* c_file_system::open( const char* fpath, const char* fmode /*= "rb" */ )
 {
-	NV_ASSERT( fpath != nullptr && fpath != "" && fmode != nullptr );
+	NV_ASSERT( fpath != nullptr && fpath != "" && fmode != nullptr, "Bad parameters passed to open" );
 	FILE* file = ::fopen( fpath, fmode );
 	if ( !file )
