Index: /trunk/nv/common.hh
===================================================================
--- /trunk/nv/common.hh	(revision 10)
+++ /trunk/nv/common.hh	(revision 11)
@@ -112,4 +112,8 @@
 #define NV_ASSERT(cond, msg) assert( (cond) && msg )
 
+#if NV_COMPILER == NV_MSVC 
+#pragma warning(disable: 4201)
+#endif
+
 namespace nv
 {
Index: /trunk/nv/gl/texture_atlas.hh
===================================================================
--- /trunk/nv/gl/texture_atlas.hh	(revision 11)
+++ /trunk/nv/gl/texture_atlas.hh	(revision 11)
@@ -0,0 +1,44 @@
+// 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
+
+#ifndef NV_GL_TEXTURE_ATLAS_HH
+#define NV_GL_TEXTURE_ATLAS_HH
+
+#include <nv/common.hh>
+#include <glm/glm.hpp>
+#include <vector>
+
+namespace nv
+{
+
+	class texture_atlas
+	{
+	public:
+		texture_atlas( glm::ivec2 size, size_t depth );
+		glm::ivec4 get_region( glm::ivec2 size );
+		void set_region( glm::ivec4 region, const uint8 * data, size_t stride );
+		void clear();
+		void fill( uint8 value );
+		~texture_atlas();
+		const uint8 * get_data() const { return m_data; }
+		const glm::ivec2 get_size() const { return m_size; }
+		const size_t get_depth() const { return m_depth; }
+		const size_t get_used() const { return m_used; }
+	protected:
+		int fit( size_t index, glm::ivec2 size );
+		void merge();
+	private:
+	    glm::ivec2 m_size;
+	    size_t  m_depth;
+	    size_t  m_used;
+
+	    uint8 * m_data;
+		std::vector<glm::ivec3> m_nodes;
+	};
+
+}
+
+#endif // NV_GL_TEXTURE_ATLAS_HH
Index: /trunk/src/gl/texture_atlas.cc
===================================================================
--- /trunk/src/gl/texture_atlas.cc	(revision 11)
+++ /trunk/src/gl/texture_atlas.cc	(revision 11)
@@ -0,0 +1,152 @@
+// 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/gl/texture_atlas.hh"
+
+using namespace nv;
+
+texture_atlas::texture_atlas( glm::ivec2 size, size_t depth )
+	: m_size( size ), m_depth( depth ), m_used( 0 ), m_data( nullptr )
+{
+	m_nodes.push_back( glm::ivec3( 1, 1, m_size.x - 2 ) );
+	m_data = new uint8[ m_size.x * m_size.y * m_depth ];
+	fill( 0 );
+}
+
+glm::ivec4 texture_atlas::get_region( glm::ivec2 size )
+{
+	glm::ivec4 region (0,0,size.x,size.y);
+    size_t i;
+
+    int best_height = INT_MAX;
+    int best_index  = -1;
+    int best_width  = INT_MAX;
+	for( size_t i=0; i < m_nodes.size(); ++i )
+	{
+        int y = fit( i, size );
+		if( y >= 0 )
+		{
+            glm::ivec3 node = m_nodes[ i ];
+			if ( ( (y + size.y) < best_height ) ||
+                ( ((y + size.y) == best_height) && (node.z < best_width)) )
+			{
+				best_height = y + size.y;
+				best_index = i;
+				best_width = node.z;
+				region.x = node.x;
+				region.y = y;
+			}
+        }
+    }
+   
+	if( best_index == -1 )
+    {
+        return glm::ivec4( -1, -1, 0, 0 );
+    }
+
+    m_nodes.insert( m_nodes.begin() + best_index, glm::ivec3( region.x, region.y + size.y, size.x ) );
+
+    for( size_t i = best_index+1; i < m_nodes.size(); ++i )
+    {
+        glm::ivec3 node = m_nodes[ i ];
+        glm::ivec3 prev = m_nodes[ i-1 ];
+
+        if (node.x < (prev.x + prev.z) )
+        {
+            int shrink = prev.x + prev.z - node.x;
+			m_nodes[ i ].x += shrink;
+			m_nodes[ i ].z -= shrink;
+
+            if (m_nodes[ i ].z <= 0)
+            {
+				m_nodes.erase( m_nodes.begin() + i );
+                --i;
+            }
+            else
+            {
+                break;
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+    merge();
+    m_used += size.x * size.y;
+    return region;
+}
+
+void texture_atlas::set_region( glm::ivec4 region, const uint8 * data, size_t stride )
+{
+    for( size_t i = 0; i < region[3]; ++i )
+    {
+        memcpy( m_data+((region.y+i)*m_size.x + region.x ) * m_depth, 
+                data + (i*stride), m_size.x * m_depth );
+    }
+}
+
+int texture_atlas::fit( size_t index, glm::ivec2 size )
+{
+	glm::ivec3 node = m_nodes[ index ];
+
+	if ( (  node.x + size.x ) > ( m_size.x - 1 ) ) 
+	{
+		return -1;
+	}
+
+	int y     = node.y;
+	int wleft = size.x;
+	size_t i  = index;
+
+	while( wleft > 0 )
+	{
+        node = m_nodes[ i ];
+        if( node.y > y )
+        {
+            y = node.y;
+        }
+		if( (y + size.y) > (m_size.y-1) )
+        {
+			return -1;
+        }
+		wleft -= node.z;
+		++i;
+	}
+	return y;
+}
+
+void texture_atlas::merge()
+{
+	for( size_t i=0; i < m_nodes.size()-1; ++i )
+    {
+		if( m_nodes[ i ].y == m_nodes[ i+1 ].y )
+		{
+			m_nodes[ i ].z += m_nodes[ i+1 ].z;
+            m_nodes.erase( m_nodes.begin()+i+1 );
+			--i;
+		}
+    }
+}
+
+void texture_atlas::clear()
+{
+	m_nodes.clear();
+	m_used = 0;
+	m_nodes.push_back( glm::ivec3( 1, 1, m_size.x - 2 ) );
+	fill( 0 );
+}
+
+void texture_atlas::fill( uint8 value )
+{
+	memset( m_data, value, m_size.x * m_size.y * m_depth );
+}
+
+texture_atlas::~texture_atlas()
+{
+	delete[] m_data;
+}
+
