Forráskód Böngészése

Adding tessellation support for new Blender 2.6 polygons

- Added BlenderBMesh.cpp/h which contains a class to convert a BMesh to an old style tri/quad mesh
- Added BlenderTessellator.cpp/h which contains a class to tessellate the poly loops contained within a BMesh
Alice 12 éve
szülő
commit
01a6ec9059

+ 176 - 0
code/BlenderBMesh.cpp

@@ -0,0 +1,176 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderBMesh.cpp
+ *  @brief Conversion of Blender's new BMesh stuff
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderBMesh.h"
+#include "BlenderTessellator.h"
+
+namespace Assimp
+{
+	template< > const std::string LogFunctions< BlenderBMeshConverter >::log_prefix = "BLEND_BMESH: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+using namespace Assimp::Formatter;
+
+// ------------------------------------------------------------------------------------------------
+BlenderBMeshConverter::BlenderBMeshConverter( const Mesh* mesh ):
+	BMesh( mesh ),
+	triMesh( NULL )
+{
+	AssertValidMesh( );
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderBMeshConverter::~BlenderBMeshConverter( )
+{
+	DestroyTriMesh( );
+}
+
+// ------------------------------------------------------------------------------------------------
+bool BlenderBMeshConverter::ContainsBMesh( ) const
+{
+	// TODO - Should probably do some additional verification here
+	return BMesh->totpoly && BMesh->totloop && BMesh->totvert;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Mesh* BlenderBMeshConverter::TriangulateBMesh( )
+{
+	AssertValidMesh( );
+	AssertValidSizes( );
+	PrepareTriMesh( );
+
+	for ( int i = 0; i < BMesh->totpoly; ++i )
+	{
+		const MPoly& poly = BMesh->mpoly[ i ];
+		ConvertPolyToFaces( poly );
+	}
+
+	return triMesh;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AssertValidMesh( )
+{
+	if ( !ContainsBMesh( ) )
+	{
+		ThrowException( "BlenderBMeshConverter requires a BMesh with \"polygons\" - please call BlenderBMeshConverter::ContainsBMesh to check this first" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AssertValidSizes( )
+{
+	if ( BMesh->totpoly != BMesh->mpoly.size( ) )
+	{
+		ThrowException( "BMesh poly array has incorrect size" );
+	}
+	if ( BMesh->totloop != BMesh->mloop.size( ) )
+	{
+		ThrowException( "BMesh loop array has incorrect size" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::PrepareTriMesh( )
+{
+	if ( triMesh )
+	{
+		DestroyTriMesh( );
+	}
+
+	triMesh = new Mesh( *BMesh );
+	triMesh->totface = 0;
+	triMesh->mface.clear( );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::DestroyTriMesh( )
+{
+	delete triMesh;
+	triMesh = NULL;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::ConvertPolyToFaces( const MPoly& poly )
+{
+	const MLoop* polyLoop = &BMesh->mloop[ poly.loopstart ];
+	if ( poly.totloop == 3 || poly.totloop == 4 )
+	{
+		AddFace( polyLoop[ 0 ].v, polyLoop[ 1 ].v, polyLoop[ 2 ].v, poly.totloop == 4 ? polyLoop[ 3 ].v : 0 );
+	}
+	else if ( poly.totloop > 4 )
+	{
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+		BlenderTessellatorGL tessGL( *this );
+		tessGL.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+#elif ASSIMP_BLEND_WITH_POLY_2_TRI
+		BlenderTessellatorP2T tessP2T( *this );
+		tessP2T.Tessellate( polyLoop, poly.totloop, triMesh->mvert );
+#endif
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderBMeshConverter::AddFace( int v1, int v2, int v3, int v4 )
+{
+	MFace face;
+	face.v1 = v1;
+	face.v2 = v2;
+	face.v3 = v3;
+	face.v4 = v4;
+	// TODO - Work out how materials work
+	face.mat_nr = 0;
+	triMesh->mface.push_back( face );
+	triMesh->totface = triMesh->mface.size( );
+}
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 93 - 0
code/BlenderBMesh.h

@@ -0,0 +1,93 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderBMesh.h
+ *  @brief Conversion of Blender's new BMesh stuff
+ */
+#ifndef INCLUDED_AI_BLEND_BMESH_H
+#define INCLUDED_AI_BLEND_BMESH_H
+
+#include "LogAux.h"
+
+namespace Assimp
+{
+	// TinyFormatter.h
+	namespace Formatter
+	{
+		template < typename T,typename TR, typename A > class basic_formatter;
+		typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format;
+	}
+
+	// BlenderScene.h
+	namespace Blender
+	{
+		struct Mesh;
+		struct MPoly;
+		struct MLoop;
+	}
+
+	class BlenderBMeshConverter: public LogFunctions< BlenderBMeshConverter >
+	{
+	public:
+		BlenderBMeshConverter( const Blender::Mesh* mesh );
+		~BlenderBMeshConverter( );
+
+		bool ContainsBMesh( ) const;
+
+		const Blender::Mesh* TriangulateBMesh( );
+
+	private:
+		void AssertValidMesh( );
+		void AssertValidSizes( );
+		void PrepareTriMesh( );
+		void DestroyTriMesh( );
+		void ConvertPolyToFaces( const Blender::MPoly& poly );
+		void AddFace( int v1, int v2, int v3, int v4 = 0 );
+
+		const Blender::Mesh* BMesh;
+		Blender::Mesh* triMesh;
+
+		friend class BlenderTessellatorGL;
+		friend class BlenderTessellatorP2T;
+	};
+
+} // end of namespace Assimp
+
+#endif // INCLUDED_AI_BLEND_BMESH_H

+ 7 - 0
code/BlenderLoader.cpp

@@ -51,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include "BlenderIntermediate.h"
 #include "BlenderModifier.h"
+#include "BlenderBMesh.h"
 
 #include "StreamReader.h"
 #include "MemoryIOWrapper.h"
@@ -658,6 +659,12 @@ void BlenderImporter::ConvertMesh(const Scene& /*in*/, const Object* /*obj*/, co
 	ConversionData& conv_data, TempArray<std::vector,aiMesh>&  temp
 	) 
 {
+	BlenderBMeshConverter BMeshConverter( mesh );
+	if ( BMeshConverter.ContainsBMesh( ) )
+	{
+		mesh = BMeshConverter.TriangulateBMesh( );
+	}
+
 	typedef std::pair<const int,size_t> MyPair;
 	if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) {
 		return;

+ 517 - 0
code/BlenderTessellator.cpp

@@ -0,0 +1,517 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderTessellator.cpp
+ *  @brief A simple tessellation wrapper
+ */
+
+#include "AssimpPCH.h"
+
+#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
+
+#include "BlenderDNA.h"
+#include "BlenderScene.h"
+#include "BlenderBMesh.h"
+#include "BlenderTessellator.h"
+
+#define BLEND_TESS_MAGIC ( 0x83ed9ac3 )
+
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+namespace Assimp
+{
+	template< > const std::string LogFunctions< BlenderTessellatorGL >::log_prefix = "BLEND_TESS_GL: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+#ifndef CALLBACK
+#define CALLBACK
+#endif
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorGL::BlenderTessellatorGL( BlenderBMeshConverter& converter ):
+	converter( &converter )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorGL::~BlenderTessellatorGL( )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::Tessellate( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+	AssertVertexCount( vertexCount );
+
+	std::vector< VertexGL > polyLoopGL;
+	GenerateLoopVerts( polyLoopGL, polyLoop, vertexCount, vertices );
+
+	TessDataGL tessData;
+	Tesssellate( polyLoopGL, tessData );
+
+	TriangulateDrawCalls( tessData );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::AssertVertexCount( int vertexCount )
+{
+	if ( vertexCount <= 4 )
+	{
+		ThrowException( "Expected more than 4 vertices for tessellation" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::GenerateLoopVerts( std::vector< VertexGL >& polyLoopGL, const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+	for ( int i = 0; i < vertexCount; ++i )
+	{
+		const MLoop& loopItem = polyLoop[ i ];
+		const MVert& vertex = vertices[ loopItem.v ];
+		polyLoopGL.push_back( VertexGL( vertex.co[ 0 ], vertex.co[ 1 ], vertex.co[ 2 ], loopItem.v, BLEND_TESS_MAGIC ) );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::Tesssellate( std::vector< VertexGL >& polyLoopGL, TessDataGL& tessData )
+{
+	GLUtesselator* tessellator = gluNewTess( );
+	gluTessCallback( tessellator, GLU_TESS_BEGIN_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateBegin ) );
+	gluTessCallback( tessellator, GLU_TESS_END_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateEnd ) );
+	gluTessCallback( tessellator, GLU_TESS_VERTEX_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateVertex ) );
+	gluTessCallback( tessellator, GLU_TESS_COMBINE_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateCombine ) );
+	gluTessCallback( tessellator, GLU_TESS_EDGE_FLAG_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateEdgeFlag ) );
+	gluTessCallback( tessellator, GLU_TESS_ERROR_DATA, reinterpret_cast< void ( CALLBACK * )( ) >( TessellateError ) );
+	gluTessProperty( tessellator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO );
+
+	gluTessBeginPolygon( tessellator, &tessData );
+	gluTessBeginContour( tessellator );
+
+	for ( unsigned int i = 0; i < polyLoopGL.size( ); ++i )
+	{
+		gluTessVertex( tessellator, reinterpret_cast< GLdouble* >( &polyLoopGL[ i ] ), &polyLoopGL[ i ] );
+	}
+
+	gluTessEndContour( tessellator );
+	gluTessEndPolygon( tessellator );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TriangulateDrawCalls( const TessDataGL& tessData )
+{
+	// NOTE - Because we are supplying a callback to GLU_TESS_EDGE_FLAG_DATA we don't technically
+	//        need support for GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN but we'll keep it here in case
+	//        GLU tessellate changes or tristrips and fans are wanted.
+	//        See: http://www.opengl.org/sdk/docs/man2/xhtml/gluTessCallback.xml
+	for ( unsigned int i = 0; i < tessData.drawCalls.size( ); ++i )
+	{
+		const DrawCallGL& drawCallGL = tessData.drawCalls[ i ];
+		const VertexGL* vertices = &tessData.vertices[ drawCallGL.baseVertex ];
+		if ( drawCallGL.drawMode == GL_TRIANGLES )
+		{
+			MakeFacesFromTris( vertices, drawCallGL.vertexCount );
+		}
+		else if ( drawCallGL.drawMode == GL_TRIANGLE_STRIP )
+		{
+			MakeFacesFromTriStrip( vertices, drawCallGL.vertexCount );
+		}
+		else if ( drawCallGL.drawMode == GL_TRIANGLE_FAN )
+		{
+			MakeFacesFromTriFan( vertices, drawCallGL.vertexCount );
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTris( const VertexGL* vertices, int vertexCount )
+{
+	int triangleCount = vertexCount / 3;
+	for ( int i = 0; i < triangleCount; ++i )
+	{
+		int vertexBase = i * 3;
+		converter->AddFace( vertices[ vertexBase + 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTriStrip( const VertexGL* vertices, int vertexCount )
+{
+	int triangleCount = vertexCount - 2;
+	for ( int i = 0; i < triangleCount; ++i )
+	{
+		int vertexBase = i;
+		converter->AddFace( vertices[ vertexBase + 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::MakeFacesFromTriFan( const VertexGL* vertices, int vertexCount )
+{
+	int triangleCount = vertexCount - 2;
+	for ( int i = 0; i < triangleCount; ++i )
+	{
+		int vertexBase = i;
+		converter->AddFace( vertices[ 0 ].index, vertices[ vertexBase + 1 ].index, vertices[ vertexBase + 2 ].index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateBegin( GLenum drawModeGL, void* userData )
+{
+	TessDataGL& tessData = *reinterpret_cast< TessDataGL* >( userData );
+	tessData.drawCalls.push_back( DrawCallGL( drawModeGL, tessData.vertices.size( ) ) );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateEnd( void* )
+{
+	// Do nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateVertex( const void* vtxData, void* userData )
+{
+	TessDataGL& tessData = *reinterpret_cast< TessDataGL* >( userData );
+
+	const VertexGL& vertex = *reinterpret_cast< const VertexGL* >( vtxData );
+	if ( vertex.magic != BLEND_TESS_MAGIC )
+	{
+		ThrowException( "Point returned by GLU Tessellate was probably not one of ours. This indicates we need a new way to store vertex information" );
+	}
+	tessData.vertices.push_back( vertex );
+	if ( tessData.drawCalls.size( ) == 0 )
+	{
+		ThrowException( "\"Vertex\" callback received before \"Begin\"" );
+	}
+	++( tessData.drawCalls.back( ).vertexCount );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateCombine( const GLdouble intersection[ 3 ], const GLdouble* [ 4 ], const GLfloat [ 4 ], GLdouble** out, void* userData )
+{
+	ThrowException( "Intersected polygon loops are not yet supported" );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateEdgeFlag( GLboolean, void* )
+{
+	// Do nothing
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorGL::TessellateError( GLenum errorCode, void* )
+{
+	ThrowException( reinterpret_cast< const char* >( gluErrorString( errorCode ) ) );
+}
+
+#endif // ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if ASSIMP_BLEND_WITH_POLY_2_TRI
+
+namespace Assimp
+{
+	template< > const std::string LogFunctions< BlenderTessellatorP2T >::log_prefix = "BLEND_TESS_P2T: ";
+}
+
+using namespace Assimp;
+using namespace Assimp::Blender;
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorP2T::BlenderTessellatorP2T( BlenderBMeshConverter& converter ):
+	converter( &converter )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+BlenderTessellatorP2T::~BlenderTessellatorP2T( )
+{
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::Tessellate( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices )
+{
+	AssertVertexCount( vertexCount );
+
+	// NOTE - We have to hope that points in a Blender polygon are roughly on the same plane.
+	//        There may be some triangulation artifacts if they are wildly different.
+
+	std::vector< PointP2T > points;
+	Copy3DVertices( polyLoop, vertexCount, vertices, points );
+
+	PlaneP2T plane = FindLLSQPlane( points );
+
+	aiMatrix4x4 transform = GeneratePointTransformMatrix( plane );
+
+	TransformAndFlattenVectices( transform, points );
+
+	std::vector< p2t::Point* > pointRefs;
+	ReferencePoints( points, pointRefs );
+
+	p2t::CDT cdt( pointRefs );
+
+	cdt.Triangulate( );
+	std::vector< p2t::Triangle* > triangles = cdt.GetTriangles( );
+
+	MakeFacesFromTriangles( triangles );
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::AssertVertexCount( int vertexCount )
+{
+	if ( vertexCount <= 4 )
+	{
+		ThrowException( "Expected more than 4 vertices for tessellation" );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::Copy3DVertices( const MLoop* polyLoop, int vertexCount, const std::vector< MVert >& vertices, std::vector< PointP2T >& points ) const
+{
+	points.resize( vertexCount );
+	for ( int i = 0; i < vertexCount; ++i )
+	{
+		const MLoop& loop = polyLoop[ i ];
+		const MVert& vert = vertices[ loop.v ];
+
+		PointP2T& point = points[ i ];
+		point.point3D.Set( vert.co[ 0 ], vert.co[ 1 ], vert.co[ 2 ] );
+		point.index = loop.v;
+		point.magic = BLEND_TESS_MAGIC;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+aiMatrix4x4 BlenderTessellatorP2T::GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const
+{
+	aiVector3D sideA( 1.0f, 0.0f, 0.0f );
+	if ( fabs( plane.normal * sideA ) > 0.999f )
+	{
+		sideA = aiVector3D( 0.0f, 1.0f, 0.0f );
+	}
+
+	aiVector3D sideB( plane.normal ^ sideA );
+	sideB.Normalize( );
+	sideA = sideB ^ plane.normal;
+
+	aiMatrix4x4 result;
+	result.a1 = sideA.x;
+	result.a2 = sideA.y;
+	result.a3 = sideA.z;
+	result.b1 = sideB.x;
+	result.b2 = sideB.y;
+	result.b3 = sideB.z;
+	result.c1 = plane.normal.x;
+	result.c2 = plane.normal.y;
+	result.c3 = plane.normal.z;
+	result.a4 = plane.centre.x;
+	result.b4 = plane.centre.y;
+	result.c4 = plane.centre.z;
+	result.Inverse( );
+
+	return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::TransformAndFlattenVectices( const aiMatrix4x4& transform, std::vector< Blender::PointP2T >& vertices ) const
+{
+	for ( unsigned int i = 0; i < vertices.size( ); ++i )
+	{
+		PointP2T& point = vertices[ i ];
+		point.point3D = transform * point.point3D;
+		point.point2D.set( point.point3D.y, point.point3D.z );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::ReferencePoints( std::vector< Blender::PointP2T >& points, std::vector< p2t::Point* >& pointRefs ) const
+{
+	pointRefs.resize( points.size( ) );
+	for ( unsigned int i = 0; i < points.size( ); ++i )
+	{
+		pointRefs[ i ] = &points[ i ].point2D;
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Yes this is filthy... but we have no choice
+#define OffsetOf( Class, Member ) ( reinterpret_cast< unsigned int >( &( reinterpret_cast< Class* >( NULL )->*( &Class::Member ) ) ) )
+inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
+{
+	int pointOffset = OffsetOf( PointP2T, point2D );
+	PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
+	if ( pointStruct.magic != BLEND_TESS_MAGIC )
+	{
+		ThrowException( "Point returned by poly2tri was probably not one of ours. This indicates we need a new way to store vertex information" );
+	}
+	return pointStruct;
+}
+
+// ------------------------------------------------------------------------------------------------
+void BlenderTessellatorP2T::MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const
+{
+	for ( unsigned int i = 0; i < triangles.size( ); ++i )
+	{
+		p2t::Triangle& Triangle = *triangles[ i ];
+
+		PointP2T& pointA = GetActualPointStructure( *Triangle.GetPoint( 0 ) );
+		PointP2T& pointB = GetActualPointStructure( *Triangle.GetPoint( 1 ) );
+		PointP2T& pointC = GetActualPointStructure( *Triangle.GetPoint( 2 ) );
+
+		converter->AddFace( pointA.index, pointB.index, pointC.index );
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+inline float p2tMax( float a, float b )
+{
+	return a > b ? a : b;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+float BlenderTessellatorP2T::FindLargestMatrixElem( const aiMatrix3x3& mtx ) const
+{
+	float result = 0.0f;
+
+	for ( int x = 0; x < 3; ++x )
+	{
+		for ( int y = 0; y < 3; ++y )
+		{
+			result = p2tMax( fabs( mtx[ x ][ y ] ), result );
+		}
+	}
+
+	return result;
+}
+
+// ------------------------------------------------------------------------------------------------
+// Aparently Assimp doesn't have matrix scaling
+aiMatrix3x3 BlenderTessellatorP2T::ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const
+{
+	aiMatrix3x3 result;
+
+	for ( int x = 0; x < 3; ++x )
+	{
+		for ( int y = 0; y < 3; ++y )
+		{
+			result[ x ][ y ] = mtx[ x ][ y ] * scale;
+		}
+	}
+
+	return result;
+}
+
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+aiVector3D BlenderTessellatorP2T::GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const
+{
+	float scale = FindLargestMatrixElem( mtx );
+	aiMatrix3x3 mc = ScaleMatrix( mtx, 1.0f / scale );
+	mc = mc * mc * mc;
+
+	aiVector3D v( 1.0f );
+	aiVector3D lastV = v;
+	for ( int i = 0; i < 100; ++i )
+	{
+		v = mc * v;
+		v.Normalize( );
+		if ( ( v - lastV ).SquareLength( ) < 1e-16f )
+		{
+			break;
+		}
+		lastV = v;
+	}
+	return v;
+} 
+
+// ------------------------------------------------------------------------------------------------
+// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+PlaneP2T BlenderTessellatorP2T::FindLLSQPlane( const std::vector< PointP2T >& points ) const
+{
+	PlaneP2T result;
+
+	aiVector3D sum( 0.0f );
+	for ( unsigned int i = 0; i < points.size( ); ++i )
+	{
+		sum += points[ i ].point3D;
+	}
+	result.centre = sum * ( 1.0f / points.size( ) );
+
+	float sumXX = 0.0f;
+	float sumXY = 0.0f;
+	float sumXZ = 0.0f;
+	float sumYY = 0.0f;
+	float sumYZ = 0.0f;
+	float sumZZ = 0.0f;
+	for ( unsigned int i = 0; i < points.size( ); ++i )
+	{
+		aiVector3D offset = points[ i ].point3D - result.centre;
+		sumXX += offset.x * offset.x;
+		sumXY += offset.x * offset.y;
+		sumXZ += offset.x * offset.z;
+		sumYY += offset.y * offset.y;
+		sumYZ += offset.y * offset.z;
+		sumZZ += offset.z * offset.z;
+	}
+
+	aiMatrix3x3 mtx( sumXX, sumXY, sumXZ, sumXY, sumYY, sumYZ, sumXZ, sumYZ, sumZZ );
+
+	float det = mtx.Determinant( );
+	if ( det == 0.0f )
+	{
+		result.normal = aiVector3D( 0.0f );
+	}
+	else
+	{
+		aiMatrix3x3 invMtx = mtx;
+		invMtx.Inverse( );
+		result.normal = GetEigenVectorFromLargestEigenValue( invMtx );
+	}
+
+	return result;
+}
+
+#endif // ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#endif // ASSIMP_BUILD_NO_BLEND_IMPORTER

+ 199 - 0
code/BlenderTessellator.h

@@ -0,0 +1,199 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2013, assimp team
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms, 
+with or without modification, are permitted provided that the 
+following conditions are met:
+
+* Redistributions of source code must retain the above
+  copyright notice, this list of conditions and the
+  following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+  copyright notice, this list of conditions and the
+  following disclaimer in the documentation and/or other
+  materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+  contributors may be used to endorse or promote products
+  derived from this software without specific prior
+  written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+*/
+
+/** @file  BlenderTessellator.h
+ *  @brief A simple tessellation wrapper
+ */
+#ifndef INCLUDED_AI_BLEND_TESSELLATOR_H
+#define INCLUDED_AI_BLEND_TESSELLATOR_H
+
+// Use these to toggle between GLU Tessellate or poly2tri
+#define ASSIMP_BLEND_WITH_GLU_TESSELLATE 1
+#define ASSIMP_BLEND_WITH_POLY_2_TRI 1
+
+#include "LogAux.h"
+
+#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if defined( WIN32 ) || defined( _WIN32 ) || defined( _MSC_VER )
+#include <windows.h>
+#endif
+#include <GL/glu.h>
+
+namespace Assimp
+{
+	class BlenderBMeshConverter;
+
+	// TinyFormatter.h
+	namespace Formatter
+	{
+		template < typename T,typename TR, typename A > class basic_formatter;
+		typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format;
+	}
+
+	// BlenderScene.h
+	namespace Blender
+	{
+		struct MLoop;
+		struct MVert;
+
+		struct VertexGL
+		{
+			GLdouble X;
+			GLdouble Y;
+			GLdouble Z;
+			int index;
+			int magic;
+
+			VertexGL( GLdouble X, GLdouble Y, GLdouble Z, int index, int magic ): X( X ), Y( Y ), Z( Z ), index( index ), magic( magic ) { }
+		};
+
+		struct DrawCallGL
+		{
+			GLenum drawMode;
+			int baseVertex;
+			int vertexCount;
+
+			DrawCallGL( GLenum drawMode, int baseVertex ): drawMode( drawMode ), baseVertex( baseVertex ), vertexCount( 0 ) { }
+		};
+
+		struct TessDataGL
+		{
+			std::vector< DrawCallGL > drawCalls;
+			std::vector< VertexGL > vertices;
+		};
+	}
+
+	class BlenderTessellatorGL: public LogFunctions< BlenderTessellatorGL >
+	{
+	public:
+		BlenderTessellatorGL( BlenderBMeshConverter& converter );
+		~BlenderTessellatorGL( );
+
+		void Tessellate( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+
+	private:
+		void AssertVertexCount( int vertexCount );
+		void GenerateLoopVerts( std::vector< Blender::VertexGL >& polyLoopGL, const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+		void Tesssellate( std::vector< Blender::VertexGL >& polyLoopGL, Blender::TessDataGL& tessData );
+		void TriangulateDrawCalls( const Blender::TessDataGL& tessData );
+		void MakeFacesFromTris( const Blender::VertexGL* vertices, int vertexCount );
+		void MakeFacesFromTriStrip( const Blender::VertexGL* vertices, int vertexCount );
+		void MakeFacesFromTriFan( const Blender::VertexGL* vertices, int vertexCount );
+
+		static void TessellateBegin( GLenum drawModeGL, void* userData );
+		static void TessellateEnd( void* userData );
+		static void TessellateVertex( const void* vtxData, void* userData );
+		static void TessellateCombine( const GLdouble intersection[ 3 ], const GLdouble* [ 4 ], const GLfloat [ 4 ], GLdouble** out, void* userData );
+		static void TessellateEdgeFlag( GLboolean edgeFlag, void* userData );
+		static void TessellateError( GLenum errorCode, void* userData );
+
+		BlenderBMeshConverter* converter;
+	};
+} // end of namespace Assimp
+
+#endif // ASSIMP_BLEND_WITH_GLU_TESSELLATE
+
+#if ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#include "../contrib/poly2tri/poly2tri/poly2tri.h"
+
+namespace Assimp
+{
+	class BlenderBMeshConverter;
+
+	// TinyFormatter.h
+	namespace Formatter
+	{
+		template < typename T,typename TR, typename A > class basic_formatter;
+		typedef class basic_formatter< char, std::char_traits< char >, std::allocator< char > > format;
+	}
+
+	// BlenderScene.h
+	namespace Blender
+	{
+		struct MLoop;
+		struct MVert;
+
+		struct PointP2T
+		{
+			aiVector3D point3D;
+			p2t::Point point2D;
+			int magic;
+			int index;
+		};
+
+		struct PlaneP2T
+		{
+			aiVector3D centre;
+			aiVector3D normal;
+		};
+	}
+
+	class BlenderTessellatorP2T: public LogFunctions< BlenderTessellatorP2T >
+	{
+	public:
+		BlenderTessellatorP2T( BlenderBMeshConverter& converter );
+		~BlenderTessellatorP2T( );
+
+		void Tessellate( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices );
+
+	private:
+		void AssertVertexCount( int vertexCount );
+		void Copy3DVertices( const Blender::MLoop* polyLoop, int vertexCount, const std::vector< Blender::MVert >& vertices, std::vector< Blender::PointP2T >& targetVertices ) const;
+		aiMatrix4x4 GeneratePointTransformMatrix( const Blender::PlaneP2T& plane ) const;
+		void TransformAndFlattenVectices( const aiMatrix4x4& transform, std::vector< Blender::PointP2T >& vertices ) const;
+		void ReferencePoints( std::vector< Blender::PointP2T >& points, std::vector< p2t::Point* >& pointRefs ) const;
+		inline Blender::PointP2T& GetActualPointStructure( p2t::Point& point ) const;
+		void MakeFacesFromTriangles( std::vector< p2t::Triangle* >& triangles ) const;
+
+		// Adapted from: http://missingbytes.blogspot.co.uk/2012/06/fitting-plane-to-point-cloud.html
+		float FindLargestMatrixElem( const aiMatrix3x3& mtx ) const;
+		aiMatrix3x3 ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const;
+		aiVector3D GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const;
+		Blender::PlaneP2T FindLLSQPlane( const std::vector< Blender::PointP2T >& points ) const;
+
+		BlenderBMeshConverter* converter;
+	};
+} // end of namespace Assimp
+
+#endif // ASSIMP_BLEND_WITH_POLY_2_TRI
+
+#endif // INCLUDED_AI_BLEND_TESSELLATOR_H

+ 16 - 0
workspaces/vc9/assimp.vcproj

@@ -1867,6 +1867,14 @@
 				<Filter
 					Name="blend"
 					>
+					<File
+						RelativePath="..\..\code\BlenderBMesh.cpp"
+						>
+					</File>
+					<File
+						RelativePath="..\..\code\BlenderBMesh.h"
+						>
+					</File>
 					<File
 						RelativePath="..\..\code\BlenderDNA.cpp"
 						>
@@ -1911,6 +1919,14 @@
 						RelativePath="..\..\code\BlenderSceneGen.h"
 						>
 					</File>
+					<File
+						RelativePath="..\..\code\BlenderTessellator.cpp"
+						>
+					</File>
+					<File
+						RelativePath="..\..\code\BlenderTessellator.h"
+						>
+					</File>
 				</Filter>
 				<Filter
 					Name="q3bsp"