diff --git a/code/BlenderBMesh.cpp b/code/BlenderBMesh.cpp new file mode 100644 index 000000000..83ccd37fb --- /dev/null +++ b/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 diff --git a/code/BlenderBMesh.h b/code/BlenderBMesh.h new file mode 100644 index 000000000..47afbf437 --- /dev/null +++ b/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 diff --git a/code/BlenderLoader.cpp b/code/BlenderLoader.cpp index 9fe93fd2a..4bbf5f9f6 100644 --- a/code/BlenderLoader.cpp +++ b/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& temp ) { + BlenderBMeshConverter BMeshConverter( mesh ); + if ( BMeshConverter.ContainsBMesh( ) ) + { + mesh = BMeshConverter.TriangulateBMesh( ); + } + typedef std::pair MyPair; if ((!mesh->totface && !mesh->totloop) || !mesh->totvert) { return; diff --git a/code/BlenderTessellator.cpp b/code/BlenderTessellator.cpp new file mode 100644 index 000000000..e2d51cf25 --- /dev/null +++ b/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 diff --git a/code/BlenderTessellator.h b/code/BlenderTessellator.h new file mode 100644 index 000000000..53c560594 --- /dev/null +++ b/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 +#endif +#include + +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 diff --git a/workspaces/vc9/assimp.vcproj b/workspaces/vc9/assimp.vcproj index 724944074..120577b67 100644 --- a/workspaces/vc9/assimp.vcproj +++ b/workspaces/vc9/assimp.vcproj @@ -1867,6 +1867,14 @@ + + + + @@ -1911,6 +1919,14 @@ RelativePath="..\..\code\BlenderSceneGen.h" > + + + +