1322 lines
35 KiB
C
1322 lines
35 KiB
C
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <commdlg.h>
|
|
#include <ptypes32.h>
|
|
#include <pwin32.h>
|
|
#include <math.h>
|
|
|
|
#include <GL\gl.h>
|
|
#include <GL\glu.h>
|
|
|
|
#include <imports.h>
|
|
#include <types.h>
|
|
|
|
#include "fontoutl.h"
|
|
|
|
// Extrusion types
|
|
#define EXTR_LINES 0
|
|
#define EXTR_POLYGONS 1
|
|
|
|
// Prim to prim transitions
|
|
#define EXTR_LINE_LINE 0
|
|
#define EXTR_LINE_CURVE 1
|
|
#define EXTR_CURVE_LINE 2
|
|
#define EXTR_CURVE_CURVE 3
|
|
|
|
static const double CurveCurveCutoffAngle = PI/2.0;
|
|
static const double LineCurveCutoffAngle = PI/4.0;
|
|
|
|
static BOOL InitFaceBuf( EXTRContext *ec );
|
|
|
|
#ifndef VARRAY
|
|
static void DrawFacePolygons( EXTRContext *ec,
|
|
FLOAT z );
|
|
#endif
|
|
|
|
static BOOL DrawSidePolygons( EXTRContext *ec,
|
|
LOOP_LIST *pLoopList );
|
|
|
|
static void DrawPrims( EXTRContext *ec,
|
|
LOOP *pLoop );
|
|
|
|
static void DrawQuads( PRIM *pPrim,
|
|
FLOAT zExtrusion );
|
|
|
|
static void DrawQuadStrip( EXTRContext *ec,
|
|
PRIM *pPrim );
|
|
|
|
static BOOL AppendToFaceBuf( EXTRContext *ec,
|
|
FLOAT value );
|
|
|
|
static BOOL ReallocFaceBuf( EXTRContext *ec );
|
|
|
|
|
|
static BOOL CalculateFaceNormals( LOOP *pLoop,
|
|
GLenum orientation );
|
|
|
|
static BOOL CalculateVertexNormals( LOOP *pLoop );
|
|
|
|
static void ConsolidatePrims( LOOP *pLoop );
|
|
|
|
static double PrimNormAngle( PRIM *pPrimA,
|
|
PRIM *pPrimB );
|
|
|
|
static int PrimTransition( PRIM *pPrevPrim,
|
|
PRIM *pPrim );
|
|
|
|
static GLenum LoopOrientation( LOOP_LIST *pLoopList );
|
|
|
|
static LOOP* GetMaxExtentLoop( LOOP_LIST *pLoopList );
|
|
|
|
double CalcAngle( POINT2D *v1,
|
|
POINT2D *v2 );
|
|
|
|
static void CalcNormal2d( POINT2D *p,
|
|
POINT2D *n,
|
|
GLenum orientation );
|
|
|
|
static void Normalize2d( POINT2D *n );
|
|
|
|
static void AddVectors3d( POINT3D *v1,
|
|
POINT3D *v2,
|
|
POINT3D *n );
|
|
|
|
static void FreeLoopMem( LOOP *pLoop );
|
|
|
|
|
|
#ifdef VARRAY
|
|
|
|
static PFNGLVERTEXPOINTEREXTPROC glWFOVertexPointerEXT ;
|
|
static PFNGLNORMALPOINTEREXTPROC glWFONormalPointerEXT ;
|
|
static PFNGLDRAWARRAYSEXTPROC glWFODrawArraysEXT ;
|
|
|
|
static BOOL InitVArray( EXTRContext *ec );
|
|
static BOOL VArrayBufSize( EXTRContext *ec, DWORD size );
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* exported functions
|
|
*****************************************************************************/
|
|
|
|
/*****************************************************************************
|
|
* extr_Init
|
|
*
|
|
* Initialises extrusion for a wglUseFontOutline call
|
|
|
|
*****************************************************************************/
|
|
|
|
EXTRContext *
|
|
extr_Init( FLOAT extrusion, INT format )
|
|
{
|
|
EXTRContext *ec;
|
|
|
|
ec = (EXTRContext *) ALLOCZ(sizeof(EXTRContext) );
|
|
|
|
if( !ec )
|
|
return NULL;
|
|
|
|
ec->zExtrusion = -extrusion;
|
|
|
|
switch( format ) {
|
|
case WGL_FONT_LINES :
|
|
ec->extrType = EXTR_LINES;
|
|
#ifdef FONT_DEBUG
|
|
ec->bSidePolys = FALSE;
|
|
ec->bFacePolys = FALSE;
|
|
#endif
|
|
break;
|
|
case WGL_FONT_POLYGONS :
|
|
ec->extrType = EXTR_POLYGONS;
|
|
#ifdef FONT_DEBUG
|
|
ec->bSidePolys = TRUE;
|
|
ec->bFacePolys = TRUE;
|
|
#endif
|
|
#ifdef VARRAY
|
|
if( ! InitVArray( ec ) ) {
|
|
FREE( ec );
|
|
return NULL;
|
|
}
|
|
#endif
|
|
break;
|
|
default:
|
|
ASSERTOPENGL( FALSE, "extr_Init(): invalid format\n" );
|
|
}
|
|
return ec;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_Finish
|
|
*
|
|
* Finishes extrusion for a wglUseFontOutline call
|
|
|
|
*****************************************************************************/
|
|
|
|
void
|
|
extr_Finish( EXTRContext *ec )
|
|
{
|
|
#ifdef VARRAY
|
|
if( ec->extrType == EXTR_POLYGONS )
|
|
FREE( ec->vaBuf );
|
|
#endif
|
|
FREE( ec );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_PolyInit
|
|
*
|
|
* Initializes the extrusion of a single glyph.
|
|
* If the extrusion is polygonal, it sets up FaceBuf, which holds a buffer
|
|
* of primitives for drawing the faces of the extruded glyphs.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL extr_PolyInit( EXTRContext *ec )
|
|
{
|
|
if( ec->extrType == EXTR_LINES )
|
|
return WFO_SUCCESS;
|
|
|
|
ec->FaceBuf = (FLOAT *) NULL;
|
|
if( !InitFaceBuf( ec ) ||
|
|
!AppendToFaceBuf( ec, 0.0f) ) // primitive count at FaceBuf[0]
|
|
return WFO_FAILURE;
|
|
|
|
// initialize error flag
|
|
ec->TessErrorOccurred = 0;
|
|
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_PolyFinish
|
|
*
|
|
* Cleans up stuff from processing a single glyph
|
|
|
|
*****************************************************************************/
|
|
|
|
void extr_PolyFinish( EXTRContext *ec )
|
|
{
|
|
if( ec->extrType == EXTR_LINES )
|
|
return;
|
|
|
|
if( ec->FaceBuf ) {
|
|
FREE( ec->FaceBuf );
|
|
ec->FaceBuf = (FLOAT *) NULL;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_DrawLines
|
|
*
|
|
* Draws the lines in a glyph loop for Line extrusion
|
|
|
|
*****************************************************************************/
|
|
|
|
void extr_DrawLines( EXTRContext *ec, LOOP_LIST *pLoopList )
|
|
{
|
|
DWORD nLoops, nVerts;
|
|
POINT2D *p;
|
|
LOOP *pLoop;
|
|
|
|
nLoops = pLoopList->nLoops;
|
|
pLoop = pLoopList->LoopBuf;
|
|
for( ; nLoops; nLoops--, pLoop++ ) {
|
|
|
|
// Draw the back face loop
|
|
|
|
#ifdef FONT_DEBUG
|
|
DrawColorCodedLineLoop( pLoop, ec->zExtrusion );
|
|
#else
|
|
glBegin(GL_LINE_LOOP);
|
|
|
|
nVerts = pLoop->nVerts - 1; // skip last point
|
|
p = pLoop->VertBuf;
|
|
for ( ; nVerts; nVerts--, p++ ) {
|
|
glVertex3f( p->x, p->y, ec->zExtrusion );
|
|
}
|
|
|
|
glEnd();
|
|
#endif
|
|
|
|
// Draw the lines along the sides
|
|
|
|
#ifdef FONT_DEBUG
|
|
glColor3d( 0.0, 0.0, 1.0 );
|
|
#endif
|
|
|
|
glBegin(GL_LINES);
|
|
|
|
nVerts = pLoop->nVerts - 1; // skip last point
|
|
p = pLoop->VertBuf;
|
|
for( ; nVerts; nVerts--, p++ ) {
|
|
glVertex2fv( (GLfloat *) p);
|
|
glVertex3f( p->x, p->y, ec->zExtrusion );
|
|
}
|
|
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_glBegin
|
|
*
|
|
* Tesselation callback for glBegin.
|
|
* Buffers data into FaceBuf
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void CALLBACK
|
|
extr_glBegin( GLenum primType, void *data )
|
|
{
|
|
EXTRContext *ec = ((OFContext *)data)->ec;
|
|
|
|
// buffer face data
|
|
ec->FaceBuf[0] += 1.0f; // increment prim counter
|
|
ec->FaceVertexCountIndex = ec->FaceBufIndex+1; // mark vertex count index
|
|
|
|
if( !AppendToFaceBuf( ec, (FLOAT) primType ) || // enter prim type
|
|
!AppendToFaceBuf( ec, 0.0f ) ) // vertex count
|
|
ec->TessErrorOccurred = GLU_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_glEnd
|
|
*
|
|
* Tesselation callback for glEnd.
|
|
* Noop, since we are just tracking the tesselation at this point.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void CALLBACK
|
|
extr_glEnd( void )
|
|
{
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* extr_glVertex
|
|
*
|
|
* Tesselation callback for glVertex.
|
|
* Buffers data into FaceBuf
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void CALLBACK
|
|
extr_glVertex( GLfloat *v, void *data )
|
|
{
|
|
EXTRContext *ec = ((OFContext *)data)->ec;
|
|
|
|
// put vertex in face buffer
|
|
if( !AppendToFaceBuf( ec, v[0]) || !AppendToFaceBuf( ec, v[1]) )
|
|
ec->TessErrorOccurred = GLU_OUT_OF_MEMORY;
|
|
|
|
// increment vertex counter
|
|
ec->FaceBuf[ec->FaceVertexCountIndex] += 1.0f;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* extr_DrawPolygons
|
|
*
|
|
* Draws the side and face polygons of a glyph for polygonal extrusion
|
|
* Gets polygon information from LineBuf, which was created during
|
|
* MakeLinesFromGlyph().
|
|
|
|
*****************************************************************************/
|
|
|
|
BOOL
|
|
extr_DrawPolygons( EXTRContext *ec, LOOP_LIST *pLoopList )
|
|
{
|
|
#ifdef FONT_DEBUG
|
|
if( ec->bSidePolys )
|
|
if( !DrawSidePolygons( ec, pLoopList ) ) {
|
|
return WFO_FAILURE;
|
|
}
|
|
|
|
if( ec->bFacePolys ) {
|
|
DrawFacePolygons( ec, 0.0f ); // front face
|
|
DrawFacePolygons( ec, ec->zExtrusion ); // back face
|
|
}
|
|
#else
|
|
if( !DrawSidePolygons( ec, pLoopList ) )
|
|
return WFO_FAILURE;
|
|
|
|
DrawFacePolygons( ec, 0.0f ); // front face
|
|
DrawFacePolygons( ec, ec->zExtrusion ); // back face
|
|
#endif
|
|
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* internal functions
|
|
*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************
|
|
* DrawSidePolygons
|
|
*
|
|
* Draw the side prims, using several passes on each prim loop:
|
|
* 1) Calculate face normals for all the prims
|
|
* 2) Consolidate prims if possible
|
|
* 3) Calculate vertex normals for curve prims
|
|
* 4) Draw the prims
|
|
* Side effects: sets glFrontFace
|
|
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
DrawSidePolygons( EXTRContext *ec,
|
|
LOOP_LIST *pLoopList )
|
|
{
|
|
DWORD nLoops;
|
|
LOOP *pLoop;
|
|
GLenum orientation;
|
|
|
|
nLoops = pLoopList->nLoops;
|
|
if( !nLoops )
|
|
return WFO_SUCCESS;
|
|
|
|
/*
|
|
* Determine orientation of loop
|
|
*/
|
|
orientation = LoopOrientation( pLoopList );
|
|
|
|
glFrontFace( orientation );
|
|
|
|
pLoop = pLoopList->LoopBuf;
|
|
for( ; nLoops; nLoops--, pLoop++ ) {
|
|
|
|
// Calculate face normals
|
|
if( !CalculateFaceNormals( pLoop, orientation ) )
|
|
return WFO_FAILURE;
|
|
|
|
// Consolidate list of prims
|
|
ConsolidatePrims( pLoop );
|
|
|
|
// Calculate vertex normals
|
|
if( !CalculateVertexNormals( pLoop ) ) {
|
|
FreeLoopMem( pLoop ); // free mem alloc'd by CalculateFaceNormals
|
|
return WFO_FAILURE;
|
|
}
|
|
|
|
DrawPrims( ec, pLoop );
|
|
|
|
// Free memory allocated during loop processing
|
|
FreeLoopMem( pLoop );
|
|
}
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FreeLoopMem
|
|
*
|
|
* Frees up memory associated with each prim loop
|
|
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
FreeLoopMem( LOOP *pLoop )
|
|
{
|
|
PRIM *pPrim;
|
|
|
|
if( !pLoop )
|
|
return;
|
|
|
|
if( pLoop->FNormBuf )
|
|
FREE( pLoop->FNormBuf );
|
|
if( pLoop->VNormBuf )
|
|
FREE( pLoop->VNormBuf );
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DrawPrims
|
|
*
|
|
* Draws a loop of Prims
|
|
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
DrawPrims( EXTRContext *ec, LOOP *pLoop )
|
|
{
|
|
PRIM *pPrim;
|
|
DWORD nPrims;
|
|
|
|
nPrims = pLoop->nPrims;
|
|
pPrim = pLoop->PrimBuf;
|
|
|
|
for( ; nPrims; nPrims--, pPrim++ ) {
|
|
|
|
switch( pPrim->primType ) {
|
|
case PRIM_LINE:
|
|
DrawQuads( pPrim, ec->zExtrusion );
|
|
break;
|
|
|
|
case PRIM_CURVE:
|
|
DrawQuadStrip( ec, pPrim );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//#define EXTRANORMAL 1
|
|
|
|
/*****************************************************************************
|
|
* DrawQuads
|
|
*
|
|
* Draws independent quads of a PRIM.
|
|
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
DrawQuads( PRIM *pPrim, FLOAT zExtrusion )
|
|
{
|
|
POINT2D *p;
|
|
POINT3D *pNorm;
|
|
ULONG quadCount;
|
|
|
|
quadCount = pPrim->nVerts - 1;
|
|
|
|
glBegin( GL_QUADS );
|
|
|
|
p = pPrim->pVert;
|
|
pNorm = pPrim->pFNorm;
|
|
|
|
while( quadCount-- ) {
|
|
Normalize2d( (POINT2D *) pNorm ); // normalize
|
|
glNormal3fv( (GLfloat *) pNorm );
|
|
|
|
glVertex3f( p->x, p->y, 0.0f );
|
|
glVertex3f( p->x, p->y, zExtrusion );
|
|
p++;
|
|
#ifdef EXTRANORMAL
|
|
glNormal3fv( (GLfloat *) pNorm );
|
|
#endif
|
|
glVertex3f( p->x, p->y, zExtrusion );
|
|
glVertex3f( p->x, p->y, 0.0f );
|
|
pNorm++;
|
|
}
|
|
|
|
glEnd();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* DrawQuadStrip
|
|
*
|
|
* Draws a quadstrip from a PRIM
|
|
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
DrawQuadStrip( EXTRContext *ec, PRIM *pPrim )
|
|
{
|
|
#ifndef VARRAY
|
|
POINT3D *pNorm;
|
|
POINT2D *p;
|
|
ULONG nVerts;
|
|
|
|
|
|
glBegin( GL_QUAD_STRIP );
|
|
|
|
// initialize pointers, setup
|
|
nVerts = pPrim->nVerts;
|
|
p = pPrim->pVert;
|
|
pNorm = pPrim->pVNorm;
|
|
|
|
while( nVerts-- ) {
|
|
glNormal3fv( (GLfloat *) pNorm );
|
|
glVertex3f( p->x, p->y, 0.0f );
|
|
#ifdef EXTRANORMAL
|
|
glNormal3fv( (GLfloat *) pNorm );
|
|
#endif
|
|
glVertex3f( p->x, p->y, ec->zExtrusion );
|
|
|
|
// reset pointers
|
|
p++; // next point
|
|
pNorm++; // next vertex normal
|
|
}
|
|
|
|
glEnd();
|
|
#else
|
|
POINT3D *n;
|
|
POINT2D *p;
|
|
ULONG nVerts;
|
|
ULONG i;
|
|
FLOAT *pDst, *pVert, *pNorm;
|
|
|
|
nVerts = pPrim->nVerts;
|
|
|
|
// For every vertex in prim, need in varray buf: 2 verts, 2 normals
|
|
if( !VArrayBufSize( ec, nVerts * 2 * 2 * 3) )
|
|
return; // nothing drawn
|
|
|
|
// setup vertices
|
|
|
|
p = pPrim->pVert;
|
|
pVert = pDst = ec->vaBuf;
|
|
for( i = 0; i < nVerts; i++, p++ ) {
|
|
*pDst++ = p->x;
|
|
*pDst++ = p->y;
|
|
*pDst++ = 0.0f;
|
|
*pDst++ = p->x;
|
|
*pDst++ = p->y;
|
|
*pDst++ = ec->zExtrusion;
|
|
}
|
|
|
|
// setup normals
|
|
|
|
n = pPrim->pVNorm;
|
|
pNorm = pDst;
|
|
for( i = 0; i < nVerts; i++, n++ ) {
|
|
*( ((POINT3D *) pDst)++ ) = *n;
|
|
*( ((POINT3D *) pDst)++ ) = *n;
|
|
}
|
|
|
|
// send it
|
|
glEnable(GL_NORMAL_ARRAY_EXT);
|
|
glWFOVertexPointerEXT(3, GL_FLOAT, 0, nVerts*2, pVert );
|
|
glWFONormalPointerEXT( GL_FLOAT, 0, nVerts*2, pNorm );
|
|
glWFODrawArraysEXT( GL_QUAD_STRIP, 0, nVerts*2);
|
|
glDisable(GL_NORMAL_ARRAY_EXT);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* DrawFacePolygons
|
|
*
|
|
* Draws the front or back facing polygons of a glyph.
|
|
* If z is 0.0, the front face of the glyph is drawn, otherwise the back
|
|
* face is drawn.
|
|
|
|
*****************************************************************************/
|
|
#ifdef VARRAY
|
|
void
|
|
#else
|
|
static void
|
|
#endif
|
|
DrawFacePolygons( EXTRContext *ec, FLOAT z )
|
|
{
|
|
ULONG primCount, vertexCount;
|
|
GLenum primType;
|
|
FLOAT *FaceBuf = ec->FaceBuf;
|
|
FLOAT *p;
|
|
#ifdef VARRAY
|
|
POINT3D normal = {0.0f, 0.0f, 0.0f};
|
|
FLOAT *pVert, *pNorm, *pDst;
|
|
ULONG i;
|
|
#endif
|
|
|
|
if( z == 0.0f ) {
|
|
glNormal3f( 0.0f, 0.0f, 1.0f );
|
|
glFrontFace( GL_CCW );
|
|
} else {
|
|
glNormal3f( 0.0f, 0.0f, -1.0f );
|
|
glFrontFace( GL_CW );
|
|
}
|
|
|
|
primCount = (ULONG) FaceBuf[0];
|
|
p = &FaceBuf[1];
|
|
|
|
#ifndef VARRAY
|
|
while( primCount-- ) {
|
|
|
|
primType = (GLenum) *p++;
|
|
vertexCount = (ULONG) *p++;
|
|
|
|
glBegin( primType );
|
|
|
|
for( ; vertexCount; vertexCount--, p+=2 )
|
|
glVertex3f( p[0], p[1], z );
|
|
|
|
glEnd();
|
|
}
|
|
#else
|
|
if( z == 0.0f )
|
|
normal.z = 1.0f;
|
|
else
|
|
normal.z = -1.0f;
|
|
|
|
while( primCount-- ) {
|
|
|
|
primType = (GLenum) *p++;
|
|
vertexCount = (ULONG) *p++;
|
|
|
|
if( !VArrayBufSize( ec, vertexCount * 3 ) )
|
|
return; // nothing drawn
|
|
|
|
pVert = pDst = ec->vaBuf;
|
|
|
|
// put vertices into varray buf
|
|
for( i = 0; i < vertexCount; i++, p+=2 ) {
|
|
*pDst++ = p[0];
|
|
*pDst++ = p[1];
|
|
*pDst++ = z;
|
|
}
|
|
|
|
glWFOVertexPointerEXT(3, GL_FLOAT, 0, vertexCount, pVert );
|
|
glWFODrawArraysEXT( primType, 0, vertexCount );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ConsolidatePrims
|
|
*
|
|
* Consolidate a loop of prims.
|
|
* Go through list of prims, consolidating consecutive Curve and Line prims
|
|
* When 2 prims are consolidated into one, the first prim is set to
|
|
* null by setting it's nVerts=0. The second prim get's the first's stuff.
|
|
* If joining occured, the array of prims is compacted at the end.
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
ConsolidatePrims( LOOP *pLoop )
|
|
{
|
|
DWORD nPrims, nJoined = 0;
|
|
BOOL bJoined;
|
|
PRIM *pPrim, *pPrevPrim;
|
|
int trans;
|
|
double angle;
|
|
|
|
nPrims = pLoop->nPrims;
|
|
if( nPrims < 2 )
|
|
return;
|
|
|
|
pPrim = pLoop->PrimBuf;
|
|
pPrevPrim = pPrim++;
|
|
|
|
nPrims--; // nPrim-1 comparisons
|
|
for( ; nPrims; nPrims--, pPrevPrim = pPrim++ ) {
|
|
|
|
bJoined = FALSE;
|
|
trans = PrimTransition( pPrevPrim, pPrim );
|
|
switch( trans ) {
|
|
case EXTR_LINE_LINE:
|
|
// always consolidate 2 lines
|
|
bJoined = TRUE;
|
|
break;
|
|
|
|
case EXTR_LINE_CURVE:
|
|
break;
|
|
|
|
case EXTR_CURVE_LINE:
|
|
break;
|
|
|
|
case EXTR_CURVE_CURVE:
|
|
/*
|
|
* Join the prims if angle_between_norms < cutoff_angle
|
|
*/
|
|
angle = PrimNormAngle( pPrevPrim, pPrim );
|
|
if( angle < CurveCurveCutoffAngle ) {
|
|
bJoined = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
if( bJoined ) {
|
|
// nullify the prev prim - move all data to current prim
|
|
pPrim->nVerts += (pPrevPrim->nVerts - 1);
|
|
pPrim->pVert = pPrevPrim->pVert;
|
|
pPrim->pFNorm = pPrevPrim->pFNorm;
|
|
pPrevPrim->nVerts = 0;
|
|
nJoined++;
|
|
}
|
|
}
|
|
|
|
if( nJoined ) {
|
|
// one or more prims eliminated - compact the list
|
|
|
|
nPrims = pLoop->nPrims;
|
|
pPrim = pLoop->PrimBuf;
|
|
// set new nPrims value
|
|
pLoop->nPrims = nPrims - nJoined;
|
|
nJoined = 0; // nJoined now used as counter
|
|
for( ; nPrims; nPrims--, pPrim++ ) {
|
|
if( pPrim->nVerts == 0 ) {
|
|
nJoined++;
|
|
continue;
|
|
}
|
|
*(pPrim-nJoined) = *pPrim;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PrimTransition
|
|
*
|
|
* Given two adjacent prims, returns a code based on prim-type transition.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static int
|
|
PrimTransition( PRIM *pPrevPrim, PRIM *pPrim )
|
|
{
|
|
int trans;
|
|
|
|
if( pPrevPrim->primType == PRIM_LINE ) {
|
|
if( pPrim->primType == PRIM_LINE )
|
|
trans = EXTR_LINE_LINE;
|
|
else
|
|
trans = EXTR_LINE_CURVE;
|
|
} else {
|
|
if( pPrim->primType == PRIM_LINE )
|
|
trans = EXTR_CURVE_LINE;
|
|
else
|
|
trans = EXTR_CURVE_CURVE;
|
|
}
|
|
|
|
return trans;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* LoopOrientation
|
|
*
|
|
* Check for glyphs that have incorrectly specified the contour direction (for
|
|
* example, many of the Wingding glyphs). We do this by first determining
|
|
* the loop in the glyph that has the largest extent. We then make the
|
|
* assumption that this loop is external, and check it's orientation. If
|
|
* the orientation is CCW (non-default), we have to set the orientation to
|
|
* GL_CCW in the extrusion context, so that normals will be generated
|
|
* correctly.
|
|
|
|
* The method used here may fail for any loops that intersect themselves.
|
|
* This will happen if the loops created by the intersections are in the opposite
|
|
* direction to the main loop (if 1 such extra loop exists, then the sum of
|
|
* angles around the entire contour will be 0 - we put in a check for this,
|
|
* and always default to CW in this case)
|
|
*
|
|
* Note that this method *always* works for properly designed TruyType glyphs.
|
|
* From the TrueType font spec "The direction of the curves has to be such that,
|
|
* if the curve is followed in the direction of increasing point numbers, the
|
|
* black space (the filled area) will always be to the right." So this means
|
|
* that the outer loop should always be CW.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
// These macros handle the rare case of a self-intersecting, polarity-reversing
|
|
// loop as explained above. (Observed in animals1.ttf) Note that will only
|
|
// catch some cases.
|
|
#define INTERSECTING_LOOP_WORKAROUND 1
|
|
#define NEAR_ZERO( fAngle ) \
|
|
( fabs(fAngle) < 0.00001 )
|
|
|
|
static GLenum
|
|
LoopOrientation( LOOP_LIST *pLoopList )
|
|
{
|
|
DWORD nLoops, nVerts;
|
|
double angle = 0;
|
|
POINT2D *p1, *p2, v1, v2;
|
|
LOOP *pMaxLoop;
|
|
|
|
nLoops = pLoopList->nLoops;
|
|
if( !nLoops )
|
|
return GL_CW; // default value
|
|
|
|
// determine which loop has the maximum extent
|
|
|
|
pMaxLoop = GetMaxExtentLoop( pLoopList );
|
|
|
|
nVerts = pMaxLoop->nVerts;
|
|
if( nVerts < 3 )
|
|
return GL_CW; // can't determine angle
|
|
|
|
p1 = pMaxLoop->VertBuf + nVerts - 2; // 2nd to last point
|
|
p2 = pMaxLoop->VertBuf; // first point
|
|
|
|
/*
|
|
* Accumulate relative angle between consecutive line segments along
|
|
* the loop - this will tell us the loop's orientation.
|
|
*/
|
|
v1.x = p2->x - p1->x;
|
|
v1.y = p2->y - p1->y;
|
|
nVerts--; // n-1 comparisons
|
|
|
|
for( ; nVerts; nVerts-- ) {
|
|
// calc next vector
|
|
p1 = p2++;
|
|
v2.x = p2->x - p1->x;
|
|
v2.y = p2->y - p1->y;
|
|
angle += CalcAngle( &v1, &v2 );
|
|
v1 = v2;
|
|
}
|
|
|
|
#ifdef INTERSECTING_LOOP_WORKAROUND
|
|
if( NEAR_ZERO( angle ) ) {
|
|
DBGPRINT( "wglUseFontOutlines:LoopOrientation : Total loop angle is zero, assuming CW orientation\n" );
|
|
return GL_CW;
|
|
}
|
|
#endif
|
|
|
|
if( angle > 0.0 )
|
|
return GL_CCW;
|
|
else
|
|
return GL_CW;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* GetMaxExtentLoop
|
|
*
|
|
* Determine which of the loops in a glyph description has the maximum
|
|
* extent, and return a ptr to it. We check extents in the x direction.
|
|
|
|
*****************************************************************************/
|
|
|
|
LOOP *
|
|
GetMaxExtentLoop( LOOP_LIST *pLoopList )
|
|
{
|
|
DWORD nLoops, nVerts;
|
|
FLOAT curxExtent, xExtent=0.0f, x, xMin, xMax;
|
|
LOOP *pMaxLoop, *pLoop;
|
|
POINT2D *p;
|
|
|
|
pMaxLoop = pLoop = pLoopList->LoopBuf;
|
|
|
|
nLoops = pLoopList->nLoops;
|
|
if( nLoops == 1 )
|
|
// just one loop - no comparison required
|
|
return pMaxLoop;
|
|
|
|
for( ; nLoops; nLoops--, pLoop++ ) {
|
|
nVerts = pLoop->nVerts;
|
|
p = pLoop->VertBuf;
|
|
// use x value of first point as reference
|
|
x = p->x;
|
|
xMin = xMax = x;
|
|
// compare x's of rest of points
|
|
for( ; nVerts; nVerts--, p++ ) {
|
|
x = p->x;
|
|
if( x < xMin )
|
|
xMin = x;
|
|
else if( x > xMax )
|
|
xMax = x;
|
|
}
|
|
curxExtent = xMax - xMin;
|
|
if( curxExtent > xExtent ) {
|
|
xExtent = curxExtent;
|
|
pMaxLoop = pLoop;
|
|
}
|
|
}
|
|
return pMaxLoop;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CalcAngle
|
|
*
|
|
* Determine the signed angle between 2 vectors. The angle is measured CCW
|
|
* from vector 1 to vector 2.
|
|
|
|
*****************************************************************************/
|
|
|
|
double
|
|
CalcAngle( POINT2D *v1, POINT2D *v2 )
|
|
{
|
|
double angle1, angle2, angle;
|
|
|
|
// Calculate absolute angle of each vector
|
|
|
|
/* Check for (0,0) vectors - this shouldn't happen unless 2 consecutive
|
|
* vertices in the VertBuf are equal.
|
|
*/
|
|
if( (v1->y == 0.0f) && (v1->x == 0.0f) )
|
|
angle1 = 0.0f;
|
|
else
|
|
angle1 = __GL_ATAN2F( v1->y, v1->x ); // range: -PI to PI
|
|
|
|
if( (v2->y == 0.0f) && (v2->x == 0.0f) )
|
|
angle1 = 0.0f;
|
|
else
|
|
angle2 = __GL_ATAN2F( v2->y, v2->x ); // range: -PI to PI
|
|
|
|
// Calculate relative angle between vectors
|
|
angle = angle2 - angle1; // range: -2*PI to 2*PI
|
|
|
|
// force angle to be in range -PI to PI
|
|
if( angle < -PI )
|
|
angle += TWO_PI;
|
|
else if( angle > PI )
|
|
angle -= TWO_PI;
|
|
|
|
return angle;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CalculateFaceNormals
|
|
*
|
|
* Calculate face normals for a prim loop.
|
|
* The normals are NOT normalized.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
CalculateFaceNormals( LOOP *pLoop,
|
|
GLenum orientation )
|
|
{
|
|
DWORD nPrims;
|
|
ULONG nQuads = 0;
|
|
POINT2D *p;
|
|
POINT3D *pNorm;
|
|
PRIM *pPrim;
|
|
|
|
// Need 1 normal per vertex
|
|
pNorm = (POINT3D*) ALLOC(pLoop->nVerts*sizeof(POINT3D));
|
|
pLoop->FNormBuf = pNorm;
|
|
if( !pNorm )
|
|
return WFO_FAILURE;
|
|
|
|
// Calculate the face normals
|
|
|
|
nPrims = pLoop->nPrims;
|
|
pPrim = pLoop->PrimBuf;
|
|
for( ; nPrims; nPrims--, pPrim++ ) {
|
|
pPrim->pFNorm = pNorm; // ptr to each prims norms
|
|
nQuads = pPrim->nVerts - 1;
|
|
p = pPrim->pVert;
|
|
for( ; nQuads; nQuads--, p++, pNorm++ ) {
|
|
CalcNormal2d( p, (POINT2D *) pNorm, orientation );
|
|
pNorm->z = 0.0f; // normals in xy plane
|
|
}
|
|
}
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CalculateVertexNormals
|
|
*
|
|
* Calculate vertex normals for a prim loop, only for those prims that
|
|
* are of type 'CURVE'.
|
|
* Uses previously calculated face normals to generate the vertex normals.
|
|
* Allocates memory for the normals by calculating memory requirements on
|
|
* the fly.
|
|
* The normals are normalized.
|
|
* Handles closing of loops properly.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
CalculateVertexNormals( LOOP *pLoop )
|
|
{
|
|
ULONG nPrims, nVerts = 0;
|
|
POINT3D *pVNorm, *pFNorm, *pDstNorm;
|
|
PRIM *pPrim, *pPrevPrim;
|
|
double angle;
|
|
GLenum trans;
|
|
|
|
// How much memory we need for the normals?
|
|
|
|
nPrims = pLoop->nPrims;
|
|
pPrim = pLoop->PrimBuf;
|
|
for( ; nPrims; nPrims--, pPrim++ ) {
|
|
if( pPrim->primType == PRIM_CURVE )
|
|
nVerts += pPrim->nVerts;
|
|
}
|
|
|
|
if( !nVerts )
|
|
return WFO_SUCCESS;
|
|
|
|
// XXX: could just allocate 2*nVerts of mem for the normals
|
|
pVNorm = (POINT3D*) ALLOC( nVerts*sizeof(POINT3D) );
|
|
pLoop->VNormBuf = pVNorm;
|
|
if( !pVNorm )
|
|
return WFO_FAILURE;
|
|
|
|
// First pass: calculate normals for all vertices of Curve prims
|
|
|
|
nPrims = pLoop->nPrims;
|
|
pPrim = pLoop->PrimBuf;
|
|
for( ; nPrims; nPrims--, pPrim++ ) {
|
|
|
|
if( pPrim->primType == PRIM_LINE )
|
|
continue;
|
|
|
|
nVerts = pPrim->nVerts;
|
|
pPrim->pVNorm = pVNorm; // ptr to each prims norms
|
|
pFNorm = pPrim->pFNorm; // ptr to face norms already calculated
|
|
|
|
// set the first vnorm to the fnorm
|
|
*pVNorm = *pFNorm;
|
|
|
|
Normalize2d( (POINT2D *) pVNorm ); // normalize it
|
|
nVerts--; // one less vertex to worry about
|
|
pVNorm++; // advance ptrs
|
|
pFNorm++;
|
|
|
|
nVerts--; // do last vertex after this loop
|
|
for( ; nVerts; nVerts--, pFNorm++, pVNorm++ ) {
|
|
// use neighbouring face normals to get vertex normal
|
|
AddVectors3d( pFNorm, pFNorm-1, pVNorm );
|
|
Normalize2d( (POINT2D *) pVNorm ); // normalize it
|
|
}
|
|
|
|
// last vnorm is same as fnorm of *previous* vertex
|
|
*pVNorm = *(pFNorm-1);
|
|
Normalize2d( (POINT2D *) pVNorm ); // normalize it
|
|
|
|
pVNorm++; // next available space in vnorm buffer
|
|
}
|
|
|
|
// Second pass: calculate normals on prim boundaries
|
|
|
|
nPrims = pLoop->nPrims;
|
|
pPrim = pLoop->PrimBuf;
|
|
// set pPrevPrim to last prim in loop
|
|
pPrevPrim = pLoop->PrimBuf + pLoop->nPrims - 1;
|
|
|
|
for( ; nPrims; nPrims--, pPrevPrim = pPrim++ ) {
|
|
trans = PrimTransition( pPrevPrim, pPrim );
|
|
angle = PrimNormAngle( pPrevPrim, pPrim );
|
|
|
|
switch( trans ) {
|
|
case EXTR_LINE_CURVE:
|
|
if( angle < LineCurveCutoffAngle ) {
|
|
// set curve's first vnorm to line's last fnorm
|
|
*(pPrim->pVNorm) =
|
|
*(pPrevPrim->pFNorm + pPrevPrim->nVerts -2);
|
|
Normalize2d( (POINT2D *) pPrim->pVNorm );
|
|
}
|
|
break;
|
|
|
|
case EXTR_CURVE_LINE:
|
|
if( angle < LineCurveCutoffAngle ) {
|
|
// set curve's last vnorm to line's first fnorm
|
|
pDstNorm = pPrevPrim->pVNorm + pPrevPrim->nVerts - 1;
|
|
*pDstNorm = *(pPrim->pFNorm);
|
|
Normalize2d( (POINT2D *) pDstNorm );
|
|
}
|
|
break;
|
|
|
|
case EXTR_CURVE_CURVE:
|
|
if( angle < CurveCurveCutoffAngle ) {
|
|
// average normals of adjoining faces, and
|
|
// set last curve's first vnorm to averaged normal
|
|
AddVectors3d( pPrevPrim->pFNorm + pPrevPrim->nVerts - 2,
|
|
pPrim->pFNorm,
|
|
pPrim->pVNorm );
|
|
Normalize2d( (POINT2D *) pPrim->pVNorm );
|
|
// set first curve's last vnorm to averaged normal
|
|
*(pPrevPrim->pVNorm + pPrevPrim->nVerts - 1) =
|
|
*(pPrim->pVNorm);
|
|
}
|
|
break;
|
|
case EXTR_LINE_LINE:
|
|
// nothing to do
|
|
break;
|
|
}
|
|
|
|
}
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* PrimNormAngle
|
|
*
|
|
* Determine angle between the last face's normal of primA, and the first
|
|
* face's normal of primB.
|
|
*
|
|
* The result should be an angle between -PI and PI.
|
|
* For now, we only care about the relative angle, so we return the
|
|
* absolute value of the signed angle between the faces.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static double
|
|
PrimNormAngle( PRIM *pPrimA, PRIM *pPrimB )
|
|
{
|
|
double angle;
|
|
// last face norm at index (nvert-2)
|
|
POINT3D *normA = pPrimA->pFNorm + pPrimA->nVerts - 2;
|
|
POINT3D *normB = pPrimB->pFNorm;
|
|
|
|
angle = CalcAngle( (POINT2D *) normA, (POINT2D *) normB );
|
|
|
|
return fabs(angle); // don't care about sign of angle for now
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* InitFaceBuf
|
|
*
|
|
* Initializes FaceBuf and its associated size and current-element
|
|
* counters.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
InitFaceBuf( EXTRContext *ec )
|
|
{
|
|
DWORD initSize = 1000;
|
|
|
|
if( !(ec->FaceBuf =
|
|
(FLOAT*) ALLOC(initSize*sizeof(FLOAT))) )
|
|
return WFO_FAILURE;
|
|
ec->FaceBufSize = initSize;
|
|
ec->FaceBufIndex = 0;
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* AppendToFaceBuf
|
|
*
|
|
* Appends one floating-point value to the FaceBuf array.
|
|
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
AppendToFaceBuf(EXTRContext *ec, FLOAT value)
|
|
{
|
|
if (ec->FaceBufIndex >= ec->FaceBufSize)
|
|
{
|
|
if( !ReallocFaceBuf( ec ) )
|
|
return WFO_FAILURE;
|
|
}
|
|
ec->FaceBuf[ec->FaceBufIndex++] = value;
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* ReallocBuf
|
|
*
|
|
* Increases size of FaceBuf by a constant value.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
ReallocFaceBuf( EXTRContext *ec )
|
|
{
|
|
FLOAT* f;
|
|
DWORD increase = 1000; // in floats
|
|
|
|
f = (FLOAT*) REALLOC(ec->FaceBuf,
|
|
(ec->FaceBufSize += increase)*sizeof(FLOAT));
|
|
if (!f)
|
|
return WFO_FAILURE;
|
|
ec->FaceBuf = f;
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CalcNormal2d
|
|
*
|
|
* Calculates the 2d normal of a 2d vector, by rotating the vector:
|
|
* - CCW 90 degrees for CW contours.
|
|
* - CW 90 degrees for CCW contours.
|
|
* Does not normalize.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
CalcNormal2d( POINT2D *p, POINT2D *n, GLenum orientation )
|
|
{
|
|
static POINT2D v;
|
|
|
|
v.x = (p+1)->x - p->x;
|
|
v.y = (p+1)->y - p->y;
|
|
if( orientation == GL_CW ) {
|
|
n->x = -v.y;
|
|
n->y = v.x;
|
|
} else {
|
|
n->x = v.y;
|
|
n->y = -v.x;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* Normalize2d
|
|
*
|
|
* Normalizes a 2d vector
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
Normalize2d( POINT2D *n )
|
|
{
|
|
float len;
|
|
|
|
len = (n->x * n->x) + (n->y * n->y);
|
|
if (len > ZERO_EPS)
|
|
len = 1.0f / __GL_SQRTF(len);
|
|
else
|
|
len = 1.0f;
|
|
|
|
n->x *= len;
|
|
n->y *= len;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* AddVectors3d
|
|
*
|
|
* Adds two 3d vectors.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static void
|
|
AddVectors3d( POINT3D *v1, POINT3D *v2, POINT3D *n )
|
|
{
|
|
n->x = v1->x + v2->x;
|
|
n->y = v1->y + v2->y;
|
|
n->z = v1->z + v2->z;
|
|
}
|
|
|
|
#ifdef VARRAY
|
|
static BOOL
|
|
InitVArray( EXTRContext *ec )
|
|
{
|
|
int size = 500;
|
|
|
|
// set up global buffer
|
|
ec->vaBufSize = size;
|
|
ec->vaBuf = (FLOAT*) ALLOC( size*sizeof(FLOAT) );
|
|
if( !ec->vaBuf ) {
|
|
return WFO_FAILURE;
|
|
}
|
|
|
|
// set up and enable ptrs
|
|
glWFOVertexPointerEXT = (PFNGLVERTEXPOINTEREXTPROC )wglGetProcAddress("glVertexPointerEXT");
|
|
glWFONormalPointerEXT = (PFNGLNORMALPOINTEREXTPROC )wglGetProcAddress("glNormalPointerEXT");
|
|
glWFODrawArraysEXT = (PFNGLDRAWARRAYSEXTPROC )wglGetProcAddress("glDrawArraysEXT");
|
|
|
|
if( (glWFOVertexPointerEXT == NULL)
|
|
|| (glWFONormalPointerEXT == NULL)
|
|
|| (glWFODrawArraysEXT == NULL) ) {
|
|
FREE( ec->vaBuf );
|
|
return WFO_FAILURE;
|
|
}
|
|
|
|
glEnable(GL_VERTEX_ARRAY_EXT);
|
|
return WFO_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Size is in floats
|
|
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static BOOL
|
|
VArrayBufSize( EXTRContext *ec, DWORD size )
|
|
{
|
|
if( size > ec->vaBufSize )
|
|
{
|
|
FLOAT *f;
|
|
|
|
f = (FLOAT*) REALLOC( ec->vaBuf, size*sizeof(FLOAT));
|
|
if( !f )
|
|
return WFO_FAILURE;
|
|
ec->vaBuf = f;
|
|
ec->vaBufSize = size;
|
|
}
|
|
return WFO_SUCCESS;
|
|
}
|
|
#endif
|
|
|