📄 cpolygonobject.cpp
字号:
/*
*
============================================================================
* Name : CPolygonObject.cpp
* Part of : Example3D
* Created : 12/14/2003 by Forum Nokia
* Description:
* This is the project specification file for Example3D.
* Initial content was generated by Series 60 AppWizard.
*
* Version : 1.0.0
* Copyright: Forum Nokia
*
============================================================================
*/
// INCLUDES
#include "CPolygonObject.h"
#include <e32math.h>
#include <e32svr.h> // for debug prints
// CONSTANTS
const TInt KZShift = 23;
const TInt KZMul = ( 1 << KZShift );
// MEMBER FUNCTIONS
CPolygonObject* CPolygonObject::NewL( C3DBase* a3DBase, TInt aNumVertices, TInt aNumFaces )
{
CPolygonObject* self = new( ELeave )CPolygonObject( a3DBase, aNumVertices, aNumFaces );
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop( self );
return self;
}
CPolygonObject::~CPolygonObject()
{
delete iFace;
delete iVertex;
delete iRVertex;
delete iDrawList;
delete iFaceZ;
delete iFaceN;
}
CPolygonObject::CPolygonObject( C3DBase* a3DBase, TInt aNumVertices, TInt aNumFaces )
: i3DBase( a3DBase )
, iMaxNumVertices( aNumVertices )
, iMaxNumFaces( aNumFaces )
{
}
void CPolygonObject::ConstructL()
{
iFace = new( ELeave )TFace[ iMaxNumFaces ];
iVertex = new( ELeave )TVertex[ iMaxNumVertices ];
iRVertex = new( ELeave )TVertex[ iMaxNumVertices ];
iCos = i3DBase->CosTable();
iSin = i3DBase->SinTable();
iFaceN = new( ELeave )TInt[ iMaxNumFaces ];
iFaceZ = new( ELeave )TInt[ iMaxNumFaces ];
iDrawList = new( ELeave )TDrawFace[ iMaxNumFaces ];
}
void CPolygonObject::Draw( const TBitmap& aScreen, TMatrix* aRotateMatrix )
{
// rotate matrix from C3DRenderer
iRotateMatrix = aRotateMatrix;
iScreen = aScreen;
TFrustum& frustum = i3DBase->ViewFrustum();
// rotate vertices to iRVertex list
Rotate();
// clipped triangle has more than 3 vertices:
TDrawVertex polygon1[ 10 ];
TDrawVertex polygon2[ 10 ];
TBool clipped;
TInt inCount = 0;
// polygon's vertex distance from clipping plane
TInt d[ 10 ];
TInt numDrawn = 0;
TInt i;
for( i=0; i<iNumFaces; i++ )
{
TBool visible = ETrue;
clipped = EFalse;
TFace& f = iFace[ i ];
TVertex& v1 = iRVertex[ f.iV1 ];
TVertex& v2 = iRVertex[ f.iV2 ];
TVertex& v3 = iRVertex[ f.iV3 ];
polygon1[ 0 ] = TDrawVertex( v1, f.iTx1, f.iTy1 );
polygon1[ 1 ] = TDrawVertex( v2, f.iTx2, f.iTy2 );
polygon1[ 2 ] = TDrawVertex( v3, f.iTx3, f.iTy3 );
TDrawVertex* poly1 = polygon1;
TDrawVertex* poly2 = polygon2;
TInt numPoints = 3;
for( TInt n=0; n<frustum.iNumPlanes; n++ )
{
TInt vn = numPoints;
TInt p;
for( p=0; p<numPoints; p++ )
{
TVertex& mv1 = frustum.iPlane[ n ].iNormal;
TDrawVertex& mv2 = poly1[ p ];
// calculate dot product to get point distance to plane:
TInt l = mv1.iX * mv2.iX + mv1.iY * mv2.iY + mv1.iZ * mv2.iZ;
l += frustum.iPlane[ n ].iDistance;
if( l <= 0 ) vn--;
d[ p ] = l;
inCount++;
}
if( vn == numPoints )
{
// all visible, do not clip
continue;
}
if( vn == 0 )
{
// none visible, do not draw, do not continue clipping
visible = EFalse;
break;
}
clipped = ETrue;
TInt np = 0;
for( TInt p1=0; p1<numPoints; p1++ )
{
TInt p0 = p1-1;
TInt p2 = p1+1;
if( p0<0 ) p0 += numPoints;
if( p2>=numPoints ) p2 -= numPoints;
TDrawVertex& dv0 = poly1[ p0 ];
TDrawVertex& dv1 = poly1[ p1 ];
TDrawVertex& dv2 = poly1[ p2 ];
// if point is out of plane, check if one or more points can be
// added to drawarea's edge in straight line to other points
// ( actual clipping )
if( d[ p1 ] < 0 )
{
if( d[ p0 ] > 0 )
{
TInt s = ( d[ p0 ] << KShift ) / ( d[ p0 ]-d[ p1 ] );
poly2[ np ].iX = dv0.iX + ( ( s*( dv1.iX - dv0.iX ) ) >> KShift );
poly2[ np ].iY = dv0.iY + ( ( s*( dv1.iY - dv0.iY ) ) >> KShift );
poly2[ np ].iZ = dv0.iZ + ( ( s*( dv1.iZ - dv0.iZ ) ) >> KShift );
poly2[ np ].iTx = dv0.iTx + ( ( s*( dv1.iTx - dv0.iTx ) ) >> KShift );
poly2[ np ].iTy = dv0.iTy + ( ( s*( dv1.iTy - dv0.iTy ) ) >> KShift );
np++;
}
if( d[ p2 ] > 0 )
{
TInt s = ( d[ p2 ] << KShift ) / ( d[ p2 ]-d[ p1 ] );
poly2[ np ].iX = dv2.iX + ( ( s*( dv1.iX - dv2.iX ) ) >> KShift );
poly2[ np ].iY = dv2.iY + ( ( s*( dv1.iY - dv2.iY ) ) >> KShift );
poly2[ np ].iZ = dv2.iZ + ( ( s*( dv1.iZ - dv2.iZ ) ) >> KShift );
poly2[ np ].iTx = dv2.iTx + ( ( s*( dv1.iTx - dv2.iTx ) ) >> KShift );
poly2[ np ].iTy = dv2.iTy + ( ( s*( dv1.iTy - dv2.iTy ) ) >> KShift );
np++;
}
}
else
{
poly2[ np++ ] = poly1[ p1 ];
}
}
numPoints = np;
TDrawVertex* temp = poly1;
poly1 = poly2;
poly2 = temp;
}
TInt t;
if( visible )
{
if( numDrawn < iMaxNumFaces )
{
// split polygon to triangles:
// ( polygon draw other but triangles are not supported )
for( t=0; t<numPoints-2; t++ )
{
iDrawList[ numDrawn++ ] = TDrawFace( poly1[ 0 ], poly1[ 1 + t ], poly1[ 2 + t ] );
}
}
}
}
// draw if there's something to draw
if( numDrawn > 0 )
{
// project vertices in iRVertex list to screen coordinates
Project( iDrawList, numDrawn );
// sort faces by their Z-values
for( i=0; i<numDrawn; i++ )
{
TDrawFace& f = iDrawList[ i ];
iFaceZ[ i ] = f.iV1.iZ + f.iV2.iZ + f.iV3.iZ;
iFaceN[ i ] = i;
}
i3DBase->QSort( iFaceZ, iFaceN, 0, numDrawn-1 );
// and draw faces from back to front
for( i=numDrawn-1; i>=0; i-- )
{
TDrawFace& f = iDrawList[ iFaceN[ i ] ];
// textured
DrawTexTri( &f.iV1, &f.iV2, &f.iV3 );
// textured, perspective correct
//DrawTexTriZ( &f.iV1, &f.iV2, &f.iV3 );
}
}
}
void CPolygonObject::SetTexture( TUint16* aTexture )
{
iTexture = aTexture;
}
TInt CPolygonObject::AddVertex( const TVertex& aVertex )
{
__ASSERT_DEBUG( iNumVertices < iMaxNumVertices, User::Panic( _L("Too many vertices"), iNumVertices ) );
iVertex[ iNumVertices++ ] = aVertex;
return iNumVertices-1;
}
void CPolygonObject::SetVertex( TInt aIndex, const TVertex& aVertex )
{
__ASSERT_DEBUG( aIndex < iNumVertices, User::Panic( _L("Illegal vertex index"), aIndex ) );
iVertex[ aIndex ] = aVertex;
}
TInt CPolygonObject::AddFace( const TFace& aFace )
{
__ASSERT_DEBUG( iNumFaces < iMaxNumFaces, User::Panic( _L("Too many faces"), iNumFaces ) );
iFace[ iNumFaces++ ] = aFace;
return iNumFaces;
}
void CPolygonObject::Init()
{
//
// Calculate bounding radius for object clipping
//
TInt r = 0;
TInt i;
for( i=0; i<iNumVertices; i++ )
{
TVertex& v = iVertex[ i ];
TInt r2 = v.iX * v.iX + v.iY * v.iY + v.iZ * v.iZ;
if( r2 > r ) r = r2;
}
TReal ra;
Math::Sqrt( ra, r );
iBoundingRadius = ( TInt )ra;
}
void CPolygonObject::Rotate()
{
// multiply all vertices by rotate matrix
// rotate matrix is calculated by C3DRenderer
for( TInt i=0; i<iNumVertices; i++ )
{
iRVertex[ i ] = iVertex[ i ];
iRVertex[ i ].MulMatrix( iRotateMatrix );
}
}
void CPolygonObject::Project( TDrawFace* aFaceList, TInt aNumFaces )
{
TInt i;
TInt mx = iScreen.iSize.iWidth / 2;
TInt my = iScreen.iSize.iHeight / 2;
for( i=0; i<aNumFaces; i++ )
{
TDrawFace& f = aFaceList[ i ];
f.iV1.iX <<= 9;
f.iV1.iX /= f.iV1.iZ ;
f.iV1.iX += mx;
f.iV1.iY <<= 9;
f.iV1.iY /= f.iV1.iZ ;
f.iV1.iY += my;
f.iV2.iX <<= 9;
f.iV2.iX /= f.iV2.iZ ;
f.iV2.iX += mx;
f.iV2.iY <<= 9;
f.iV2.iY /= f.iV2.iZ ;
f.iV2.iY += my;
f.iV3.iX <<= 9;
f.iV3.iX /= f.iV3.iZ ;
f.iV3.iX += mx;
f.iV3.iY <<= 9;
f.iV3.iY /= f.iV3.iZ ;
f.iV3.iY += my;
}
}
// disable warning of local variable may be used without initialized
// these variables are always initialized but compiler doesn't know it.
#pragma warning( disable : 4701 )
void CPolygonObject::DrawTexTri( TDrawVertex* aV1, TDrawVertex* aV2, TDrawVertex* aV3 )
{
// backface culling ( only one side of triangle is visible )
TInt z = ( aV3->iX - aV1->iX ) * ( aV3->iY - aV2->iY ) - ( aV3->iY - aV1->iY ) * ( aV3->iX - aV2->iX );
if( z <= 0 )
{
return;
}
// calculate starting points and adders for triangle sides
// and texture coordinates
if( aV1->iY > aV2->iY )
{
TDrawVertex* temp = aV1; aV1 = aV2; aV2 = temp;
}
if( aV2->iY > aV3->iY )
{
TDrawVertex* temp = aV2; aV2 = aV3; aV3 = temp;
}
if( aV1->iY > aV2->iY )
{
TDrawVertex* temp = aV1; aV1 = aV2; aV2 = temp;
}
TInt h1 = ( aV2->iY - aV1->iY );
TInt h2 = ( aV3->iY - aV2->iY );
TInt h3 = ( aV3->iY - aV1->iY );
if( h3 == 0 ) return;
TInt xAdd1;
TInt xAdd2;
TInt xAdd3;
TInt txAdd1;
TInt txAdd2;
TInt txAdd3;
TInt tyAdd1;
TInt tyAdd2;
TInt tyAdd3;
if( h1 )
{
xAdd1 = ( ( aV2->iX - aV1->iX ) << KShift ) / h1;
txAdd1 = ( ( aV2->iTx - aV1->iTx ) << 8 ) / h1;
tyAdd1 = ( ( aV2->iTy - aV1->iTy ) << 8 ) / h1;
}
if( h2 )
{
xAdd2 = ( ( aV3->iX - aV2->iX ) << KShift ) / h2;
txAdd2 = ( ( aV3->iTx - aV2->iTx ) << 8 ) / h2;
tyAdd2 = ( ( aV3->iTy - aV2->iTy ) << 8 ) / h2;
}
if( h3 )
{
xAdd3 = ( ( aV3->iX - aV1->iX ) << KShift ) / h3;
txAdd3 = ( ( aV3->iTx - aV1->iTx ) << 8 ) / h3;
tyAdd3 = ( ( aV3->iTy - aV1->iTy ) << 8 ) / h3;
}
TInt mul = ( aV2->iY - aV1->iY );
TInt midX = ( aV1->iX << 0 ) + ( ( xAdd3 * mul ) >> KShift );
TInt midTx = ( aV1->iTx << 8 ) + ( ( txAdd3 * mul ) >> 0 );
TInt midTy = ( aV1->iTy << 8 ) + ( ( tyAdd3 * mul ) >> 0 );
TInt w = ( aV2->iX << 0 ) - midX;
TInt txa = ( aV2->iTx << 8 ) - midTx;
TInt tya = ( aV2->iTy << 8 ) - midTy;
if( w )
{
txa /= w;
tya /= w;
}
// Draw triangle in two parts
// first upper half and then lower
// split by mid point
// Also choose parameters depending which side of triangle
// has the midpoint
if( midX < aV2->iX ) // if long side left
{
if( h1 ) DrawTexSlice( aV1->iX, aV1->iX,
xAdd3, xAdd1,
aV1->iY, aV2->iY,
aV1->iTx << 8, aV1->iTy << 8,
txa, tya,
txAdd3, tyAdd3 );
if( h2 ) DrawTexSlice( midX, aV2->iX,
xAdd3, xAdd2,
aV2->iY, aV3->iY,
midTx, midTy,
txa, tya,
txAdd3, tyAdd3 );
}
else // if long side right
{
if( h1 ) DrawTexSlice( aV1->iX, aV1->iX,
xAdd1, xAdd3,
aV1->iY, aV2->iY,
aV1->iTx << 8, aV1->iTy << 8,
txa, tya,
txAdd1, tyAdd1 );
if( h2 ) DrawTexSlice( aV2->iX, midX,
xAdd2, xAdd3,
aV2->iY, aV3->iY,
aV2->iTx << 8, aV2->iTy << 8,
txa, tya,
txAdd2, tyAdd2 );
}
}
// enable warning of local variable may be used without initialized
#pragma warning( default : 4701 )
inline void CPolygonObject::DrawTexSlice( TInt aX1, TInt aX2, TInt aX1Add, TInt aX2Add,
TInt aY1, TInt aY2, TInt aTx1, TInt aTy1,
TInt aTxAddX, TInt aTyAddX, TInt aTxAddY, TInt aTyAddY )
{
// This function is a bit optimized so the reading might be hard
TInt tp = aY1 * iScreen.iSize.iWidth;
tp <<= 1;
tp += (TInt)iScreen.iData; // calculate screen address by starting
// y-position
TInt h = aY2 - aY1;
TInt x1 = aX1 << KShift; // calculate starting x-positions
TInt x2 = aX2 << KShift;
// here is combined adders for texture x and y coordinates
// to only one integer, high half has the X
TInt otxa = ( ( aTxAddX & 0xffff ) << 16 ) + ( aTyAddX & 0xffff );
// scan all lines from up to down
// and fill area from x1 to x2 with texture
// updates x1 and x2 positions after every line
for( TInt y=h; y>0; y-- )
{
#ifdef __WINS__
TUint32 txa = otxa;
TUint32 tx = ( aTx1 << 16 ) + ( aTy1 & 0xffff );
TInt w = ( x2-x1 + ( 1 << ( KShift-1 ) ) ) >> KShift;
TInt sp = (TInt)iTexture;
TUint16* p = (TUint16*)( tp + ( ( x1 >> KShift ) << 1 ) );
TInt x;
#else
// compiler has difficulties to optimize right the rendering loop
// these are helpers to keep all parameters in registers
// which speeds up the drawing a lot
// next phase would be writing straight assembler
// but you can check the code this generates with "ABLD LISTING"
// -command in "\group" -folder
register TUint32 txa asm("r0");
register TInt w asm("r1");
register TUint32 tx asm("r2");
register TInt sp asm("r3");
register TInt x asm("r4");
txa = otxa;
w = ( x2-x1 ) >> KShift;
tx = ( aTx1 << 16 ) + ( aTy1 & 0xffff );
sp = (TInt)iTexture ;
TUint16* p = (TUint16*)( tp + ( ( x1 >> KShift ) << 1 ) );
#endif
for( x=w; x>=0; x-- )
{
TInt cp = ( tx >> 24 ) + ( tx & 0xff00 );
*p++ = ((TUint16*)sp)[cp];
tx += txa;
}
x1 += aX1Add;
x2 += aX2Add;
aTx1 += aTxAddY;
aTy1 += aTyAddY;
tp += iScreen.iSize.iWidth*2;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -