|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "Portal_DynamicMeshRenderingUtils.h"
#include "iviewrender.h"
extern ConVar mat_wireframe;
int ClipPolyToPlane_LerpTexCoords( PortalMeshPoint_t *inVerts, int vertCount, PortalMeshPoint_t *outVerts, const Vector& normal, float dist, float fOnPlaneEpsilon ) { vec_t *dists = (vec_t *)stackalloc( sizeof(vec_t) * vertCount * 4 ); //4x vertcount should cover all cases
int *sides = (int *)stackalloc( sizeof(int) * vertCount * 4 ); int counts[3]; vec_t dot; int i, j; Vector mid = vec3_origin; int outCount;
counts[0] = counts[1] = counts[2] = 0;
// determine sides for each point
for ( i = 0; i < vertCount; i++ ) { dot = DotProduct( inVerts[i].vWorldSpacePosition, normal) - dist; dists[i] = dot; if ( dot > fOnPlaneEpsilon ) sides[i] = SIDE_FRONT; else if ( dot < -fOnPlaneEpsilon ) sides[i] = SIDE_BACK; else sides[i] = SIDE_ON; counts[sides[i]]++; } sides[i] = sides[0]; dists[i] = dists[0];
if (!counts[0]) return 0;
if (!counts[1]) { // Copy to output verts
//for ( i = 0; i < vertCount; i++ )
memcpy( outVerts, inVerts, sizeof( PortalMeshPoint_t ) * vertCount ); return vertCount; }
outCount = 0; for ( i = 0; i < vertCount; i++ ) { if (sides[i] == SIDE_ON) { memcpy( &outVerts[outCount], &inVerts[i], sizeof( PortalMeshPoint_t ) ); ++outCount; continue; } if (sides[i] == SIDE_FRONT) { memcpy( &outVerts[outCount], &inVerts[i], sizeof( PortalMeshPoint_t ) ); ++outCount; } if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i]) continue;
Vector& p1 = inVerts[i].vWorldSpacePosition;
// generate a split point
int i2 = (i+1)%vertCount; Vector& p2 = inVerts[i2].vWorldSpacePosition; dot = dists[i] / (dists[i]-dists[i+1]); for (j=0 ; j<3 ; j++) { mid[j] = p1[j] + dot*(p2[j]-p1[j]); }
VectorCopy (mid, outVerts[outCount].vWorldSpacePosition); outVerts[outCount].texCoord.x = inVerts[i].texCoord.x + dot*(inVerts[i2].texCoord.x - inVerts[i].texCoord.x); outVerts[outCount].texCoord.y = inVerts[i].texCoord.y + dot*(inVerts[i2].texCoord.y - inVerts[i].texCoord.y); ++outCount; }
return outCount; }
// Returns true if clipping took place.
// Outputs two polygons: One for each side of the plane
void ProjectPortalPolyToPlane( PortalMeshPoint_t *pInVerts, int nVertCount, const Vector& normal, float flDist, const Vector &vCameraPos ) { for ( int i = 0; i < nVertCount; i++ ) { // project point onto plane
Vector vDir( pInVerts[i].vWorldSpacePosition - vCameraPos ); float flT = ( flDist - DotProduct( vCameraPos, normal ) ) / DotProduct( vDir, normal ); pInVerts[i].vWorldSpacePosition = vCameraPos + flT * vDir;
/*
double dirx, diry, dirz; dirx = pInVerts[i].vWorldSpacePosition.x - vCameraPos.x; diry = pInVerts[i].vWorldSpacePosition.y - vCameraPos.y; dirz = pInVerts[i].vWorldSpacePosition.z - vCameraPos.z; double dot1 = vCameraPos.x * normal.x + vCameraPos.y * normal.y + vCameraPos.z * normal.z; double dot2 = dirx * normal.x + diry * normal.y + dirz * normal.z; double flT2 = ( double( flDist ) - dot1 ) / dot2; pInVerts[i].vWorldSpacePosition.x = double( vCameraPos.x ) + flT2 * dirx; pInVerts[i].vWorldSpacePosition.y = double( vCameraPos.y ) + flT2 * diry; pInVerts[i].vWorldSpacePosition.z = double( vCameraPos.z ) + flT2 * dirz; */ } }
// Clips a convex poly to a single plane
bool ClipPortalPolyToPlane( PortalMeshPoint_t *pInVerts, int nVertCount, PortalMeshPoint_t *pFrontVerts, int *pFrontVertCount, const Vector& normal, float flDist ) { if ( nVertCount < 3 ) { *pFrontVertCount = 0; return false; } float *dists = (float *)stackalloc( sizeof(float) * nVertCount ); int *sides = (int *)stackalloc( sizeof(int) * nVertCount ); int count[2] = { 0, 0 };
// determine sides for each point
for ( int i = 0; i < nVertCount; i++ ) { float dot = DotProduct( pInVerts[i].vWorldSpacePosition, normal) - flDist; dists[i] = dot; sides[i] = ( dot >= 0.0f ) ? SIDE_FRONT : SIDE_BACK; count[ sides[i] ]++; }
// no need to clip anything
if ( count[0] == 0 ) { *pFrontVertCount = 0; return false; }
int outCount1 = 0; for ( int i = 0; i < nVertCount; i++ ) { int i2 = ( i + 1 ) % nVertCount;
if ( sides[i] == SIDE_FRONT ) { memcpy( &pFrontVerts[outCount1], &pInVerts[i], sizeof( PortalMeshPoint_t ) ); ++outCount1;
} if ( sides[i2] == sides[i] ) { continue; }
// generate a split point
float dot = dists[i] / ( dists[i] - dists[i2] );
Vector& p1 = pInVerts[i].vWorldSpacePosition; Vector& p2 = pInVerts[i2].vWorldSpacePosition; Vector midPt = p1 + dot * ( p2 - p1 );
Vector2D& uv1 = pInVerts[i].texCoord; Vector2D& uv2 = pInVerts[i2].texCoord; Vector2D midUv = uv1 + dot * ( uv2 - uv1 );
VectorCopy( midPt, pFrontVerts[outCount1].vWorldSpacePosition ); pFrontVerts[outCount1].texCoord = midUv;
++outCount1; }
*pFrontVertCount = outCount1; return true; }
void RenderPortalMeshConvexPolygon( PortalMeshPoint_t *pVerts, int iVertCount, const IMaterial *pMaterial, void *pBind ) { CMatRenderContextPtr pRenderContext( materials ); pRenderContext->Bind( (IMaterial *)pMaterial, pBind );
//PortalMeshPoint_t *pMidVerts = (PortalMeshPoint_t *)stackalloc( sizeof( PortalMeshPoint_t ) * iVertCount );
CMeshBuilder meshBuilder; IMesh* pMesh = pRenderContext->GetDynamicMesh( true ); meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, iVertCount - 2 );
//any convex polygon can be rendered with a triangle strip by starting at a vertex and alternating vertices from each side
int iForwardCounter = 0; int iReverseCounter = iVertCount - 1; //guaranteed to be >= 2 to start
do { PortalMeshPoint_t *pVertex = &pVerts[iForwardCounter]; meshBuilder.Position3fv( &pVertex->vWorldSpacePosition.x ); meshBuilder.TexCoord2fv( 0, &pVertex->texCoord.x ); meshBuilder.AdvanceVertex(); ++iForwardCounter;
if( iForwardCounter > iReverseCounter ) break;
pVertex = &pVerts[iReverseCounter]; meshBuilder.Position3fv( &pVertex->vWorldSpacePosition.x ); meshBuilder.TexCoord2fv( 0, &pVertex->texCoord.x ); meshBuilder.AdvanceVertex(); --iReverseCounter; } while( iForwardCounter <= iReverseCounter );
meshBuilder.End(); pMesh->Draw(); }
void Clip_And_Render_Convex_Polygon( PortalMeshPoint_t *pVerts, int iVertCount, const IMaterial *pMaterial, void *pBind ) { PortalMeshPoint_t *pInVerts = (PortalMeshPoint_t *)stackalloc( iVertCount * 4 * sizeof( PortalMeshPoint_t ) ); //really only should need 2x points, but I'm paranoid
PortalMeshPoint_t *pOutVerts = (PortalMeshPoint_t *)stackalloc( iVertCount * 4 * sizeof( PortalMeshPoint_t ) ); PortalMeshPoint_t *pTempVerts;
//clip by the viewing frustum
{ VPlane *pFrustum = view->GetFrustum(); //clip by first plane and put output into pInVerts
iVertCount = ClipPolyToPlane_LerpTexCoords( pVerts, iVertCount, pInVerts, pFrustum[0].m_Normal, pFrustum[0].m_Dist, 0.01f );
//clip by other planes and flipflop in and out pointers
for( int i = 1; i != FRUSTUM_NUMPLANES; ++i ) { if( iVertCount < 3 ) return; //nothing to draw
iVertCount = ClipPolyToPlane_LerpTexCoords( pInVerts, iVertCount, pOutVerts, pFrustum[i].m_Normal, pFrustum[i].m_Dist, 0.01f ); pTempVerts = pInVerts; pInVerts = pOutVerts; pOutVerts = pTempVerts; //swap vertex pointers
}
if( iVertCount < 3 ) return; //nothing to draw
}
CMatRenderContextPtr pRenderContext( materials ); RenderPortalMeshConvexPolygon( pOutVerts, iVertCount, pMaterial, pBind ); if( mat_wireframe.GetBool() ) RenderPortalMeshConvexPolygon( pOutVerts, iVertCount, materials->FindMaterial( "shadertest/wireframe", TEXTURE_GROUP_CLIENT_EFFECTS, false ), pBind );
stackfree( pOutVerts ); stackfree( pInVerts ); }
|