Index: trunk/src/formats/obj_loader.cc
===================================================================
--- trunk/src/formats/obj_loader.cc	(revision 237)
+++ trunk/src/formats/obj_loader.cc	(revision 238)
@@ -11,4 +11,35 @@
 using namespace nv;
 
+struct obj_vertex_vt
+{
+	vec3 position;
+	vec2 texcoord;
+
+	obj_vertex_vt( vec3 a_position, vec2 a_texcoord ) 
+		: position( a_position ), texcoord( a_texcoord ) {}
+};
+
+struct obj_vertex_vtn
+{
+	vec3 position;
+	vec2 texcoord;
+	vec3 normal;
+
+	obj_vertex_vtn( vec3 a_position, vec2 a_texcoord, vec3 a_normal ) 
+		: position( a_position ), texcoord( a_texcoord ), normal( a_normal ) {}
+};
+
+
+struct obj_vertex_vtnt
+{
+	vec3 position;
+	vec2 texcoord;
+	vec3 normal;
+	vec4 tangent;
+
+	obj_vertex_vtnt( vec3 a_position, vec2 a_texcoord, vec3 a_normal ) 
+		: position( a_position ), texcoord( a_texcoord ), normal( a_normal ) {}
+};
+
 struct obj_reader
 {
@@ -24,6 +55,9 @@
 	obj_reader();
 	bool read_stream( std::istream& stream );
-
-	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count ) = 0;
+	virtual size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count ) = 0;
+	virtual size_t raw_size() { return 0; }
+	virtual const uint8* raw_pointer() { return nullptr; }
+	virtual void calculate_tangents() {}
+
 	virtual ~obj_reader(){}
 };
@@ -120,5 +154,5 @@
 {
 	mesh_obj_reader( mesh_data_creator* m ) : m_mesh( m ) {}
-	virtual std::size_t add_face( uint32* v, uint32* t, uint32* n, size_t count );
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count );
 	virtual void calculate_tangents();
 
@@ -238,8 +272,5 @@
 bool nv::obj_loader::load( stream& source )
 {
-	if ( m_mesh != nullptr )
-	{
-		delete m_mesh;
-	}
+	if ( m_mesh != nullptr ) delete m_mesh;
 	mesh_data_creator creator;
 	mesh_obj_reader reader( &creator );
@@ -254,2 +285,207 @@
 	return true;
 }
+
+struct mesh_data_reader_vt : public obj_reader
+{
+	mesh_data_reader_vt()  {}
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32*, size_t count )
+	{
+		if ( count < 3 ) return 0; // TODO : report error?
+		// TODO : support if normals not present;
+		std::size_t result = 0;
+		// Simple triangulation - obj's shouldn't have more than quads anyway
+		for ( size_t i = 2; i < count; ++i )
+		{
+			result++;
+			m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ] );
+			m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ] );
+			m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ] );
+		}
+		return result;
+	}
+	std::vector< obj_vertex_vt > m_data;
+	virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vt ); }
+	virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
+};
+
+struct mesh_data_reader_vtn : public obj_reader
+{
+	mesh_data_reader_vtn()  {}
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
+	{
+		if ( count < 3 ) return 0; // TODO : report error?
+		// TODO : support if normals not present;
+		std::size_t result = 0;
+		// Simple triangulation - obj's shouldn't have more than quads anyway
+		for ( size_t i = 2; i < count; ++i )
+		{
+			result++;
+			m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
+			m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
+			m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
+		}
+		return result;
+	}
+	std::vector< obj_vertex_vtn > m_data;
+	virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vtn ); }
+	virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
+};
+
+struct mesh_data_reader_vtnt : public obj_reader
+{
+	mesh_data_reader_vtnt()  {}
+	virtual std::size_t add_face( uint32* vi, uint32* ti, uint32* ni, size_t count )
+	{
+		if ( count < 3 ) return 0; // TODO : report error?
+		// TODO : support if normals not present;
+		std::size_t result = 0;
+		// Simple triangulation - obj's shouldn't have more than quads anyway
+		for ( size_t i = 2; i < count; ++i )
+		{
+			result++;
+			m_data.emplace_back( v[ vi[ 0 ]   ], t[ ti[ 0   ] ], n[ ni[ 0   ] ] );
+			m_data.emplace_back( v[ vi[ i-1 ] ], t[ ti[ i-1 ] ], n[ ni[ i-1 ] ] );
+			m_data.emplace_back( v[ vi[ i ]   ], t[ ti[ i   ] ], n[ ni[ i   ] ] );
+		}
+		return result;
+	}
+	std::vector< obj_vertex_vtnt > m_data;
+	virtual size_t raw_size() { return m_data.size() * sizeof( obj_vertex_vtnt ); }
+	virtual const uint8* raw_pointer() { return (const uint8*)m_data.data(); }
+
+	// based on http://www.terathon.com/code/tangent.html
+	void calculate_tangents()
+	{
+		//		const std::vector< vec3 >& vp = m_mesh->get_positions();
+		//		const std::vector< vec2 >& vt = m_mesh->get_texcoords();
+		//		const std::vector< vec3 >& vn = m_mesh->get_normals();
+		//		std::vector< vec3 >& tg = m_mesh->get_tangents();
+
+		size_t count  = m_data.size();
+		size_t tcount = count / 3;
+
+		std::vector< vec3 > tan1( count );
+		std::vector< vec3 > tan2( count );
+
+		for (size_t a = 0; a < tcount; ++a )
+		{
+			size_t i1 = a * 3;
+			size_t i2 = a * 3 + 1;
+			size_t i3 = a * 3 + 2;
+			obj_vertex_vtnt& vtx1 = m_data[ i1 ];
+			obj_vertex_vtnt& vtx2 = m_data[ i2 ];
+			obj_vertex_vtnt& vtx3 = m_data[ i3 ];
+
+			// TODO: simplify
+			vec3 xyz1 = vtx2.position - vtx1.position;
+			vec3 xyz2 = vtx3.position - vtx1.position;
+			//vec2 st1  = w2 - w1;
+			//vec2 st2  = w3 - w1;
+
+			float s1 = vtx2.texcoord.x - vtx1.texcoord.x;
+			float t1 = vtx2.texcoord.y - vtx1.texcoord.y;
+			float s2 = vtx3.texcoord.x - vtx1.texcoord.x;
+			float t2 = vtx3.texcoord.y - vtx1.texcoord.y;
+
+			float stst = s1 * t2 - s2 * t1;
+			float r = 0.0f;
+			if (stst > 0.0f || stst < 0.0f) r = 1.0f / stst;
+
+			vec3 sdir = ( t2 * xyz1 - t1 * xyz2 ) * r;
+			vec3 tdir = ( s1 * xyz2 - s2 * xyz1 ) * r;
+
+			// the += below obviously doesn't make sense in this case, but I'll
+			// leave it here for when I move to indices
+			tan1[i1] += sdir;
+			tan1[i2] += sdir;
+			tan1[i3] += sdir;
+
+			// tan2 not needed anymore??
+			tan2[i1] += tdir;
+			tan2[i2] += tdir;
+			tan2[i3] += tdir;
+		}
+
+		for (std::size_t a = 0; a < count; ++a )
+		{
+			const vec3& n = m_data[a].normal;
+			const vec3& t = tan1[a];
+			if ( ! (t.x == 0.0f && t.y == 0.0f && t.z == 0.0f) )
+			{
+				m_data[a].tangent    = vec4( glm::normalize(t - n * glm::dot( n, t )), 0.0f ); 
+				m_data[a].tangent[3] = (glm::dot(glm::cross(n, t), tan2[a]) < 0.0f) ? -1.0f : 1.0f;
+			}
+		}
+
+	}
+
+
+};
+
+nv::wavefront_loader::wavefront_loader( bool normals /*= true*/, bool tangents /*= false */ )
+	: m_normals( normals ), m_tangents( tangents ), m_mesh( nullptr )
+{
+	if ( normals )
+	{
+		if ( tangents )
+			m_descriptor.initialize<obj_vertex_vtnt>();
+		else
+			m_descriptor.initialize<obj_vertex_vtn>();
+	}
+	else
+		m_descriptor.initialize<obj_vertex_vt>();
+}
+
+bool nv::wavefront_loader::load( stream& source )
+{
+	if ( m_mesh ) delete m_mesh;
+	
+	obj_reader* reader = nullptr;
+	if ( m_normals )
+	{
+		if ( m_tangents )
+			reader = new mesh_data_reader_vtnt();
+		else
+			reader = new mesh_data_reader_vtn();
+	}
+	else
+		reader = new mesh_data_reader_vt();
+	std_stream sstream( &source );
+	reader->read_stream( sstream );
+
+	if ( m_tangents )
+	{
+		reader->calculate_tangents();
+	}
+	
+
+	mesh_raw_channel* channel = new mesh_raw_channel();
+	nv::uint8* data = nullptr;
+	if ( reader->raw_size() > 0 ) 
+	{
+		data = new uint8[ reader->raw_size() ];
+		std::copy_n( reader->raw_pointer(), reader->raw_size(), data );
+	}
+	channel->data  = data;
+	channel->desc  = m_descriptor;
+	channel->count = reader->size * 3;
+	channel->size  = reader->raw_size();
+	delete reader;
+
+	m_mesh = new mesh_data();
+	m_mesh->add_channel( channel );
+	return true;
+
+}
+
+mesh_data* nv::wavefront_loader::release_mesh_data()
+{
+	mesh_data* result = m_mesh;
+	m_mesh = nullptr;
+	return result;
+}
+
+nv::wavefront_loader::~wavefront_loader()
+{
+	if ( m_mesh ) delete m_mesh;
+}
