Index: trunk/nv/ecs/field_detection.hh
===================================================================
--- trunk/nv/ecs/field_detection.hh	(revision 552)
+++ trunk/nv/ecs/field_detection.hh	(revision 552)
@@ -0,0 +1,196 @@
+// Copyright (C) 2016-2017 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file field_detection.hh
+* @author Kornel Kisielewicz epyon@chaosforge.org
+* @brief Compile time field detection for nv ecs
+*/
+
+#ifndef NV_ECS_FIELD_DETECTION_HH
+#define NV_ECS_FIELD_DETECTION_HH
+
+namespace nv
+{
+	namespace lua
+	{
+		class stack_proxy;
+	}
+
+	namespace ecs
+	{
+
+		namespace detail
+		{
+			template<typename, typename T>
+			struct has_message
+			{
+				static_assert( nv::integral_constant<T, false>::value, "Second template parameter needs to be of function type." );
+			};
+
+			template< typename C, typename Ret, typename... Args >
+			struct has_message<C, Ret( Args... )>
+			{
+			private:
+				template<typename T>
+				static constexpr auto check( T* )
+					-> typename nv::is_same< decltype( nv::declval<T>().on( nv::declval<Args>()... ) ), Ret >::type;
+
+				template<typename>
+				static constexpr nv::false_type check( ... );
+
+			public:
+				typedef decltype( check<C>( 0 ) ) type;
+
+				static constexpr bool value = type::value;
+			};
+
+			template< typename, typename T >
+			struct has_update
+			{
+				static_assert( nv::integral_constant<T, false>::value, "Second template parameter needs to be of function type." );
+			};
+
+			template< typename C, typename Ret, typename... Args >
+			struct has_update<C, Ret( Args... )>
+			{
+			private:
+				template<typename T>
+				static constexpr auto check( T* )
+					-> typename nv::is_same< decltype( nv::declval<T>().update( nv::declval<Args>()... ) ), Ret >::type;
+
+				template<typename>
+				static constexpr nv::false_type check( ... );
+			public:
+				typedef decltype( check<C>( 0 ) ) type;
+				static constexpr bool value = type::value;
+			};
+
+			template<typename, typename T>
+			struct has_destroy
+			{
+				static_assert( nv::integral_constant<T, false>::value, "Second template parameter needs to be of function type." );
+			};
+
+			template< typename C, typename Ret, typename... Args >
+			struct has_destroy<C, Ret( Args... )>
+			{
+			private:
+				template<typename T>
+				static constexpr auto check( T* )
+					-> typename nv::is_same< decltype( nv::declval<T>().destroy( nv::declval<Args>()... ) ), Ret >::type;
+
+				template<typename>
+				static constexpr nv::false_type check( ... );
+			public:
+				typedef decltype( check<C>( 0 ) ) type;
+				static constexpr bool value = type::value;
+			};
+
+			template<typename, typename T>
+			struct has_create
+			{
+				static_assert( nv::integral_constant<T, false>::value, "Second template parameter needs to be of function type." );
+			};
+
+			template< typename C, typename Ret, typename... Args >
+			struct has_create<C, Ret( Args... )>
+			{
+			private:
+				template<typename T>
+				static constexpr auto check( T* )
+					-> typename nv::is_same< decltype( nv::declval<T>().create( nv::declval<Args>()... ) ), Ret >::type;
+
+				template<typename>
+				static constexpr nv::false_type check( ... );
+			public:
+				typedef decltype( check<C>( 0 ) ) type;
+				static constexpr bool value = type::value;
+			};
+
+			template < typename S, typename T, typename Cs >
+			struct has_ct_update_helper;
+
+			template < typename S, typename T, typename... Cs >
+			struct has_ct_update_helper< S, T, mpl::list< Cs... > >
+			{
+				using type = detail::has_update<S, void( Cs&..., T ) >;
+			};
+
+			template < typename S, typename E, typename T, typename Cs >
+			struct has_ect_update_helper;
+
+			template < typename S, typename E, typename T, typename... Cs >
+			struct has_ect_update_helper< S, E, T, mpl::list< Cs... > >
+			{
+				using type = detail::has_update<S, void( E&, Cs&..., T ) >;
+			};
+
+			template < typename S, typename E, typename M, typename Cs >
+			struct has_ec_message_helper;
+
+			template < typename S, typename E, typename M, typename... Cs >
+			struct has_ec_message_helper< S, E, M, mpl::list< Cs... > >
+			{
+				using type = detail::has_message<S, void( const M&, E&, Cs&... ) >;
+			};
+
+			template < typename S, typename M, typename Cs >
+			struct has_c_message_helper;
+
+			template < typename S, typename M, typename... Cs >
+			struct has_c_message_helper< S, M, mpl::list< Cs... > >
+			{
+				using type = detail::has_message<S, void( const M&, Cs&... ) >;
+			};
+
+		}
+
+		template < typename S, typename M >
+		using has_message = detail::has_message<S, void( const M& ) >;
+
+		template < typename C >
+		struct has_components
+		{
+		private:
+			template < typename T >
+			static true_type  test( typename T::components* );
+			template < typename >
+			static false_type test( ... );
+		public:
+			typedef decltype( test<C>( 0 ) ) type;
+			static constexpr bool value = type::value;
+		};
+
+		template < typename E, typename S, typename T >
+		using has_ecs_update = detail::has_update<S, void( E&, T ) >;
+
+		template < typename S, typename T >
+		using has_destroy = detail::has_destroy<S, void( T& ) >;
+
+		template < typename S, typename T, typename H >
+		using has_create = detail::has_create<S, void( H, T&, const lua::stack_proxy& ) >;
+
+		template < typename S, typename Cs, typename T >
+		using has_component_update = typename detail::has_ct_update_helper<S, T, Cs >::type;
+
+		template < typename E, typename S, typename Cs, typename T >
+		using has_ecs_component_update = typename detail::has_ect_update_helper<S, E, T, Cs >::type;
+
+		template < typename S, typename Cs, typename M >
+		using has_component_message = typename detail::has_c_message_helper<S, M, Cs >::type;
+
+		template < typename E, typename S, typename Cs, typename M >
+		using has_ecs_component_message = typename detail::has_ec_message_helper<S, E, M, Cs >::type;
+
+		template < typename E, typename S, typename M >
+		using has_ecs_message = detail::has_message<S, void( const M&, E& ) >;
+
+	}
+
+}
+
+#endif // NV_ECS_FIELD_DETECTION_HH
Index: trunk/nv/engine/decal_group.hh
===================================================================
--- trunk/nv/engine/decal_group.hh	(revision 552)
+++ trunk/nv/engine/decal_group.hh	(revision 552)
@@ -0,0 +1,67 @@
+// Copyright (C) 2016-2017 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#ifndef NV_ENGINE_DECAL_GROUP
+#define NV_ENGINE_DECAL_GROUP
+
+#include <nv/common.hh>
+#include <nv/stl/math.hh>
+#include <nv/stl/vector.hh>
+#include <nv/stl/handle.hh>
+#include <nv/interface/context.hh>
+#include <nv/stl/handle_store.hh>
+
+namespace nv
+{
+
+	struct decal_data
+	{
+		mat4 model;
+		vec3 center;
+	};
+
+	struct decal_block_data
+	{
+		mat4 model;
+		vec2 tca;
+		vec2 tcb;
+	};
+
+	struct decal_group_info
+	{
+		uint32                count;
+		uint32                quota;
+		vertex_array          vtx_array;
+		buffer                vtx_buffer;
+		bool                  local;
+		decal_data*        quads;
+		uint32                ref_counter;
+	};
+
+	struct decal_group_tag {};
+	typedef handle< uint32, 16, 16, decal_group_tag > decal_group;
+
+	class decal_group_manager
+	{
+	public:
+		decal_group_manager( context* a_context );
+		decal_group create_group( uint32 max_particles );
+		void release( decal_group group );
+
+		bool ref( decal_group group );
+		bool unref( decal_group group );
+		void clear();
+	protected:
+		void release( decal_group_info* info );
+
+		context* m_context;
+		handle_store< decal_group_info, decal_group > m_groups;
+
+	};
+
+}
+
+#endif // NV_ENGINE_DECAL_GROUP
Index: trunk/nv/formats/ndfm_loader.hh
===================================================================
--- trunk/nv/formats/ndfm_loader.hh	(revision 552)
+++ trunk/nv/formats/ndfm_loader.hh	(revision 552)
@@ -0,0 +1,107 @@
+// Copyright (C) 2015-2017 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#ifndef NV_FORMATS_NDFM_LOADER_HH
+#define NV_FORMATS_NDFM_LOADER_HH
+
+#include <nv/common.hh>
+#include <nv/interface/mesh_loader.hh>
+#include <nv/interface/mesh_data.hh>
+#include <nv/stl/vector.hh>
+#include <nv/stl/string_table.hh>
+
+namespace nv
+{
+
+	struct ndf_header
+	{
+		uint32  id;
+		uint32  version;
+	};
+
+	struct ndf_element
+	{
+		shash64 name;
+		shash64 type;
+		uint32  flags;
+		uint32  size;
+		uint16  attributes;
+		uint16  children;
+	};
+
+	enum class ndf_datatype : uint16
+	{
+		STRING,
+		INTEGER,
+		FLOAT
+	};
+
+	struct ndf_attribute
+	{
+		shash64      key;
+		ndf_datatype type;
+		uint8        data[22];
+	};
+
+	static_assert( sizeof( ndf_attribute ) == 32, "alignment issue!" );
+
+	struct ndfm_node_data
+	{
+		mat4     transform;
+		sint16   parent_id;
+	};
+
+	struct ndfm_animation_data
+	{
+		uint16 frame_rate;
+		uint16 frame_count;
+	};
+
+	struct ndfm_channel_header
+	{
+		data_descriptor format;
+		uint32          count;
+	};
+
+#if 0
+
+	class ndfm_loader : public mesh_loader
+	{
+	public:
+		explicit ndfm_loader( string_table* strings ) : mesh_loader( strings ), m_node_data( nullptr ) {}
+		virtual bool load( stream& source );
+		virtual data_channel_set* release_mesh_data( uint32 index = 0 );
+		virtual uint32 get_nodes_data_count() const { return 1; }
+		virtual mesh_nodes_data* release_mesh_nodes_data( uint32 = 0 );
+		virtual data_node_list* release_data_node_list( uint32 = 0 );
+		virtual bool is_animated( uint32 = 0 );
+		virtual uint32 get_mesh_count() const { return m_meshes.size(); }
+		virtual ~ndfm_loader();
+	private:
+		void reset();
+		void skip_attributes( stream& source, uint32 count );
+		bool load_mesh( stream& source, const ndf_element& e );
+		bool load_strings( stream& source );
+		bool load_animation( stream& source, const ndf_element& e );
+		bool load_channel( stream& source, data_channel_set* channel_set );
+		bool load_channel_set( stream& source, data_channel_set* channel_set, const ndf_element& e );
+
+		mesh_nodes_data*            m_node_data;
+		vector< data_channel_set* > m_meshes;
+	};
+
+#endif
+	// HACK : TEMPORARY - will go to it's own file, probably nmd_io
+	void ndfm_dump_header( stream& stream_out, uint16 elements, shash64 name );
+	void ndfm_dump_strings( stream& stream_out, const string_table& strings );
+	void ndfm_dump_element( stream& stream_out, const data_channel_set& data, shash64 type );
+	void ndfm_dump_animation( stream& stream_out, const mesh_nodes_data& nodes );
+	void ndfm_dump_bones( stream& stream_out, const data_node_list& bones );
+
+
+}
+
+#endif // NV_FORMATS_NDFM_LOADER_HH
Index: trunk/nv/gui/gui_menu.hh
===================================================================
--- trunk/nv/gui/gui_menu.hh	(revision 552)
+++ trunk/nv/gui/gui_menu.hh	(revision 552)
@@ -0,0 +1,51 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+ * @file gui_menu.hh
+ * @author Kornel Kisielewicz
+ * @brief GUI menu
+ */
+
+#ifndef NV_GUI_MENU_HH
+#define NV_GUI_MENU_HH
+
+#include <nv/common.hh>
+#include <nv/stl/handle.hh>
+#include <nv/stl/flags.hh>
+#include <nv/gui/gui_common.hh>
+#include <nv/gui/gui_environment.hh>
+
+namespace nv
+{
+
+	namespace gui
+	{
+
+		template < typename T = void*>
+		struct menu_option
+		{
+			string_view name;
+			string_view klass;
+			T data;
+			flags f;
+		};
+
+		template < typename T = void* >
+		inline handle create_menu( environment* env, rectangle r, handle parent, 
+			array_view< menu_option > options, 
+			const function< void( T ) >& on_activate,
+			const function< void( T ) >& on_select
+			)
+		{
+			handle result = env->create_element( parent, r );
+
+		}
+
+	}
+}
+
+#endif // NV_GUI_MENU_HH
Index: trunk/nv/stl/filesystem.hh
===================================================================
--- trunk/nv/stl/filesystem.hh	(revision 552)
+++ trunk/nv/stl/filesystem.hh	(revision 552)
@@ -0,0 +1,139 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file filesystem.hh
+* @author Kornel Kisielewicz epyon@chaosforge.org
+* @brief filesystem library
+*/
+
+#ifndef NV_STL_FILESYSTEM_HH
+#define NV_STL_FILESYSTEM_HH
+
+#include <nv/common.hh>
+#include <nv/stl/utility.hh>
+
+namespace nv
+{
+
+	enum class file_type
+	{
+		NOT_FOUND = -1,
+		NONE      = 0,
+		REGULAR   = 1,
+		DIRECTORY = 2,
+		SYMLINK   = 3,
+		BLOCK     = 4,
+		CHARACTER = 5,
+		FIFO      = 6,
+		SOCKET    = 7,
+		UNKNOWN   = 8
+	};
+
+	enum class perms
+	{
+		NONE             = 0,
+		OWNER_READ       = 0400, // S_IRUSR
+		OWNER_WRITE      = 0200, // S_IWUSR
+		OWNER_EXEC       = 0100, // S_IXUSR
+		OWNER_ALL        = 0700, // S_IRWXU
+		GROUP_READ       = 040,  // S_IRGRP
+		GROUP_WRITE      = 020,  // S_IWGRP
+		GROUP_EXEC       = 010,  // S_IXGRP
+		GROUP_ALL        = 070,  // S_IRWXG
+		OTHERS_READ      = 04,   // S_IROTH
+		OTHERS_WRITE     = 02,   // S_IWOTH
+		OTHERS_EXEC      = 01,   // S_IXOTH
+		OTHERS_ALL       = 07,   // S_IRWXO
+		ALL              = 0777,
+
+		SET_UID          = 04000,// S_ISUID
+		SET_GID          = 02000,// S_ISGID
+		STICKY_BIT       = 01000,// S_ISVTX
+		MASK             = 07777,
+
+		UNKNOWN          = 0xFFFF,
+		ADD_PERMS        = 0x10000,
+		REMOVE_PERMS     = 0x20000,
+		RESOLVE_SYMLINKS = 0x40000
+	};
+
+	inline perms& operator&=( perms& lhs, perms rhs ) { lhs = perms( static_cast<int>( lhs ) & static_cast<int>(rhs) ); return lhs; }
+	inline perms& operator|=( perms& lhs, perms rhs ) { lhs = perms( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); return lhs; }
+	inline perms& operator^=( perms& lhs, perms rhs ) { lhs = perms( static_cast<int>( lhs ) ^ static_cast<int>( rhs ) ); return lhs; }
+	inline constexpr perms operator&( perms lhs, perms rhs ) { return perms( static_cast<int>( lhs ) & static_cast<int>( rhs ) ); }
+	inline constexpr perms operator|( perms lhs, perms rhs ) { return perms( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); }
+	inline constexpr perms operator^( perms lhs, perms rhs ) { return perms( static_cast<int>( lhs ) ^ static_cast<int>( rhs ) ); }
+	inline constexpr perms operator~( perms lhs ) { return perms( ~static_cast<int>( lhs ) ); }
+
+	enum class copy_options
+	{
+		NONE               = 0,
+		SKIP_EXISTING      = 1,
+		OVERWRITE_EXISTING = 2,
+		UPDATE_EXISTING    = 4,
+		RECURSIVE          = 8,
+		COPY_SYMLINKS      = 16,
+		SKIP_SYMLINKS      = 32,
+		DIRECTORIES_ONLY   = 64,
+		CREATE_SYMLINKS    = 128,
+	};
+
+	inline copy_options& operator&=( copy_options& lhs, copy_options rhs ) { lhs = copy_options( static_cast<int>( lhs ) & static_cast<int>( rhs ) ); return lhs; }
+	inline copy_options& operator|=( copy_options& lhs, copy_options rhs ) { lhs = copy_options( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); return lhs; }
+	inline copy_options& operator^=( copy_options& lhs, copy_options rhs ) { lhs = copy_options( static_cast<int>( lhs ) ^ static_cast<int>( rhs ) ); return lhs; }
+	inline constexpr copy_options operator&( copy_options lhs, copy_options rhs ) { return copy_options( static_cast<int>( lhs ) & static_cast<int>( rhs ) ); }
+	inline constexpr copy_options operator|( copy_options lhs, copy_options rhs ) { return copy_options( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); }
+	inline constexpr copy_options operator^( copy_options lhs, copy_options rhs ) { return copy_options( static_cast<int>( lhs ) ^ static_cast<int>( rhs ) ); }
+	inline constexpr copy_options operator~( copy_options lhs ) { return copy_options( ~static_cast<int>( lhs ) ); }
+
+	enum class directory_options
+	{
+		NONE                     = 0,
+		FOLLOW_DIRECTORY_SYMLINK = 1,
+		SKIP_PERMISSION_DENIED   = 2,
+	};
+
+	inline directory_options& operator&=( directory_options& lhs, directory_options rhs ) { lhs = directory_options( static_cast<int>( lhs ) & static_cast<int>( rhs ) ); return lhs; }
+	inline directory_options& operator|=( directory_options& lhs, directory_options rhs ) { lhs = directory_options( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); return lhs; }
+	inline directory_options& operator^=( directory_options& lhs, directory_options rhs ) { lhs = directory_options( static_cast<int>( lhs ) ^ static_cast<int>( rhs ) ); return lhs; }
+	inline constexpr directory_options operator&( directory_options lhs, directory_options rhs ) { return directory_options( static_cast<int>( lhs ) & static_cast<int>( rhs ) ); }
+	inline constexpr directory_options operator|( directory_options lhs, directory_options rhs ) { return directory_options( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); }
+	inline constexpr directory_options operator^( directory_options lhs, directory_options rhs ) { return directory_options( static_cast<int>( lhs ) ^ static_cast<int>( rhs ) ); }
+	inline constexpr directory_options operator~( directory_options lhs ) { return directory_options( ~static_cast<int>( lhs ) ); }
+
+	struct space_info
+	{
+		uintmax capacity;
+		uintmax free;
+		uintmax available;
+	};
+
+	class file_status
+	{
+	public:
+		explicit file_status( file_type ftype = file_type::NONE, perms p = perms::UNKNOWN )
+			: m_ftype( ftype ), m_perms( p )
+		{
+		}
+
+		file_status( const file_status& ) = default;
+		file_status& operator=( const file_status& ) = default;
+		file_status( file_status&& ) = default;
+		file_status& operator=( file_status&& ) = default;
+
+		file_type type() const { return m_ftype;  }
+		perms permissions() const { return m_perms; }
+		void type( file_type ftype ) { m_ftype = ftype; }
+		void permissions( perms p ) { m_perms = p; }
+	private:
+		file_type m_ftype;
+		perms m_perms;
+	};
+
+}
+
+#endif // NV_STL_FILESYSTEM_HH
Index: trunk/nv/stl/functional/function.hh
===================================================================
--- trunk/nv/stl/functional/function.hh	(revision 552)
+++ trunk/nv/stl/functional/function.hh	(revision 552)
@@ -0,0 +1,388 @@
+// Copyright (C) 2015 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file function.hh
+* @author Kornel Kisielewicz epyon@chaosforge.org
+* @brief function class
+*/
+
+#ifndef NV_STL_FUNCTIONAL_FUNCTION_HH
+#define NV_STL_FUNCTIONAL_FUNCTION_HH
+
+#include <nv/stl/functional/common.hh>
+#include <nv/stl/functional/mem_fn.hh>
+#include <nv/stl/functional/reference.hh>
+#include <nv/stl/type_traits/common.hh>
+#include <nv/stl/utility/common.hh>
+
+namespace nv
+{
+
+	template < typename Signature >
+	class function;
+
+	namespace detail
+	{
+		template < typename T >
+		struct cast_type_wrapper
+		{
+			cast_type_wrapper( T v ) : value( v ) {}
+			T value;
+		};
+
+		// TODO : call conventions?
+		template < typename F > 
+		inline F& callable_functor( F& f ) { return f; }
+		template < typename C, typename T > 
+		inline detail::mem_fn_impl< T C::* > callable_functor( T C::* &f ) { return mem_fn(f); }
+		template < typename C, typename T >
+		inline detail::mem_fn_impl< T C::* const > callable_functor( T C::* const &f ) { return mem_fn( f ); }
+		template < typename C, typename T > 
+		inline detail::mem_fn_impl< T C::* volatile > callable_functor( T C::* volatile &f ) { return mem_fn(f); }
+		template < typename C, typename T >
+		inline detail::mem_fn_impl< T C::* const volatile > callable_functor( T C::* const volatile &f ) { return mem_fn( f ); }
+
+		template < typename T >
+		struct is_location_invariant 
+			: is_trivially_copyable<T> {};
+
+		template < typename T >
+		struct is_location_invariant< cast_type_wrapper< T > >
+			: is_location_invariant< T >{};
+
+		class dummy_class;
+
+#if NV_COMPILER == NV_MSVC 
+#pragma warning( push )
+#pragma warning( disable : 4121 ) 
+#endif		
+		union function_storage
+		{
+			void* dummy_object;
+			const void* dummy_const_object;
+			void* (*dummy_function_pointer)();
+			void (dummy_class::*dummy_member_pointer)();
+		};
+#if NV_COMPILER == NV_MSVC 
+#pragma warning( pop )
+#endif	
+
+		union any_data
+		{
+			void*       access()       { return &raw_data[0]; }
+			const void* access() const { return &raw_data[0]; }
+
+			template < typename T >
+			T& access() { return *static_cast<T*>( access() ); }
+
+			template < typename T >
+			const T& access() const { return *static_cast<const T*>( access() ); }
+
+			function_storage unused;
+			char raw_data[sizeof( function_storage )];
+		};
+
+		template < typename Signature >
+		constexpr bool function_not_empty( const function< Signature >& f )
+		{
+			return static_cast<bool>( f );
+		}
+
+		template < typename T >
+		constexpr bool function_not_empty( const T*& f )
+		{
+			return f != 0;
+		}
+
+		template < typename C, typename T >
+		constexpr bool function_not_empty( T C::* const& f )
+		{
+			return f != 0;
+		}
+
+		template < typename T >
+		constexpr bool function_not_empty( const T& )
+		{
+			return true;
+		}
+
+		enum operation
+		{
+			op_clone_functor,
+			op_destroy_functor,
+		};
+
+		template <
+			typename F,
+			bool local = is_location_invariant< F >::value && sizeof( F ) <= sizeof( function_storage )
+		>
+		struct function_storage_policy;
+
+		template < typename F >
+		struct function_storage_policy< F, true >
+		{
+			static F* get_pointer( const any_data& src )
+			{
+				const F* ptr = addressof( src.access<F>() );
+				return const_cast<F*>( ptr );
+			}
+
+			static bool execute_op( any_data& dest, const any_data& src, operation op )
+			{
+				switch ( op )
+				{
+				case op_clone_functor:
+					new ( dest.access() ) F( src.access<F>() );
+					break;
+				case op_destroy_functor:
+					dest.access<F>().~F();
+					break;
+				default:
+					break;
+				}
+				return false;
+			}
+
+			static void init_functor( any_data& fdata, F&& f )
+			{
+				new ( fdata.access() ) F( move( f ) );
+			}
+
+		};
+
+		template < typename F >
+		struct function_storage_policy< F, false >
+		{
+			static F* get_pointer( const any_data& src )
+			{
+				const F* ptr = src.access<F*>();
+				return const_cast<F*>( ptr );
+			}
+
+			static bool execute_op( any_data& dest, const any_data& src, operation op )
+			{
+				switch ( op )
+				{
+				case op_clone_functor:
+					dest.access<F*>() = new F( *src.access<F*>() );
+					break;
+				case op_destroy_functor:
+					delete dest.access<F*>();
+					break;
+				default:
+					break;
+				}
+				return false;
+			}
+
+			static void init_functor( any_data& fdata, F&& f )
+			{
+				fdata.access<F*>() = new F( move( f ) );
+			}
+
+		};
+
+		template < typename F >
+		struct function_reference_storage_policy : function_storage_policy< F* >
+		{
+			typedef function_storage_policy< F* > base_type;
+
+			static void init_functor( any_data& fdata, reference_wrapper<F> f )
+			{
+				base_type::init_functor( fdata, addressof( f.get() ) );
+			}
+
+		};
+
+		template < typename F >
+		struct function_wrapped_storage_policy : function_storage_policy< cast_type_wrapper< F > >
+		{
+			typedef function_storage_policy< cast_type_wrapper< F > > base_type;
+
+			static F* get_pointer( const any_data& src )
+			{
+				return &base_type::get_pointer( src )->value;
+			}
+		};
+
+
+		template < typename Signature, typename Functor >
+		class function_handler;
+
+		template < typename R, typename F, typename... Args >
+		class function_handler< R(Args...), F > : public function_storage_policy<F>
+		{
+			typedef function_storage_policy<F> base_type;
+		public:
+			static R invoke( const any_data& f, Args... args )
+			{
+				return ( *base_type::get_pointer( f ) )( forward<Args>( args )... );
+			}
+		};
+
+		template < typename R, typename F, typename... Args >
+		class function_handler< R( Args... ), reference_wrapper<F> > : public function_reference_storage_policy<F>
+		{
+			typedef function_reference_storage_policy<F> base_type;
+		public:
+			static R invoke( const any_data& f, Args... args )
+			{
+				return callable_functor( **base_type::get_pointer( f ) )( forward<Args>( args )... );
+			}
+		};
+
+		template < typename Signature, typename Functor, bool IsMemFn = is_member_function_pointer<Functor>::value >
+		class pick_function_handler;
+
+		template < typename F, typename R, typename... Args >
+		class pick_function_handler< R( Args... ), F, true > 
+			: public function_wrapped_storage_policy< F >
+		{
+			typedef function_wrapped_storage_policy< F > base_type;
+		public:
+			static R invoke( const any_data& f, Args... args )
+			{
+				return mem_fn( *base_type::get_pointer( f ) )( forward<Args>( args )... );
+			}
+		};
+		template < typename F, typename R, typename... Args >
+		class pick_function_handler< R( Args... ), F, false >
+			: public function_handler<  R( Args... ), F >
+		{
+		};
+
+
+	}
+
+	template < typename R, typename... Args >
+	class function< R( Args... ) > 
+	{
+		typedef R signature_type( Args... );
+
+		template < typename F >
+		struct invoke_type
+		{
+			typedef decltype( ( detail::callable_functor( declval<F&>() ) ) ( declval<Args>()... ) ) type;
+		};
+
+		template < typename R1, typename R2 >
+		struct is_call_convertible : is_convertible<R1, R2> {};
+
+		template < typename R1 >
+		struct is_call_convertible< R1, void > : true_type {};
+
+		template < typename F >
+		using is_callable = is_call_convertible< invoke_type<F>, R >;
+
+		template < typename C, typename T >
+		using requires = typename enable_if< C::value, T >::type;
+	public:
+		typedef R result_type;
+
+		function() noexcept : m_manager( nullptr ), m_invoker( nullptr ) {}
+		function( nullptr_t ) noexcept : m_manager( nullptr ), m_invoker( nullptr ) {}
+		function( const function& f ) : m_manager( nullptr ), m_invoker( nullptr )
+		{
+			if ( static_cast<bool>( f ) )
+			{
+				m_invoker = f.m_invoker;
+				m_manager = f.m_manager;
+				f.m_manager( m_functor, f.m_functor, detail::op_clone_functor );
+			}
+		}
+		function( function&& f ) : m_manager( nullptr ), m_invoker( nullptr ) { f.swap( *this ); }
+
+		template < typename F, typename = requires<is_callable<F>, void > >
+		function( F f ) : m_manager( nullptr ), m_invoker( nullptr )
+		{
+			typedef detail::pick_function_handler< signature_type, F > handler;
+
+			if ( detail::function_not_empty( f ) )
+			{
+				handler::init_functor( m_functor, move( f ) );
+				m_invoker = &handler::invoke;
+				m_manager = &handler::execute_op;
+			}
+		}
+
+		function& operator=( const function& f )
+		{
+			function( f ).swap( *this );
+			return *this;
+		}
+
+		function& operator=( function&& f )
+		{
+			function( move( f ) ).swap( *this );
+			return *this;
+		}
+
+		function& operator=( nullptr_t )
+		{
+			clear();
+			return *this;
+		}
+
+		template < typename F >
+		requires< is_callable<F>, function& > operator=( F&& f )
+		{
+			function( forward<F>( f ) ).swap( *this );
+			return *this;
+		}
+		template < typename F >
+		function& operator=( reference_wrapper<F> f ) noexcept
+		{
+			function( f ).swap( *this );
+			return *this;
+		}
+
+		void swap( function& f )
+		{
+			nv::swap( m_functor, f.m_functor );
+			nv::swap( m_manager, f.m_manager );
+			nv::swap( m_invoker, f.m_invoker );
+		}
+
+		explicit operator bool() const noexcept
+		{
+			return !!m_manager;
+		}
+
+		R operator()( Args... args ) const
+		{
+			NV_ASSERT( m_invoker, "Empty function execution!" );
+			return m_invoker( m_functor, forward<Args>( args )... );
+		}
+
+		~function()
+		{
+			clear();
+		}
+
+	private:
+
+		void clear()
+		{
+			if ( m_manager ) m_manager( m_functor, m_functor, detail::op_destroy_functor );
+			m_manager = nullptr;
+			m_invoker = nullptr;
+		}
+
+		typedef R( *invoker_type )( const detail::any_data&, Args... );
+		typedef bool( *manager_type )( detail::any_data&, const detail::any_data&, detail::operation );
+
+		detail::any_data m_functor;
+		manager_type     m_manager; 
+		invoker_type     m_invoker;
+	};
+
+
+
+}
+
+#endif // NV_STL_FUNCTIONAL_FUNCTION_HH
+
Index: trunk/nv/stl/memory/notnull_ptr.hh
===================================================================
--- trunk/nv/stl/memory/notnull_ptr.hh	(revision 552)
+++ trunk/nv/stl/memory/notnull_ptr.hh	(revision 552)
@@ -0,0 +1,1 @@
+#pragma once
Index: trunk/nv/stl/memory/unique.hh
===================================================================
--- trunk/nv/stl/memory/unique.hh	(revision 552)
+++ trunk/nv/stl/memory/unique.hh	(revision 552)
@@ -0,0 +1,174 @@
+// Copyright (C) 2015-2015 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file unique.hh
+* @author Kornel Kisielewicz
+* @brief unique_ptr implementation
+*/
+
+#ifndef NV_STL_MEMORY_UNIQUE_HH
+#define NV_STL_MEMORY_UNIQUE_HH
+
+#include <nv/stl/memory/common.hh>
+#include <nv/stl/type_traits/common.hh>
+
+namespace nv
+{
+	template < typename T >
+	class unique_ptr
+	{
+	public:
+		typedef T*               pointer;
+		typedef T                element_type;
+
+		unique_ptr() : ptr( pointer() ) {}
+		explicit unique_ptr( pointer p ) : ptr( p ) {}
+
+		unique_ptr( unique_ptr&& u ) : ptr( u.release() ) {}
+
+		template< typename U >
+		unique_ptr( unique_ptr<U> u ) : ptr( u.release() ) {}
+		~unique_ptr() { reset(); }
+
+		unique_ptr& operator=( unique_ptr&& u )
+		{
+			reset( u.release() );
+			return *this;
+		}
+
+		template< typename U >
+		unique_ptr& operator=( unique_ptr<U>&& u )
+		{
+			reset( u.release() );
+			return *this;
+		}
+
+		typename add_lvalue_reference<element_type>::type 
+		operator*() const
+		{
+			NV_ASSERT( ptr != nullptr, "dereferencing null ptr!" );
+			return *ptr;
+		}
+		pointer operator->() const
+		{
+			NV_ASSERT( ptr != nullptr, "dereferencing null ptr!" );
+			return ptr;
+		}
+
+		pointer get() const
+		{
+			return ptr;
+		}
+
+		pointer release()
+		{
+			pointer p = ptr;
+			ptr = 0;
+			return p;
+		}
+
+		void reset( pointer p = pointer() )
+		{
+			if ( p != ptr )
+			{
+				delete ptr;
+				ptr = p;
+			}
+		}
+
+		void swap( unique_ptr& u )
+		{
+			using nv::swap;
+			swap( ptr, u.ptr );
+		}
+
+		unique_ptr( const unique_ptr& ) = delete;
+		template< typename U >
+		unique_ptr( const unique_ptr<U>& ) = delete;
+		unique_ptr& operator=( const unique_ptr& ) = delete;
+		template< typename U >
+		unique_ptr& operator=( const unique_ptr<U>& ) = delete;
+	private:
+		pointer ptr;
+	};
+
+	template < typename T >
+	class unique_ref
+	{
+	public:
+		typedef T*               pointer;
+		typedef T                element_type;
+
+		unique_ref( unique_ref&& u ) : ptr( u.release() ) {}
+
+		template< typename U >
+		unique_ref( unique_ref<U> u ) : ptr( u.release() ) {}
+		~unique_ref() { reset(); }
+
+		unique_ref& operator=( unique_ref&& u )
+		{
+			reset( u.release() );
+			return *this;
+		}
+
+		template< typename U >
+		unique_ref& operator=( unique_ref<U>&& u )
+		{
+			reset( u.release() );
+			return *this;
+		}
+
+		typename add_lvalue_reference<element_type>::type
+			operator*() const
+		{
+			return *ptr;
+		}
+		pointer operator->() const
+		{
+			return ptr;
+		}
+
+	private:
+		explicit unique_ref( pointer p ) : ptr( p ) {}
+
+		pointer release()
+		{
+			pointer p = ptr;
+			ptr = 0;
+			return p;
+		}
+
+		void reset( pointer p = pointer() )
+		{
+			if ( p != ptr )
+			{
+				delete ptr;
+				ptr = p;
+			}
+		}
+
+	public:
+		void swap( unique_ref& u )
+		{
+			using nv::swap;
+			swap( ptr, u.ptr );
+		}
+
+		unique_ref( const unique_ref& ) = delete;
+		template< typename U >
+		unique_ref( const unique_ref<U>& ) = delete;
+		unique_ref& operator=( const unique_ref& ) = delete;
+		template< typename U >
+		unique_ref& operator=( const unique_ref<U>& ) = delete;
+	private:
+		pointer ptr;
+	};
+
+}
+
+#endif // NV_STL_MEMORY_UNIQUE_HH
+
Index: trunk/nv/stl/mpl/list.hh
===================================================================
--- trunk/nv/stl/mpl/list.hh	(revision 552)
+++ trunk/nv/stl/mpl/list.hh	(revision 552)
@@ -0,0 +1,87 @@
+// Copyright (C) 2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file list.hh
+* @author Kornel Kisielewicz epyon@chaosforge.org
+* @brief STL/MPL list 
+*/
+
+#ifndef NV_STL_MPL_LIST_HH
+#define NV_STL_MPL_LIST_HH
+
+#include <nv/common.hh>
+#include <nv/stl/type_traits.hh>
+
+namespace nv
+{
+	namespace mpl
+	{
+
+		template < typename... Ts > struct list {};
+
+		template < typename T, T... Values >
+		using integral_list = mpl::list< integral_constant< T, Values >... >;
+
+		using empty_list = mpl::list<>;
+
+		namespace detail
+		{
+
+// 			template < typename... Ts >
+// 			struct append_impl
+// 			{
+// 				typedef mpl::empty_list type;
+// 			};
+// 
+// 			template < typename T >
+// 			struct append_impl< T >
+// 			{
+// 				typedef T type;
+// 			};
+// 
+// 			template < 
+// 				typename < typename... > class L1,
+// 				typename < typename... > class L2,
+// 				typename... T1s,
+// 				typename... T2s,
+// 				typename... Ts 
+// 			>
+// 			struct append_impl< L1< T1s... >, L2< T2s... >, Ts... >
+// 				: append_impl< L1< T1s..., T2s... >, Ts... >
+// 			{
+// 			};
+
+			template< typename T > struct list_size_impl;
+
+			template< typename... T> struct list_size_impl< list<T...> >
+			{
+				using type = nv::integral_constant< uint32, sizeof...( T )>;
+			};
+
+			template< typename T > struct head_impl;
+
+			template< typename H, typename... T> struct head_impl< list<H,T...> >
+			{
+				using type = H;
+			};
+
+		}
+
+		template< typename List > 
+		using list_size = typename detail::list_size_impl<List>::type;
+
+		template< typename List >
+		using head = typename detail::head_impl<List>::type;
+		
+// 		template < typename... Ts >
+// 		using append = detail::append_impl< Ts... >::type;
+
+
+	}
+}
+
+#endif // NV_STL_MPL_LIST_HH
Index: trunk/nv/stl/type_traits/concepts.hh
===================================================================
--- trunk/nv/stl/type_traits/concepts.hh	(revision 552)
+++ trunk/nv/stl/type_traits/concepts.hh	(revision 552)
@@ -0,0 +1,190 @@
+// Copyright (C) 2017 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+ * @file concepts.hh
+ * @author Kornel Kisielewicz epyon@chaosforge.org
+ * @brief concepts - experimental
+ * 
+ * based on https://www.youtube.com/watch?v=VctviQl-SR4
+ * and https://github.com/slurps-mad-rips/cxx-concepts
+ */
+
+#ifndef NV_STL_TYPE_TRAITS_CONCEPTS_HH
+#define NV_STL_TYPE_TRAITS_CONCEPTS_HH
+
+#include <nv/stl/type_traits.hh>
+#include <nv/stl/type_traits/experimental.hh>
+
+namespace nv
+{
+	
+	template < typename T, typename Void, template < typename...> class, typename...>
+	struct detector : identity<T> { using value_t = false_type; };
+
+	template < typename T, template < typename... > class U, typename... Args>
+	struct detector<T, void_t< U<Args...> >, U, Args... >
+		: identity< U< Args... > >
+	{
+		using value_t = true_type;
+	};
+
+	struct nonesuch final
+	{
+		nonesuch( nonesuch const& ) = delete;
+		nonesuch() = delete;
+		~nonesuch() = delete;
+		void operator = ( nonesuch const& ) = delete;
+	};
+
+	template < typename T, template < typename... > class U, typename... Args >
+	using detected_or = detector<T, void, U, Args...>;
+
+	template < template < typename... > class T, typename... Args >
+	using detected_t = typename detected_or<nonesuch, T, Args...>::type;
+
+	template < typename T, template < typename... > class U, typename... Args>
+	using detected_or_t = typename detected_or<T, U, Args...>::type;
+
+	template < typename To, template < typename... > class T, typename... Args>
+	using is_detected_convertible = is_convertible<
+		detected_t<T, Args...>,
+		To
+	>;
+
+	template < typename T, template < typename... > class U, typename... Args >
+	using is_detected_exact = is_same<T, detected_t<U, Args...>>;
+
+	template < template < typename... > class T, typename... Args >
+	using is_detected = typename detected_or<nonesuch, T, Args...>::value_t;
+
+	template < typename... > struct conjunction;
+	template < typename... > struct disjunction;
+
+	template < typename B > 
+	using negation = bool_constant< !B::value >;
+
+	template < typename T, typename... Ts>
+	struct conjunction<T, Ts...> :
+		bool_constant<T::value && conjunction<Ts...>::value>
+	{
+	};
+	template <> struct conjunction<> : true_type {};
+
+	template < typename T, typename... Ts>
+	struct disjunction<T, Ts...> :
+		bool_constant<T::value || disjunction<Ts...>::value>
+	{
+	};
+
+	template <> struct disjunction<> : false_type {};
+
+	template < typename From, typename To >
+	using explicit_cast = decltype( static_cast<To>( declval<From>() ) );
+
+	template < bool... Bs>
+	constexpr bool require = conjunction<bool_constant<Bs>...>::value;
+
+	template < bool... Bs>
+	constexpr bool either = disjunction<bool_constant<Bs>...>::value;
+
+	template < bool... Bs>
+	constexpr bool disallow = not require<Bs...>;
+
+	template < template < typename... > class Op, typename... Args >
+	constexpr bool exists = is_detected<Op, Args...>::value;
+
+	template < typename To, template < typename... > class Op, typename... Args >
+	constexpr bool casts_to = exists<explicit_cast, detected_t<Op, Args...>, To>;
+
+	template < typename To, template < typename...> class Op, typename... Args >
+	constexpr bool converts_to = is_detected_convertible<To, Op, Args...>::value;
+
+	template < typename Exact, template < typename...> class Op, typename... Args >
+	constexpr bool identical_to = is_detected_exact<Exact, Op, Args...>::value;
+
+	namespace ops
+	{
+		template < typename T, typename U > using equal_to = decltype( declval<T>() == declval<U>() );
+		template < typename T, typename U > using less = decltype( declval<T>() < declval<U>() );
+		template < typename T > using dereference = decltype( *declval<T>() );
+		template < typename T > using arrow = decltype( declval<T>().operator->() );
+
+		template < typename T > using postfix_increment = decltype( declval<T>()++ );
+		template < typename T > using prefix_increment = decltype( ++declval<T>() );
+	}
+
+	namespace adl
+	{
+		using nv::swap;
+		template <class T, class U = T>
+		using swap_with = decltype( swap( declval<T>(), declval<U>() ) );
+	}
+
+	namespace alias
+	{
+		template < typename T > using value_type = typename T::value_type;
+		template < typename T > using reference = typename T::reference;
+		template < typename T > using pointer = typename T::pointer;
+
+		template < typename T >
+		using iterator_category = typename nv::iterator_traits<T>::iterator_category;
+	} 
+
+	namespace concepts
+	{
+
+		template < typename T, typename U >
+		constexpr bool SwappableWith = exists<adl::swap_with, T, U>;
+
+//		template < typename T >
+//		constexpr bool CopyConstructible = is_copy_constructible<T>::value;
+
+		template < typename T > constexpr bool CopyAssignable = is_copy_assignable<T>::value;
+//		template < typename T > constexpr bool Destructible = is_destructible<T>::value;
+		template < typename T > constexpr bool Swappable = SwappableWith<T&, T&>;
+		template < typename T > constexpr bool Pointer = is_pointer<T>::value;
+
+		template < typename T, typename U = T >
+		constexpr bool EqualityComparable = converts_to<bool, ops::equal_to, T, U>;
+
+		template < typename T >
+		constexpr bool Iterator = require<
+//			CopyConstructible<T>,
+			CopyAssignable<T>,
+//			Destructible<T>,
+			Swappable<T>,
+			exists<ops::postfix_increment, T>,
+			exists<ops::prefix_increment, T>,
+			exists<ops::dereference, T>
+		>;
+
+		template < typename T >
+		constexpr bool InputIterator = either<
+			Pointer<T>,
+			require<
+			EqualityComparable<T,T>,
+			Iterator<T>,
+			exists<alias::value_type, T>,
+			exists<alias::reference, T>,
+			either<
+			identical_to<detected_t<alias::reference, T>, ops::dereference, T>,
+			converts_to<detected_t<alias::value_type, T>, ops::dereference, T>
+			>,
+			identical_to<detected_t<alias::pointer, T>, ops::arrow, T>,
+			converts_to<detected_t<alias::value_type, T>, ops::dereference, T&>,
+			converts_to<input_iterator_tag, alias::iterator_category, T>
+			>
+		>;
+
+	} /* namespace concepts */
+
+#define NV_CONCEPT( TYPE, CONCEPT ) \
+	static_assert( nv::concepts::CONCEPT<TYPE>, #TYPE " does not model " #CONCEPT );
+
+}
+
+#endif // NV_STL_TYPE_TRAITS_CONCEPTS_HH
Index: trunk/nv/stl/utility/enumerator.hh
===================================================================
--- trunk/nv/stl/utility/enumerator.hh	(revision 552)
+++ trunk/nv/stl/utility/enumerator.hh	(revision 552)
@@ -0,0 +1,59 @@
+// Copyright (C) 2017-2017 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+/**
+* @file enumerator.hh
+* @author Kornel Kisielewicz epyon@chaosforge.org
+* @brief enumerator support
+*/
+
+#ifndef NV_STL_ENUMERATOR_HH
+#define NV_STL_ENUMERATOR_HH
+
+#include <nv/common.hh>
+#include <nv/stl/math.hh>
+#include <nv/stl/iterator.hh>
+#include <nv/stl/type_traits/transforms.hh>
+
+namespace nv
+{
+
+	template < typename T >
+	class value_enumerator_base : public iterator< input_iterator_tag, T >
+	{
+	public:
+		value_enumerator_base( T value ) : m_value( value ) {}
+		T operator* () const { return m_value; }
+		T const* operator-> () const { return &m_value; }
+		bool operator== ( const value_enumerator_base& rhs ) const
+		{
+			return m_value == rhs.m_value;
+		}
+		bool operator!= ( const value_enumerator_base& rhs ) const
+		{
+			return !( *this == rhs );
+		}
+	protected:
+		T m_value;
+	};
+
+
+	template < typename Enumerator >
+	class enumerator_provider
+	{
+	public:
+		enumerator_provider( Enumerator begin, Enumerator end )
+			: m_begin( begin ), m_end( end ) {}
+		Enumerator begin() const { return m_begin; }
+		Enumerator end() const { return m_end; }
+	protected:
+		Enumerator m_begin;
+		Enumerator m_end;
+	};
+		
+}
+
+#endif // NV_STL_ENUMERATOR_HH
Index: trunk/src/engine/decal_group.cc
===================================================================
--- trunk/src/engine/decal_group.cc	(revision 552)
+++ trunk/src/engine/decal_group.cc	(revision 552)
@@ -0,0 +1,74 @@
+// Copyright (C) 2016-2016 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#include "nv/engine/decal_group.hh"
+
+using namespace nv;
+
+
+decal_group_manager::decal_group_manager( context* a_context )
+	: m_context( a_context )
+{
+
+}
+
+decal_group decal_group_manager::create_group( uint32 max_particles )
+{
+	decal_group result = m_groups.create();
+	decal_group_info* info = m_groups.get( result );
+	info->local = false;
+	info->count = 0;
+	info->quota = max_particles;
+//	info->vtx_buffer = m_context->create_buffer( VERTEX_BUFFER, STREAM_DRAW, info->quota * sizeof( particle_quad )/*, info->quads_[0].data*/ );
+// 	vertex_array_desc desc;
+// 	desc.add_vertex_buffers< de >( info->vtx_buffer, true );
+// 	info->vtx_array = m_context->create_vertex_array( desc );
+// 	info->quads = new particle_quad[info->quota];
+	info->ref_counter = 0;
+	return result;
+}
+
+void decal_group_manager::release( decal_group group )
+{
+	if ( decal_group_info* info = m_groups.get( group ) )
+	{
+		release( info );
+		m_groups.destroy( group );
+	}
+}
+
+void decal_group_manager::release( decal_group_info* info )
+{
+	if ( info )
+	{
+		delete[] info->quads;
+		m_context->release( info->vtx_array );
+	}
+}
+
+
+void decal_group_manager::clear()
+{
+	for ( auto& g : m_groups )
+		release( &g );
+}
+
+bool decal_group_manager::ref( decal_group group )
+{
+	decal_group_info* info = m_groups.get( group );
+	if ( !info ) return false;
+	info->ref_counter++;
+	return true;
+}
+
+bool decal_group_manager::unref( decal_group group )
+{
+	decal_group_info* info = m_groups.get( group );
+	if ( !info ) return false;
+	info->ref_counter--;
+	return true;
+}
+
Index: trunk/src/formats/ndfm_loader.cc
===================================================================
--- trunk/src/formats/ndfm_loader.cc	(revision 552)
+++ trunk/src/formats/ndfm_loader.cc	(revision 552)
@@ -0,0 +1,319 @@
+// Copyright (C) 2014-2017 ChaosForge Ltd
+// http://chaosforge.org/
+//
+// This file is part of Nova libraries. 
+// For conditions of distribution and use, see copying.txt file in root folder.
+
+#include "nv/formats/ndfm_loader.hh"
+#include "nv/interface/data_channel_access.hh"
+#include "nv/stl/string.hh"
+
+using namespace nv;
+
+#if 0
+
+bool nv::ndfm_loader::load( stream& source )
+{
+	// TODO: proper error handling
+	reset();
+	ndf_header header;
+	source.read( &header, sizeof( header ), 1 );
+
+	ndf_element root_header;
+	source.read( &root_header, sizeof( root_header ), 1 );
+	skip_attributes( source, root_header.attributes );
+	for ( uint32 i = 0; i < root_header.children; ++i )
+	{
+		ndf_element element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		skip_attributes( source, element_header.attributes );
+		if ( element_header.type == "mesh"_sh64 )
+			load_mesh( source, element_header );
+		else if ( element_header.type == "animation"_sh64 )
+			load_animation( source, element_header );
+		else if ( element_header.type == "strings"_sh64 )
+			load_strings( source ); 
+		else NV_ASSERT( false, "UNKNOWN NMD ELEMENT!" );
+	}
+	return true;
+}
+
+bool nv::ndfm_loader::load_mesh( stream& source, const ndf_element& e )
+{
+	data_channel_set* mesh = data_channel_set_creator::create_set( e.children );
+	load_channel_set( source, mesh, e );
+	m_meshes.push_back( mesh );
+	return true;
+}
+
+data_channel_set* nv::ndfm_loader::release_mesh_data( uint32 index )
+{
+	data_channel_set* result = m_meshes[index];
+	m_meshes[index] = nullptr;
+	return result;
+}
+
+void nv::ndfm_loader::reset()
+{
+	for ( auto mesh : m_meshes ) if ( mesh ) delete mesh;
+	if ( m_node_data ) delete m_node_data;
+	m_meshes.clear();
+
+	m_node_data = nullptr;
+}
+
+void nv::ndfm_loader::skip_attributes( stream& source, uint32 count )
+{
+	if ( count == 0 ) return;
+	source.seek( count * sizeof( ndf_attribute ), origin::CUR );
+}
+
+nv::ndfm_loader::~ndfm_loader()
+{
+	reset();
+}
+
+bool nv::ndfm_loader::load_strings( stream& source )
+{
+	if ( !m_strings ) return true;
+	// TODO: load strings optionally
+	string_table* strings = new string_table( source );
+	m_strings->insert( strings );
+	delete strings;
+	return true;
+}
+
+bool nv::ndfm_loader::load_animation( stream& source, const ndf_element& e )
+{
+	NV_ASSERT( m_node_data == nullptr, "MULTIPLE NODE ENTRIES!" );
+	ndfm_animation_data animation_header;
+	source.read( &animation_header, sizeof( animation_header ), 1 );
+	m_node_data = new mesh_nodes_data( e.name, animation_header.frame_rate, animation_header.frame_count, animation_header.flat );
+	for ( uint32 i = 0; i < e.children; ++i )
+	{
+		ndf_element element_header;
+		source.read( &element_header, sizeof( element_header ), 1 );
+		skip_attributes( source, element_header.attributes );
+		NV_ASSERT( element_header.type == "node"_sh64, "NODE expected!" );
+		data_channel_set* set = data_channel_set_creator::create_set( element_header.children );
+		load_channel_set( source, set, element_header );
+		m_node_data->append( set );
+	}
+	m_node_data->initialize();
+	return true;
+}
+
+bool nv::ndfm_loader::load_channel( stream& source, data_channel_set* channel_set )
+{
+	data_channel_set_creator kaccess( channel_set );
+	ndfm_channel_header cheader;
+	source.read( &cheader, sizeof( cheader ), 1 );
+	raw_data_channel_access channel( kaccess.add_channel( cheader.format, cheader.count ) );
+	source.read( channel.raw_data(), channel.element_size(), channel.size() );
+	return true;
+}
+
+bool nv::ndfm_loader::load_channel_set( stream& source, data_channel_set* channel_set, const ndf_element& e )
+{
+	ndfm_node_data nheader;
+	source.read( &nheader, sizeof( nheader ), 1 );
+
+	data_channel_set_creator kaccess( channel_set );
+	for ( uint32 c = 0; c < e.children; ++c )
+	{
+		load_channel( source, channel_set );
+	}
+	data_channel_set_creator access( channel_set );
+	access.set_name( e.name );
+	access.set_parent_id( nheader.parent_id );
+	access.set_transform( nheader.transform );
+	return true;
+}
+
+mesh_nodes_data* nv::ndfm_loader::release_mesh_nodes_data( uint32 )
+{
+	mesh_nodes_data* result = m_node_data;
+	m_node_data = nullptr;
+	return result;
+}
+
+data_node_list* nv::ndfm_loader::release_data_node_list( uint32 /*= 0 */ )
+{
+	if ( !m_node_data ) return nullptr;
+	data_node_list* result = new data_node_list( m_node_data->get_name() );
+	for ( auto node : *m_node_data )
+		result->append( node->get_info() );
+	delete m_node_data;
+	m_node_data = nullptr;
+	return result;
+}
+
+bool nv::ndfm_loader::is_animated( uint32 /*= 0 */ )
+{
+	if ( !m_node_data ) return false;
+	return m_node_data->is_animated();
+}
+
+
+
+// ----------------------------------------------------------------
+// nmd format dump
+// HACK : TEMPORARY - will go to it's own file, probably nmd_io
+
+
+void nv::ndfm_dump_header( stream& stream_out, uint16 elements, shash64 name )
+{
+	ndf_header header;
+	header.id = four_cc<'n', 'd', 'f', '1'>::value;
+	header.version = 1;
+	stream_out.write( &header, sizeof( header ), 1 );
+
+	ndf_element eheader;
+	eheader.name       = name;
+	eheader.type       = "model"_sh64;
+	eheader.flags      = 0;
+	eheader.size       = 0;
+	eheader.attributes = 0;
+	eheader.children   = elements;
+	stream_out.write( &eheader, sizeof( eheader ), 1 );
+}
+
+void nv::ndfm_dump_element( stream& stream_out, const data_channel_set& data, shash64 type )
+{
+	uint32 size = sizeof( ndfm_node_data );
+	for ( auto& chan : data )
+	{
+		size += sizeof( ndfm_channel_header );
+		size += chan.raw_size();
+	}
+
+	ndf_element eheader;
+	eheader.name       = data.get_name();
+	eheader.type       = type;
+	eheader.flags      = 0;
+	eheader.size       = size;
+	eheader.attributes = 0;
+	eheader.children   = static_cast<uint16>( data.size() );
+	stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+	ndfm_node_data nheader;
+	int confirm_that_not_needed;
+//	nheader.transform = data.get_transform();
+	nheader.parent_id = data.get_parent_id();
+	stream_out.write( &nheader, sizeof( nheader ), 1 );
+	for ( auto& channel : data )
+	{
+		ndfm_channel_header cheader;
+		cheader.format = channel.descriptor();
+		cheader.count = channel.size();
+		stream_out.write( &cheader, sizeof( cheader ), 1 );
+		stream_out.write( channel.raw_data(), channel.element_size(), channel.size() );
+	}
+}
+
+void nv::ndfm_dump_animation( stream& stream_out, const mesh_nodes_data& nodes )
+{
+	uint32 total = sizeof( ndfm_animation_data );
+	for ( auto node : nodes )
+	{
+		total += sizeof( ndf_element ) + sizeof( ndfm_node_data );
+		for ( uint32 c = 0; c < node->size(); ++c )
+		{
+			total += sizeof( ndfm_channel_header );
+			total += node->get_channel( c )->raw_size();
+		}
+	}
+
+	ndf_element eheader;
+	eheader.name       = nodes.get_name();
+	eheader.type       = "animation"_sh64;
+	eheader.flags      = 0;
+	eheader.size       = total;
+	eheader.attributes = 0;
+	eheader.children   = static_cast<uint16>( nodes.size() );
+	stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+	ndfm_animation_data aheader;
+	aheader.frame_rate  = nodes.get_fps();
+	aheader.frame_count = nodes.get_frame_count();
+	stream_out.write( &aheader, sizeof( aheader ), 1 );
+
+	for ( auto node : nodes )
+	{
+		ndfm_dump_element( stream_out, *node, "node"_sh64 );
+	}
+}
+
+void nv::ndfm_dump_bones( stream& stream_out, const data_node_list& bones )
+{
+	uint32 total = sizeof( ndfm_animation_data );
+	for ( auto bone : bones )
+	{
+		total += sizeof( ndf_element ) + sizeof( ndfm_node_data );
+	}
+
+	ndf_element eheader;
+	eheader.name = bones.get_name();
+	eheader.type = "animation"_sh64;
+	eheader.flags = 0;
+	eheader.size = total;
+	eheader.attributes = 0;
+	eheader.children = static_cast<uint16>( bones.size() );
+	stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+	for ( auto bone : bones )
+	{
+		uint32 size = sizeof( ndfm_node_data );
+		ndf_element eheader;
+		eheader.name = bone.name;
+		eheader.type = "bone"_sh64;
+		eheader.flags = 0;
+		eheader.size = size;
+		eheader.attributes = 0;
+		eheader.children = 0;
+		stream_out.write( &eheader, sizeof( eheader ), 1 );
+
+		ndfm_node_data nheader;
+		nheader.transform = bone.transform;
+		nheader.parent_id = bone.parent_id;
+		stream_out.write( &nheader, sizeof( nheader ), 1 );
+	}
+}
+
+void nv::ndfm_dump_strings( stream& stream_out, const string_table& strings )
+{
+	ndf_element sheader;
+	sheader.name       = shash64();
+	sheader.type       = "strings"_sh64;
+	sheader.flags      = 0;
+	sheader.size       = strings.dump_size();
+	sheader.children   = 0;
+	sheader.attributes = 0;
+	stream_out.write( &sheader, sizeof( sheader ), 1 );
+	strings.dump( stream_out );
+}
+
+// void nv::ndfm_dump( stream& stream_out, const mesh_data_pack* model, const string_table* strings, shash64 name )
+// {
+// 	uint16 elements = ( strings ? 1 : 0 ) // +1 string array
+// 		+ uint16( model->get_count() ) // meshes
+// 		+ ( model->get_node_count() > 0 ? 1 : 0 ); // nodes
+// 	ndfm_dump_header( stream_out, elements, name );
+// 
+// 	for ( uint32 i = 0; i < model->get_count(); ++i )
+// 	{
+// 		ndfm_dump_element( stream_out, *model->get_mesh( i ), "mesh"_sh64 );
+// 	}
+// 
+// 	if ( model->get_node_count() > 0 )
+// 	{
+// 		ndfm_dump_nodes( stream_out, *model->get_nodes() );
+// 	}
+// 
+// 	if ( strings )
+// 	{
+// 		ndfm_dump_strings( stream_out, *strings );
+// 	}
+// }
+
+#endif
