You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
16 KiB
558 lines
16 KiB
/*
|
|
**************************************************************************
|
|
description
|
|
--------------------
|
|
copyright : (C) 2003 by Leon Pennington
|
|
email : leon@leonscape.co.uk
|
|
**************************************************************************
|
|
|
|
**************************************************************************
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
**************************************************************************/
|
|
|
|
|
|
#include "pmmesh.h"
|
|
|
|
#include <klocale.h>
|
|
#include "pmxmlhelper.h"
|
|
#include "pmmeshedit.h"
|
|
#include "pmmemento.h"
|
|
#include "pmtriangle.h"
|
|
#include "pm3dcontrolpoint.h"
|
|
#include "pmvectorcontrolpoint.h"
|
|
|
|
const PMVector insideVectorDefault = PMVector( 0.0, 0.0, 0.0 );
|
|
|
|
PMDefinePropertyClass( PMMesh, PMMeshProperty );
|
|
|
|
class PMMeshMemento : public PMMemento
|
|
{
|
|
public:
|
|
/**
|
|
* Creates a memento for the object originator
|
|
*/
|
|
PMMeshMemento( PMObject* originator ) : PMMemento( originator )
|
|
{
|
|
m_bTriangleMementosSaved = false;
|
|
m_triangleMementos.setAutoDelete( true );
|
|
}
|
|
/**
|
|
* Deletes the memento
|
|
*/
|
|
virtual ~PMMeshMemento( )
|
|
{
|
|
m_triangleMementos.clear( );
|
|
}
|
|
|
|
/**
|
|
* Saves the triangles memento data
|
|
*/
|
|
void setTriangleMementos( const TQPtrList<PMMemento>& list )
|
|
{
|
|
if ( !m_bTriangleMementosSaved )
|
|
{
|
|
TQPtrListIterator<PMMemento> Itr( list );
|
|
PMMemento* m;
|
|
while( ( m = Itr.current( ) ) != 0 )
|
|
{
|
|
m_triangleMementos.append( m );
|
|
++Itr;
|
|
}
|
|
|
|
m_bTriangleMementosSaved = true;
|
|
addChange( PMCData );
|
|
}
|
|
}
|
|
/**
|
|
* Returns the triangles memento data
|
|
*/
|
|
TQPtrList<PMMemento> triangleMementos( ) const
|
|
{
|
|
if ( !m_bTriangleMementosSaved )
|
|
kdError( PMArea ) << "Triangles mementos not saved in PMMeshMemento::triangleMementos\n";
|
|
return m_triangleMementos;
|
|
}
|
|
/**
|
|
* Returns true if the triangle mementos have been saved
|
|
*/
|
|
bool triangleMementosSaved( ) const { return m_bTriangleMementosSaved; }
|
|
private:
|
|
TQPtrList<PMMemento> m_triangleMementos;
|
|
bool m_bTriangleMementosSaved;
|
|
};
|
|
|
|
PMMetaObject* PMMesh::s_pMetaObject = 0;
|
|
PMObject* createNewMesh( PMPart* part )
|
|
{
|
|
return new PMMesh( part );
|
|
}
|
|
|
|
PMMesh::PMMesh( PMPart* part )
|
|
: Base( part )
|
|
{
|
|
m_hierarchy = true;
|
|
m_enableInsideVector = false;
|
|
m_insideVector = insideVectorDefault;
|
|
}
|
|
|
|
PMMesh::PMMesh( const PMMesh& m )
|
|
: Base( m )
|
|
{
|
|
m_hierarchy = m.m_hierarchy;
|
|
m_enableInsideVector = m.m_enableInsideVector;
|
|
m_insideVector = m.m_insideVector;
|
|
}
|
|
|
|
PMMesh::~PMMesh( )
|
|
{
|
|
}
|
|
|
|
TQString PMMesh::description( ) const
|
|
{
|
|
return i18n( "mesh" );
|
|
}
|
|
|
|
void PMMesh::serialize( TQDomElement& e, TQDomDocument& doc ) const
|
|
{
|
|
e.setAttribute( "hierarchy", m_hierarchy );
|
|
e.setAttribute( "enable_inside_vector", m_enableInsideVector );
|
|
e.setAttribute( "inside_vector", m_insideVector.serializeXML( ) );
|
|
Base::serialize( e, doc );
|
|
}
|
|
|
|
void PMMesh::readAttributes( const PMXMLHelper& h )
|
|
{
|
|
m_hierarchy = h.boolAttribute( "hierarchy", true );
|
|
m_enableInsideVector = h.boolAttribute( "enable_inside_vector", false );
|
|
m_insideVector = h.vectorAttribute( "inside_vector", insideVectorDefault );
|
|
Base::readAttributes( h );
|
|
}
|
|
|
|
PMMetaObject* PMMesh::metaObject( ) const
|
|
{
|
|
if( !s_pMetaObject )
|
|
{
|
|
s_pMetaObject = new PMMetaObject( "Mesh", Base::metaObject( ), createNewMesh );
|
|
|
|
s_pMetaObject->addProperty(
|
|
new PMMeshProperty( "hierarchy", &PMMesh::setHierarchy, &PMMesh::hierarchy ) );
|
|
s_pMetaObject->addProperty(
|
|
new PMMeshProperty( "insideVectorEnabled", &PMMesh::enableInsideVector, &PMMesh::isInsideVectorEnabled ) );
|
|
s_pMetaObject->addProperty(
|
|
new PMMeshProperty( "insideVector", &PMMesh::setInsideVector, &PMMesh::insideVector ) );
|
|
}
|
|
return s_pMetaObject;
|
|
}
|
|
|
|
void PMMesh::cleanUp( ) const
|
|
{
|
|
if( s_pMetaObject )
|
|
{
|
|
delete s_pMetaObject;
|
|
s_pMetaObject = 0;
|
|
}
|
|
Base::cleanUp( );
|
|
}
|
|
|
|
void PMMesh::setHierarchy( bool h )
|
|
{
|
|
if( h != m_hierarchy )
|
|
{
|
|
if( m_pMemento )
|
|
m_pMemento->addData( s_pMetaObject, PMHierarchyID, m_hierarchy );
|
|
m_hierarchy = h;
|
|
}
|
|
}
|
|
|
|
void PMMesh::enableInsideVector( bool eiv )
|
|
{
|
|
if( eiv != m_enableInsideVector )
|
|
{
|
|
if( m_pMemento )
|
|
m_pMemento->addData( s_pMetaObject, PMEnableInsideVectorID, m_enableInsideVector );
|
|
m_enableInsideVector = eiv;
|
|
}
|
|
}
|
|
|
|
void PMMesh::setInsideVector( const PMVector& iv )
|
|
{
|
|
if( iv != m_insideVector )
|
|
{
|
|
if( m_pMemento )
|
|
m_pMemento->addData( s_pMetaObject, PMInsideVectorID, m_insideVector );
|
|
m_insideVector = iv;
|
|
}
|
|
}
|
|
|
|
PMDialogEditBase* PMMesh::editWidget( TQWidget* parent ) const
|
|
{
|
|
return new PMMeshEdit( parent );
|
|
}
|
|
|
|
void PMMesh::createMemento( )
|
|
{
|
|
delete m_pMemento;
|
|
m_pMemento = new PMMeshMemento( this );
|
|
}
|
|
|
|
void PMMesh::restoreMemento( PMMemento* s )
|
|
{
|
|
PMMeshMemento* m = ( PMMeshMemento * ) s;
|
|
PMMementoDataIterator it( s );
|
|
PMMementoData* data;
|
|
|
|
for( ; it.current( ); ++it )
|
|
{
|
|
data = it.current( );
|
|
if( data->objectType( ) == s_pMetaObject )
|
|
{
|
|
switch( data->valueID( ) )
|
|
{
|
|
case PMHierarchyID:
|
|
setHierarchy( data->boolData( ) );
|
|
break;
|
|
case PMEnableInsideVectorID:
|
|
enableInsideVector( data->boolData( ) );
|
|
break;
|
|
case PMInsideVectorID:
|
|
setInsideVector( data->vectorData( ) );
|
|
break;
|
|
default:
|
|
kdError( PMArea ) << "Wrong ID in PMMesh::restoreMemento\n";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( m->triangleMementosSaved( ) )
|
|
{
|
|
int numChildren = countChildren( );
|
|
PMMemento* tm;
|
|
TQPtrList<PMMemento> list = m->triangleMementos( );
|
|
TQPtrListIterator<PMMemento> Itr( list );
|
|
for ( int i = 0; i < numChildren && ( tm = Itr.current( ) ) != 0; ++i, ++Itr )
|
|
childAt( i )->restoreMemento( tm );
|
|
}
|
|
|
|
Base::restoreMemento( s );
|
|
}
|
|
|
|
void PMMesh::controlPoints( PMControlPointList& list )
|
|
{
|
|
unsigned numChildren = countChildren();
|
|
PMTriangle *obj;
|
|
pointToPoint ptp;
|
|
bool found;
|
|
PMVector point, normal;
|
|
PMControlPoint* listP;
|
|
PM3DControlPoint* cp;
|
|
PMVectorControlPoint* vp;
|
|
int currentPoint = 0;
|
|
int firstNormal = numChildren * 3;
|
|
int currentNormal = firstNormal;
|
|
|
|
m_pointToPointList.clear( );
|
|
for ( unsigned i = 0; i < numChildren; ++i )
|
|
{
|
|
if ( childAt( i )->isA( "Triangle" ) )
|
|
{
|
|
|
|
obj = ( PMTriangle * ) childAt( i );
|
|
ptp.object = obj;
|
|
for ( unsigned j = 0; j < 3; ++j )
|
|
{
|
|
found = false;
|
|
ptp.pointID = j;
|
|
point = obj->point( j );
|
|
|
|
for( listP = list.first( ); listP; listP = list.next( ) )
|
|
{
|
|
if ( listP->id( ) < firstNormal && point == listP->position( ) )
|
|
{
|
|
found = true;
|
|
ptp.listID = listP->id( );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
cp = new PM3DControlPoint( point, currentPoint,
|
|
i18n( "Mesh Point " + currentPoint ) );
|
|
list.append( cp );
|
|
ptp.listID = currentPoint++;
|
|
}
|
|
|
|
m_pointToPointList.append( ptp );
|
|
|
|
if ( obj->isSmoothTriangle( ) )
|
|
{
|
|
found = false;
|
|
ptp.pointID = j + 3;
|
|
normal = obj->normal( j );
|
|
|
|
for ( listP = list.first( ); listP; listP = list.next( ) )
|
|
{
|
|
if ( listP->id( ) >= firstNormal )
|
|
{
|
|
vp = ( PMVectorControlPoint* ) listP;
|
|
if ( vp->basePoint( ) == point &&
|
|
vp->vector( ) == normal )
|
|
{
|
|
found = true;
|
|
ptp.listID = listP->id( );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !found )
|
|
{
|
|
vp = new PMVectorControlPoint( point, normal, currentNormal,
|
|
i18n( "Mesh Normal " + currentNormal ) );
|
|
list.append( vp );
|
|
ptp.listID = currentNormal++;
|
|
}
|
|
|
|
m_pointToPointList.append( ptp );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PMMesh::controlPointsChangedList( PMControlPointList& list, PMObjectList& objList )
|
|
{
|
|
int numChildren = countChildren( );
|
|
PMControlPoint* p;
|
|
TQValueList<pointToPoint>::ConstIterator ptpItr = m_pointToPointList.begin( );
|
|
TQPtrList<PMMemento> mementoList;
|
|
PMTriangle *obj;
|
|
PMVector p0, p1, p2;
|
|
PM3DControlPoint* cp0, * cp1, * cp2;
|
|
bool bp0, bp1, bp2;
|
|
PMVector n0, n1, n2;
|
|
PMVectorControlPoint* cn0, * cn1, * cn2;
|
|
bool bn0, bn1, bn2;
|
|
PMVector triangleNormal;
|
|
double d, normalDirection = 1.0;
|
|
bool found, validNormal, validTriangles = true;
|
|
int listID, pointID, numCP;
|
|
|
|
// have to cache changed values because checking once changes them to false
|
|
TQMemArray<bool> changed( list.count( ) );
|
|
p = list.first( );
|
|
for ( int i = 0; p; ++i, p = list.next( ) )
|
|
changed[i] = p->changed( );
|
|
|
|
for ( int i = 0; i < numChildren && validTriangles; ++i )
|
|
{
|
|
if ( childAt( i )->isA( "Triangle" ) )
|
|
{
|
|
obj = ( PMTriangle* )childAt( i );
|
|
obj->createMemento( );
|
|
objList.append( obj );
|
|
validNormal = false;
|
|
|
|
if ( obj->isSmoothTriangle( ) )
|
|
numCP = 6;
|
|
else
|
|
numCP = 3;
|
|
|
|
cp0 = cp1 = cp2 = 0;
|
|
cn0 = cn1 = cn2 = 0;
|
|
|
|
bp0 = bp1 = bp2 = bn0 = bn1 = bn2 = false;
|
|
|
|
for ( int j = 0; j < numCP; ++j, ++ptpItr )
|
|
{
|
|
|
|
listID = (*ptpItr).listID;
|
|
pointID = (*ptpItr).pointID;
|
|
found = false;
|
|
p = list.first( );
|
|
for ( int k = 0; p && !found; p = list.next( ), ++k )
|
|
{
|
|
if( listID == p->id( ) )
|
|
{
|
|
switch( pointID )
|
|
{
|
|
case 0:
|
|
cp0 = ( PM3DControlPoint* ) p;
|
|
p0 = cp0->point( );
|
|
bp0 = changed[ k ];
|
|
break;
|
|
case 1:
|
|
cp1 = ( PM3DControlPoint* ) p;
|
|
p1 = cp1->point( );
|
|
bp1 = changed[ k ];
|
|
break;
|
|
case 2:
|
|
cp2 = ( PM3DControlPoint* ) p;
|
|
p2 = cp2->point( );
|
|
bp2 = changed[ k ];
|
|
break;
|
|
case 3:
|
|
cn0 = ( PMVectorControlPoint* ) p;
|
|
n0 = cn0->vector( );
|
|
bn0 = changed[ k ];
|
|
break;
|
|
case 4:
|
|
cn1 = ( PMVectorControlPoint* ) p;
|
|
n1 = cn1->vector( );
|
|
bn1 = changed[ k ];
|
|
break;
|
|
case 5:
|
|
cn2 = ( PMVectorControlPoint* ) p;
|
|
n2 = cn2->vector( );
|
|
bn2 = changed[ k ];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( obj->isSmoothTriangle( ) )
|
|
{
|
|
triangleNormal = PMVector::cross( obj->point( 1 ) - obj->point( 0 ),
|
|
obj->point( 2 ) - obj->point( 0 ) );
|
|
normalDirection = PMVector::dot( triangleNormal, obj->normal( 0 ) );
|
|
if( approxZero( normalDirection ) )
|
|
normalDirection = PMVector::dot( triangleNormal, obj->normal( 1 ) );
|
|
if( approxZero( normalDirection ) )
|
|
normalDirection = PMVector::dot( triangleNormal, obj->normal( 2 ) );
|
|
if( normalDirection < 0 )
|
|
triangleNormal = -triangleNormal;
|
|
if( !approxZero( triangleNormal.abs( ) ) )
|
|
{
|
|
validNormal = true;
|
|
triangleNormal /= triangleNormal.abs( );
|
|
}
|
|
}
|
|
|
|
if ( bp0 )
|
|
{
|
|
if ( !( p0.approxEqual( p1 ) || p0.approxEqual( p2 ) ) )
|
|
obj->setPoint( 0, p0 );
|
|
else
|
|
{
|
|
validTriangles = false;
|
|
cp0->setPoint( obj->point( 0 ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bp1 )
|
|
{
|
|
if ( !( p1.approxEqual( p0 ) || p1.approxEqual( p2 ) ) )
|
|
obj->setPoint( 1, p1 );
|
|
else
|
|
{
|
|
validTriangles = false;
|
|
cp1->setPoint( obj->point( 1 ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( bp2 )
|
|
{
|
|
if ( !( p2.approxEqual( p0 ) || p2.approxEqual( p1 ) ) )
|
|
obj->setPoint( 2, p2 );
|
|
else
|
|
{
|
|
validTriangles = false;
|
|
cp2->setPoint( obj->point( 2 ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( obj->isSmoothTriangle( ) )
|
|
{
|
|
if ( bn0 )
|
|
{
|
|
if( validNormal )
|
|
{
|
|
d = PMVector::dot( triangleNormal, n0 );
|
|
if( d > 0 )
|
|
obj->setNormal( 0, n0 );
|
|
else
|
|
{
|
|
obj->setNormal( 0, n0 - ( d - 1e-5 ) * triangleNormal );
|
|
cn0->setVector( obj->normal( 0 ) );
|
|
}
|
|
}
|
|
else
|
|
cn0->setVector( obj->normal( 0 ) );
|
|
}
|
|
|
|
if ( bn1 )
|
|
{
|
|
if( validNormal )
|
|
{
|
|
d = PMVector::dot( triangleNormal, n1 );
|
|
if( d > 0 )
|
|
obj->setNormal( 1, n1 );
|
|
else
|
|
{
|
|
obj->setNormal( 1, n1 - ( d - 1e-5 ) * triangleNormal );
|
|
cn1->setVector( obj->normal( 1 ) );
|
|
}
|
|
}
|
|
else
|
|
cn1->setVector( obj->normal( 1 ) );
|
|
}
|
|
|
|
if ( bn2 )
|
|
{
|
|
if( validNormal )
|
|
{
|
|
d = PMVector::dot( triangleNormal, n2 );
|
|
if( d > 0 )
|
|
obj->setNormal( 2, n2 );
|
|
else
|
|
{
|
|
obj->setNormal( 2, n2 - ( d - 1e-5 ) * triangleNormal );
|
|
cn2->setVector( obj->normal( 2 ) );
|
|
}
|
|
}
|
|
else
|
|
cn2->setVector( obj->normal( 2 ) );
|
|
}
|
|
}
|
|
|
|
mementoList.append( obj->takeMemento( ) );
|
|
|
|
if ( !validTriangles )
|
|
{
|
|
PMMemento *tm;
|
|
for ( int j = i; j >= 0; --j )
|
|
{
|
|
if ( ( tm = mementoList.getLast( ) ) )
|
|
{
|
|
childAt( j )->restoreMemento( tm );
|
|
delete tm;
|
|
mementoList.removeLast( );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( validTriangles )
|
|
{
|
|
if ( m_pMemento )
|
|
( ( PMMeshMemento * ) m_pMemento )->setTriangleMementos( mementoList );
|
|
objList.append( this );
|
|
setViewStructureChanged( );
|
|
}
|
|
}
|