#ifndef ASSIMP_BUILD_NO_BLEND_IMPORTER
#include "BlenderDNA.h"
#include "BlenderScene.h"
#include "BlenderBMesh.h"
#include "BlenderTessellator.h"
#include <stddef.h>
static const unsigned int BLEND_TESS_MAGIC = 0x83ed9ac3;
#if ASSIMP_BLEND_WITH_GLU_TESSELLATE
namspace 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 )
{
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 )
{
const 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 )
{
const 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 )
{
const 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* )
{
}
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* )
{
}
void BlenderTessellatorGL::TessellateError( GLenum errorCode, void* )
{
ThrowException( reinterpret_cast< const char* >( gluErrorString( errorCode ) ) );
}
#endif
#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 );
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 ( std::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 ( size_t 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 ( size_t i = 0; i < points.size( ); ++i )
{
pointRefs[ i ] = &points[ i ].point2D;
}
}
inline PointP2T& BlenderTessellatorP2T::GetActualPointStructure( p2t::Point& point ) const
{
unsigned int pointOffset = offsetof( PointP2T, point2D );
PointP2T& pointStruct = *reinterpret_cast< PointP2T* >( reinterpret_cast< char* >( &point ) - pointOffset );
if ( pointStruct.magic != static_cast<int>( 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 ( size_t 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;
}
float BlenderTessellatorP2T::FindLargestMatrixElem( const aiMatrix3x3& mtx ) const
{
float result = 0.0f;
for ( unsigned int x = 0; x < 3; ++x )
{
for ( unsigned int y = 0; y < 3; ++y )
{
result = p2tMax( std::fabs( mtx[ x ][ y ] ), result );
}
}
return result;
}
aiMatrix3x3 BlenderTessellatorP2T::ScaleMatrix( const aiMatrix3x3& mtx, float scale ) const
{
aiMatrix3x3 result;
for ( unsigned int x = 0; x < 3; ++x )
{
for ( unsigned int y = 0; y < 3; ++y )
{
result[ x ][ y ] = mtx[ x ][ y ] * scale;
}
}
return result;
}
aiVector3D BlenderTessellatorP2T::GetEigenVectorFromLargestEigenValue( const aiMatrix3x3& mtx ) const
{
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;
}
PlaneP2T BlenderTessellatorP2T::FindLLSQPlane( const std::vector< PointP2T >& points ) const
{
PlaneP2T result;
aiVector3D sum( 0.0 );
for ( size_t i = 0; i < points.size( ); ++i )
{
sum += points[ i ].point3D;
}
result.centre = sum * (ai_real)( 1.0 / points.size( ) );
ai_real sumXX = 0.0;
ai_real sumXY = 0.0;
ai_real sumXZ = 0.0;
ai_real sumYY = 0.0;
ai_real sumYZ = 0.0;
ai_real sumZZ = 0.0;
for ( size_t 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 );
const ai_real 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
#endif