// Aqsis // Copyright © 1997 - 2001, Paul C. Gregory // // Contact: pgregory@aqsis.org // // This library 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. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA /** \file \brief Implements the classes and support structures for handling polygons. \author Paul C. Gregory (pgregory@aqsis.org) */ #include "aqsis.h" #include "polygon.h" #include "patch.h" #include "imagebuffer.h" START_NAMESPACE( Aqsis ) //--------------------------------------------------------------------- /** Return the boundary extents in camera space of the polygon */ CqBound CqPolygonBase::Bound() const { CqVector3D vecA( FLT_MAX, FLT_MAX, FLT_MAX ); CqVector3D vecB( -FLT_MAX, -FLT_MAX, -FLT_MAX ); TqInt i, n; n = NumVertices(); for ( i = 0; i < n; i++ ) { CqVector3D vecV = PolyP( i ); if ( vecV.x() < vecA.x() ) vecA.x( vecV.x() ); if ( vecV.y() < vecA.y() ) vecA.y( vecV.y() ); if ( vecV.x() > vecB.x() ) vecB.x( vecV.x() ); if ( vecV.y() > vecB.y() ) vecB.y( vecV.y() ); if ( vecV.z() < vecA.z() ) vecA.z( vecV.z() ); if ( vecV.z() > vecB.z() ) vecB.z( vecV.z() ); } CqBound B; B.vecMin() = vecA; B.vecMax() = vecB; return ( B ); } //--------------------------------------------------------------------- /** Split the polygon into bilinear patches. * This split has a special way of dealing with triangles. To avoid the problem of grids with degenerate micro polygons * ( micro polygons with two points the same. ) we treat a triangle as a parallelogram, by extending the fourth point out. * Then everything on the left side of the vector between points 1 and 2 ( on the same side as point 0 ) is rendered, * everything on the right (the same side as point 3) is not. */ TqInt CqPolygonBase::Split( std::vector >& aSplits ) { CqVector3D vecN(0,0,0); TqInt indexA, indexB, indexC, indexD; // We need to take into account Orientation here, even though most other // primitives leave it up to the CalcNormals function on the MPGrid, because we // are forcing N to be setup here, so clockwise nature is important. TqBool CSO = pTransform()->GetHandedness(pTransform()->Time(0)); TqBool O = pAttributes() ->GetIntegerAttribute( "System", "Orientation" ) [ 0 ] != 0; indexA = 0; indexB = 1; TqInt iUses = PolyUses(); TqInt n = NumVertices(); // Get the normals, or calculate the facet normal if not specified. if ( !bHasVar(EnvVars_N) ) { CqVector3D vecA = PolyP( indexA ); // Find two suitable vectors, and produce a geometric normal to use. TqInt i = 1; CqVector3D vecN0, vecN1; while ( i < n ) { vecN0 = static_cast( PolyP( i ) ) - vecA; if ( vecN0.Magnitude() > FLT_EPSILON ) break; i++; } i++; while ( i < n ) { vecN1 = static_cast( PolyP( i ) ) - vecA; if ( vecN1.Magnitude() > FLT_EPSILON && vecN1 != vecN0 ) break; i++; } vecN = vecN0 % vecN1; vecN = ( (O && CSO) || (!O && !CSO) ) ? vecN : -vecN; vecN.Unit(); } // Start by splitting the polygon into 4 point patches. // Get the normals, or calculate the facet normal if not specified. TqInt cNew = 0; TqInt i; for ( i = 2; i < n; i += 2 ) { indexC = indexD = i; if ( n > i + 1 ) indexD = i + 1; // Create bilinear patches boost::shared_ptr pNew( new CqSurfacePatchBilinear() ); //ADDREF( pNew ); pNew->SetSurfaceParameters( Surface() ); /* Comment this out for now as it breaks Bug948827 in a strange way, needs more investigation */ /* if ( indexC == indexD ) { // Calculate which point in the triangle produces an angle with it's neighbours that is closest to 90 degrees. // Placing the phantom point opposite this point will ensure the best orientation of the final grids, reducing // shading artefacts. CqVector3D pointA = PolyP(indexA); CqVector3D pointB = PolyP(indexB); CqVector3D pointC = PolyP(indexC); TqFloat aA = 1.5707f - acosf((pointB - pointA).Unit()*(pointC - pointA).Unit()); TqFloat aB = 1.5707f - acosf((pointA - pointB).Unit()*(pointC - pointB).Unit()); TqFloat aC = 1.5707f - acosf((pointA - pointC).Unit()*(pointB - pointC).Unit()); TqInt cycle = 0; if( aB < aA && aB < aC ) cycle = 1; else if( aC < aA && aC < aB) cycle = 2; for(; cycle>0; --cycle) { TqInt temp = indexA; indexA = indexB; indexB = indexC; indexC = indexD = temp; } } */ TqInt iUPA, iUPB, iUPC, iUPD; TqInt iUPAf, iUPBf, iUPCf, iUPDf; // Get the indices for varying variables. iUPA = PolyIndex( indexA ); iUPB = PolyIndex( indexB ); iUPC = PolyIndex( indexC ); iUPD = PolyIndex( indexD ); // Get the indices for facevarying variables. iUPAf = FaceVaryingIndex( indexA ); iUPBf = FaceVaryingIndex( indexB ); iUPCf = FaceVaryingIndex( indexC ); iUPDf = FaceVaryingIndex( indexD ); // Copy any user specified primitive variables. std::vector::iterator iUP; std::vector::iterator end = Surface().aUserParams().end(); for ( iUP = Surface().aUserParams().begin(); iUP != end; iUP++ ) { CqParameter* pNewUP = ( *iUP ) ->CloneType( ( *iUP ) ->strName().c_str(), ( *iUP ) ->Count() ); if ( pNewUP->Class() == class_varying || pNewUP->Class() == class_vertex ) { pNewUP->SetSize( pNew->cVarying() ); pNewUP->SetValue( ( *iUP ), 0, iUPA ); pNewUP->SetValue( ( *iUP ), 1, iUPB ); pNewUP->SetValue( ( *iUP ), 2, iUPD ); pNewUP->SetValue( ( *iUP ), 3, iUPC ); if ( indexC == indexD ) CreatePhantomData( pNewUP ); } else if ( pNewUP->Class() == class_uniform ) { pNewUP->SetSize( pNew->cUniform() ); pNewUP->SetValue( ( *iUP ), 0, MeshIndex() ); } else if ( pNewUP->Class() == class_constant ) { pNewUP->SetSize( 1 ); pNewUP->SetValue( ( *iUP ), 0, 0 ); } else if ( pNewUP->Class() == class_facevarying || pNewUP->Class() == class_facevertex ) { pNewUP->SetSize( pNew->cVarying() ); pNewUP->SetValue( ( *iUP ), 0, iUPAf ); pNewUP->SetValue( ( *iUP ), 1, iUPBf ); pNewUP->SetValue( ( *iUP ), 2, iUPDf ); pNewUP->SetValue( ( *iUP ), 3, iUPCf ); if ( indexC == indexD ) CreatePhantomData( pNewUP ); } pNew->AddPrimitiveVariable( pNewUP ); } // If this is a triangle, then mark the patch as a special case. See function header comment for more details. if ( indexC == indexD ) pNew->SetfHasPhantomFourthVertex( TqTrue ); // If there are no smooth normals specified, then fill in the facet normal at each vertex. if ( !bHasVar(EnvVars_N) && USES( iUses, EnvVars_N ) ) { CqParameterTypedVarying* pNewUP = new CqParameterTypedVarying( "N", 1 ); pNewUP->SetSize( pNew->cVarying() ); pNewUP->pValue() [ 0 ] = vecN; pNewUP->pValue() [ 1 ] = vecN; pNewUP->pValue() [ 2 ] = vecN; pNewUP->pValue() [ 3 ] = vecN; pNew->AddPrimitiveVariable( pNewUP ); } // If the shader needs s/t or u/v, and s/t is not specified, then at this point store the object space x,y coordinates. if ( USES( iUses, EnvVars_s ) || USES( iUses, EnvVars_t ) || USES( iUses, EnvVars_u ) || USES( iUses, EnvVars_v ) ) { CqVector3D PA, PB, PC, PD; const CqMatrix& matCurrentToWorld = QGetRenderContext() ->matSpaceToSpace( "current", "object", NULL, Surface().pTransform().get(), Surface().pTransform() ->Time(0) ); PA = matCurrentToWorld * pNew->P() ->pValue() [ 0 ]; PB = matCurrentToWorld * pNew->P() ->pValue() [ 1 ]; PC = matCurrentToWorld * pNew->P() ->pValue() [ 3 ]; PD = matCurrentToWorld * pNew->P() ->pValue() [ 2 ]; if ( USES( iUses, EnvVars_s ) && !bHasVar(EnvVars_s) ) { CqParameterTypedVarying* pNewUP = new CqParameterTypedVarying( "s" ); pNewUP->SetSize( pNew->cVarying() ); pNewUP->pValue() [ 0 ] = PA.x(); pNewUP->pValue() [ 1 ] = PB.x(); pNewUP->pValue() [ 2 ] = PD.x(); pNewUP->pValue() [ 3 ] = PC.x(); pNew->AddPrimitiveVariable( pNewUP ); } if ( USES( iUses, EnvVars_t ) && !bHasVar(EnvVars_t) ) { CqParameterTypedVarying* pNewUP = new CqParameterTypedVarying( "t" ); pNewUP->SetSize( pNew->cVarying() ); pNewUP->pValue() [ 0 ] = PA.y(); pNewUP->pValue() [ 1 ] = PB.y(); pNewUP->pValue() [ 2 ] = PD.y(); pNewUP->pValue() [ 3 ] = PC.y(); pNew->AddPrimitiveVariable( pNewUP ); } if ( USES( iUses, EnvVars_u ) && !bHasVar(EnvVars_u) ) { CqParameterTypedVarying* pNewUP = new CqParameterTypedVarying( "u" ); pNewUP->SetSize( pNew->cVarying() ); pNewUP->pValue() [ 0 ] = PA.x(); pNewUP->pValue() [ 1 ] = PB.x(); pNewUP->pValue() [ 2 ] = PD.x(); pNewUP->pValue() [ 3 ] = PC.x(); pNew->AddPrimitiveVariable( pNewUP ); } if ( USES( iUses, EnvVars_v ) && !bHasVar(EnvVars_v) ) { CqParameterTypedVarying* pNewUP = new CqParameterTypedVarying( "v" ); pNewUP->SetSize( pNew->cVarying() ); pNewUP->pValue() [ 0 ] = PA.y(); pNewUP->pValue() [ 1 ] = PB.y(); pNewUP->pValue() [ 2 ] = PD.y(); pNewUP->pValue() [ 3 ] = PC.y(); pNew->AddPrimitiveVariable( pNewUP ); } } aSplits.push_back( pNew ); cNew++; // Move onto the next quad indexB = indexD; } return ( cNew ); } //--------------------------------------------------------------------- /** Generate phanton data to 'stretch' the triangle patch into a parallelogram. */ void CqPolygonBase::CreatePhantomData( CqParameter* pParam ) { assert( pParam->Class() == class_varying || pParam->Class() == class_vertex || pParam->Class() == class_facevarying || pParam->Class() == class_facevertex ); TqInt iArrayCount = 1; TqInt iArray; if( pParam->Count() > 0) iArrayCount = pParam->Count(); switch ( pParam->Type() ) { case type_point: case type_vector: case type_normal: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } case type_hpoint: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } case type_float: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } case type_integer: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } case type_color: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } case type_matrix: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } case type_string: { CqParameterTyped* pTParam = static_cast*>( pParam ); for( iArray = 0; iArray < iArrayCount; iArray++ ) pTParam->pValue( 3 ) [ iArray ] = ( pTParam->pValue( 1 ) [ iArray ] - pTParam->pValue( 0 ) [ iArray ] ) + pTParam->pValue( 2 ) [ iArray ]; break; } default: { // left blank to avoid compiler warnings about unhandled types break; } } } //--------------------------------------------------------------------- /** Default constructor. */ CqSurfacePolygon::CqSurfacePolygon( TqInt cVertices ) : CqSurface(), m_cVertices( cVertices ) {} //--------------------------------------------------------------------- /** Destructor. */ CqSurfacePolygon::~CqSurfacePolygon() {} //--------------------------------------------------------------------- /** Check if a polygon is degenerate, i.e. all points collapse to the same or almost the same place. */ TqBool CqSurfacePolygon::CheckDegenerate() const { // Check if all points are within a minute distance of each other. TqBool fDegen = TqTrue; TqInt i, n; n = NumVertices(); for ( i = 1; i < n; i++ ) { if ( ( PolyP( i ) - PolyP( i - 1 ) ).Magnitude() > FLT_EPSILON ) { fDegen = TqFalse; break; } } return ( fDegen ); } //--------------------------------------------------------------------- /** Create a copy of this polygon primitive. */ CqSurface* CqSurfacePolygon::Clone() const { CqSurfacePolygon* clone = new CqSurfacePolygon(); CqSurface::CloneData(clone); clone->m_cVertices = m_cVertices; return ( clone ); } //--------------------------------------------------------------------- /** Copy constructor. */ /* CqSurfacePointsPolygon::CqSurfacePointsPolygon( const CqSurfacePointsPolygon& From ) * { * *this = From; * } */ //--------------------------------------------------------------------- /** Assignment operator. */ /* CqSurfacePointsPolygon& CqSurfacePointsPolygon::operator=( const CqSurfacePointsPolygon& From ) * { * TqInt i; * m_aIndices.resize( From.m_aIndices.size() ); * for ( i = From.m_aIndices.size() - 1; i >= 0; i-- ) * m_aIndices[ i ] = From.m_aIndices[ i ]; * * // Store the old points array pointer, as we must reference first then * // unreference to avoid accidental deletion if they are the same and we are the * // last reference. * m_pPoints = From.m_pPoints; * m_Index = From.m_Index; * m_FaceVaryingIndex = From.m_FaceVaryingIndex; * * return ( *this ); * } */ //--------------------------------------------------------------------- /** Transform the points by the specified matrix. */ void CqPolygonPoints::Transform( const CqMatrix& matTx, const CqMatrix& matITTx, const CqMatrix& matRTx, TqInt iTime ) { // if ( m_Transformed ) // return ; CqSurface::Transform( matTx, matITTx, matRTx, iTime ); // m_Transformed = TqTrue; } CqSurface* CqPolygonPoints::Clone() const { CqPolygonPoints* clone = new CqPolygonPoints(); CqSurface::CloneData(clone); clone->m_cVertices = m_cVertices; clone->m_Transformed = m_Transformed; clone->m_cFaces = m_cFaces; clone->m_sumnVerts = m_sumnVerts; return(clone); } CqBound CqSurfacePointsPolygons::Bound() const { CqBound B; if( m_pPoints && m_pPoints->P() ) { TqInt PointIndex; for( PointIndex = m_pPoints->P()->Size()-1; PointIndex >= 0; PointIndex-- ) B.Encapsulate( (CqVector3D)m_pPoints->P()->pValue()[PointIndex] ); } return(AdjustBoundForTransformationMotion( B )); } TqInt CqSurfacePointsPolygons::Split( std::vector >& aSplits ) { TqInt CreatedPolys = 0; TqInt iP = 0, poly; for ( poly = 0; poly < m_NumPolys; poly++ ) { // Create a surface polygon boost::shared_ptr pSurface( new CqSurfacePointsPolygon( m_pPoints, poly, iP ) ); RtBoolean fValid = RI_TRUE; pSurface->aIndices().resize( m_PointCounts[ poly ] ); TqUint i; for ( i = 0; i < (TqUint)m_PointCounts[ poly ]; i++ ) // Fill in the points { if ( (TqUint) m_PointIndices[ iP ] >= m_pPoints->P()->Size() ) { fValid = RI_FALSE; //CqAttributeError( 1, Severity_Normal, "Invalid PointsPolygon index", pSurface->pAttributes() ); CqString objname( "unnamed" ); const CqString* pattrName = pSurface->pAttributes()->GetStringAttribute( "identifier", "name" ); if ( pattrName != 0 ) objname = pattrName[ 0 ]; Aqsis::log() << warning << "Invalid PointsPolygon index in object \"" << objname.c_str() << "\"" << std::endl; break; } pSurface->aIndices() [ i ] = m_PointIndices[ iP ]; iP++; } if ( fValid ) { aSplits.push_back( pSurface ); CreatedPolys++; } } return( CreatedPolys ); } CqSurface* CqSurfacePointsPolygons::Clone() const { // Make a 'complete' clone of this primitive, which means cloning the points too. CqPolygonPoints* clone_points = static_cast(m_pPoints->Clone()); CqSurfacePointsPolygons* clone = new CqSurfacePointsPolygons(); CqSurface::CloneData(clone); clone->m_NumPolys = m_NumPolys; clone->m_PointCounts = m_PointCounts; clone->m_PointIndices = m_PointIndices; clone->m_pPoints = boost::shared_ptr(clone_points); return(clone); } END_NAMESPACE( Aqsis ) //---------------------------------------------------------------------