예제 #1
0
static void draw(const Svg* svg)
{
    GLenum nextPrimType, primType = GL_LINE_STRIP;
    const SvgLine* lIt;
    uint i;

    DENG_ASSERT_IN_MAIN_THREAD();
    DENG_ASSERT_GL_CONTEXT_ACTIVE();

    lIt = svg->lines;
    for(i = 0; i < svg->lineCount; ++i, lIt++)
    {
        if(lIt->numPoints != 2)
        {
            nextPrimType = SvgLine_IsLoop(lIt)? GL_LINE_LOOP : GL_LINE_STRIP;

            // Do we need to end the current primitive?
            if(primType == GL_LINES)
            {
                glEnd(); // 2-vertex set ends.
            }

            // A new n-vertex primitive begins.
            glBegin(nextPrimType);
        }
        else
        {
            // Do we need to start a new 2-vertex primitive set?
            if(primType != GL_LINES)
            {
                primType = GL_LINES;
                glBegin(GL_LINES);
            }
        }

        // Write the vertex data.
        if(lIt->head)
        {
            const SvgLinePoint* pIt = lIt->head;
            do
            {
                /// @todo Use TexGen?
                glTexCoord2dv((const GLdouble*)pIt->coords.xy);
                glVertex2dv((const GLdouble*)pIt->coords.xy);
            } while(NULL != (pIt = pIt->next) && pIt != lIt->head);
        }

        if(lIt->numPoints != 2)
        {
            glEnd(); // N-vertex primitive ends.
        }
    }

    if(primType == GL_LINES)
    {
        // Close any remaining open 2-vertex set.
        glEnd();
    }
}
예제 #2
0
void H_SetupState(bool dosetup)
{
    DENG_ASSERT_IN_MAIN_THREAD();
    DENG_ASSERT_GL_CONTEXT_ACTIVE();

    if(dosetup)
    {
        glDepthMask(GL_FALSE);
        glDisable(GL_DEPTH_TEST);
        GL_BlendMode(BM_ADD);
    }
    else
    {
        GL_BlendMode(BM_NORMAL);
        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
    }
}
예제 #3
0
static void textFragmentDrawer(const char* fragment, int x, int y, int alignFlags,
    short textFlags, int initialCount)
{
    assert(fragment && fragment[0]);
    {
    font_t* font = Fonts_ToFont(fr.fontNum);
    fr_state_attributes_t* sat = currentAttribs();
    boolean noTypein = (textFlags & DTF_NO_TYPEIN) != 0;
    boolean noGlitter = (sat->glitterStrength <= 0 || (textFlags & DTF_NO_GLITTER) != 0);
    boolean noShadow  = (sat->shadowStrength  <= 0 || (textFlags & DTF_NO_SHADOW)  != 0 ||
                         (Font_Flags(font) & FF_SHADOWED) != 0);
    boolean noCharacter = (textFlags & DTF_NO_CHARACTER) != 0;
    float glitter = (noGlitter? 0 : sat->glitterStrength), glitterMul;
    float shadow  = (noShadow ? 0 : sat->shadowStrength), shadowMul;
    float flashColor[3] = { 0, 0, 0 };
    int w, h, cx, cy, count, yoff;
    unsigned char c;
    const char* ch;

    if(alignFlags & ALIGN_RIGHT)
        x -= textFragmentWidth(fragment);
    else if(!(alignFlags & ALIGN_LEFT))
        x -= textFragmentWidth(fragment)/2;

    if(alignFlags & ALIGN_BOTTOM)
        y -= textFragmentHeight(fragment);
    else if(!(alignFlags & ALIGN_TOP))
        y -= textFragmentHeight(fragment)/2;

    if(!(noTypein && noGlitter))
    {
        flashColor[CR] = (1 + 2 * sat->rgba[CR]) / 3;
        flashColor[CG] = (1 + 2 * sat->rgba[CG]) / 3;
        flashColor[CB] = (1 + 2 * sat->rgba[CB]) / 3;
    }

    if(renderWireframe > 1)
    {
        DENG_ASSERT_IN_MAIN_THREAD();
        DENG_ASSERT_GL_CONTEXT_ACTIVE();

        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glDisable(GL_TEXTURE_2D);
    }
    if(Font_Type(font) == FT_BITMAP && 0 != BitmapFont_GLTextureName(font))
    {
        GL_BindTextureUnmanaged(BitmapFont_GLTextureName(font), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE,
                                filterUI? GL_LINEAR : GL_NEAREST);

        glMatrixMode(GL_TEXTURE);
        glPushMatrix();
        glLoadIdentity();
        glScalef(1.f / BitmapFont_TextureWidth(font),
                 1.f / BitmapFont_TextureHeight(font), 1.f);
    }

    { int pass;
    for(pass = (noShadow? 1 : 0); pass < (noCharacter && noGlitter? 1 : 2); ++pass)
    {
        count = initialCount;
        ch = fragment;
        cx = x + (pass == 0? sat->shadowOffsetX : 0);
        cy = y + (pass == 0? sat->shadowOffsetY : 0);

        for(;;)
        {
            c = *ch++;
            yoff = 0;

            glitter = (noGlitter? 0 : sat->glitterStrength);
            glitterMul = 0;

            shadow = (noShadow? 0 : sat->shadowStrength);
            shadowMul = (noShadow? 0 : sat->rgba[CA]);

            // Do the type-in effect?
            if(!noTypein && (pass || (!noShadow && !pass)))
            {
                int maxCount = (typeInTime > 0? typeInTime * 2 : 0);

                if(pass)
                {
                    if(!noGlitter)
                    {
                        if(count == maxCount)
                        {
                            glitterMul = 1;
                            flashColor[CR] = sat->rgba[CR];
                            flashColor[CG] = sat->rgba[CG];
                            flashColor[CB] = sat->rgba[CB];
                        }
                        else if(count + 1 == maxCount)
                        {
                            glitterMul = 0.88f;
                            flashColor[CR] = (1 + sat->rgba[CR]) / 2;
                            flashColor[CG] = (1 + sat->rgba[CG]) / 2;
                            flashColor[CB] = (1 + sat->rgba[CB]) / 2;
                        }
                        else if(count + 2 == maxCount)
                        {
                            glitterMul = 0.75f;
                            flashColor[CR] = sat->rgba[CR];
                            flashColor[CG] = sat->rgba[CG];
                            flashColor[CB] = sat->rgba[CB];
                        }
                        else if(count + 3 == maxCount)
                        {
                            glitterMul = 0.5f;
                            flashColor[CR] = sat->rgba[CR];
                            flashColor[CG] = sat->rgba[CG];
                            flashColor[CB] = sat->rgba[CB];
                        }
                        else if(count > maxCount)
                        {
                            break;
                        }
                    }
                    else if(count > maxCount)
                    {
                        break;
                    }
                }
                else
                {
                    if(count == maxCount)
                    {
                        shadowMul = 0;
                    }
                    else if(count + 1 == maxCount)
                    {
                        shadowMul *= .25f;
                    }
                    else if(count + 2 == maxCount)
                    {
                        shadowMul *= .5f;
                    }
                    else if(count + 3 == maxCount)
                    {
                        shadowMul *= .75f;
                    }
                    else if(count > maxCount)
                    {
                        break;
                    }
                }
            }
            count++;

            if(!c || c == '\n')
                break;

            w = FR_CharWidth(c);
            h = FR_CharHeight(c);

            if(' ' != c)
            {
                // A non-white-space character we have a glyph for.
                if(pass)
                {
                    if(!noCharacter)
                    {
                        // The character itself.
                        glColor4fv(sat->rgba);
                        drawChar(c, cx, cy + yoff, font, ALIGN_TOPLEFT, DTF_NO_EFFECTS);
                    }

                    if(!noGlitter && glitter > 0)
                    {
                        // Do something flashy.
                        Point2Raw origin;
                        Size2Raw size;
                        origin.x = cx;
                        origin.y = cy + yoff;
                        size.width  = w;
                        size.height = h;
                        glColor4f(flashColor[CR], flashColor[CG], flashColor[CB], glitter * glitterMul);
                        drawFlash(&origin, &size, true);
                    }
                }
                else if(!noShadow)
                {
                    Point2Raw origin;
                    Size2Raw size;
                    origin.x = cx;
                    origin.y = cy + yoff;
                    size.width  = w;
                    size.height = h;
                    glColor4f(1, 1, 1, shadow * shadowMul);
                    drawFlash(&origin, &size, false);
                }
            }

            cx += w + sat->tracking;
        }
    }}

    // Restore previous GL-state.
    if(renderWireframe > 1)
    {
        /// @todo do not assume previous state.
        glEnable(GL_TEXTURE_2D);
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    if(Font_Type(font) == FT_BITMAP && 0 != BitmapFont_GLTextureName(font))
    {
        glMatrixMode(GL_TEXTURE);
        glPopMatrix();
    }
    }
}
예제 #4
0
bool H_RenderHalo(coord_t x, coord_t y, coord_t z, float size,
    DGLuint tex, float const color[3], coord_t distanceToViewer,
    float occlusionFactor, float brightnessFactor, float viewXOffset,
    bool primary, bool viewRelativeRotate)
{
    int i, k;
    float viewToCenter[3], mirror[3], normalViewToCenter[3];
    float leftOff[3], rightOff[3], center[3], radius;
    float haloPos[3];
    float rgba[4], radX, radY, scale, turnAngle = 0;
    float fadeFactor = 1, secBold, secDimFactor;
    float colorAverage, f, distanceDim;
    flare_t *fl;
    viewdata_t const *viewData = R_ViewData(viewPlayer - ddPlayers);

    // In realistic mode we don't render secondary halos.
    if(!primary && haloRealistic)
        return false;

    if(distanceToViewer <= 0 || occlusionFactor == 0 ||
       (haloFadeMax && distanceToViewer > haloFadeMax))
        return false;

    occlusionFactor = (1 + occlusionFactor) / 2;

    if(haloFadeMax && haloFadeMax != haloFadeMin &&
       distanceToViewer < haloFadeMax && distanceToViewer >= haloFadeMin)
    {
        fadeFactor = (distanceToViewer - haloFadeMin) / (haloFadeMax - haloFadeMin);
    }

    // viewSideVec is to the left.
    for(i = 0; i < 3; ++i)
    {
        leftOff[i] = viewData->upVec[i] + viewData->sideVec[i];
        rightOff[i] = viewData->upVec[i] - viewData->sideVec[i];
    }

    rgba[CR] = color[CR];
    rgba[CG] = color[CG];
    rgba[CB] = color[CB];
    rgba[CA] = 1; // Real alpha is set later.

    center[VX] = x;
    center[VZ] = y;
    center[VY] = z;

    // Apply the flare's X offset. (Positive is to the right.)
    for(i = 0; i < 3; i++)
        center[i] -= viewXOffset * viewData->sideVec[i];

    // Calculate the mirrored position.
    // Project viewtocenter vector onto viewSideVec.
    for(i = 0; i < 3; ++i)
    {
        normalViewToCenter[i] = viewToCenter[i] = center[i] - (float)(vOrigin[i]);
    }
    V3f_Normalize(normalViewToCenter);

    // Calculate the dimming factor for secondary flares.
    secDimFactor = V3f_DotProduct(normalViewToCenter, viewData->frontVec);

    scale = V3f_DotProduct(viewToCenter, viewData->frontVec) / V3f_DotProduct(viewData->frontVec, viewData->frontVec);

    for(i = 0; i < 3; ++i)
        haloPos[i] = mirror[i] = (viewData->frontVec[i] * scale - viewToCenter[i]) * 2;
    // Now adding 'mirror' to a position will mirror it.

    // Calculate texture turn angle.
    if(V3f_Normalize(haloPos))
    {
        // Now halopos is a normalized version of the mirror vector.
        // Both vectors are on the view plane.
        if(viewRelativeRotate)
        {
            turnAngle = V3f_DotProduct(haloPos, viewData->upVec);
            if(turnAngle > 1)
                turnAngle = 1;
            else if(turnAngle < -1)
                turnAngle = -1;

            if(turnAngle >= 1)
                turnAngle = 0;
            else if(turnAngle <= -1)
                turnAngle = float(de::PI);
            else
                turnAngle = acos(turnAngle);

            // On which side of the up vector (left or right)?
            if(V3f_DotProduct(haloPos, viewData->sideVec) < 0)
                turnAngle = -turnAngle;
        }
        else
        {
            turnAngle = 0;
        }
    }

    // The overall brightness of the flare.
    colorAverage = (rgba[CR] + rgba[CG] + rgba[CB] + 1) / 4;

    // Small flares have stronger dimming.
    f = distanceToViewer / size;
    if(haloDimStart && haloDimStart < haloDimEnd && f > haloDimStart)
        distanceDim = 1 - (f - haloDimStart) / (haloDimEnd - haloDimStart);
    else
        distanceDim = 1;

    // Setup GL state.
    if(primary)
        H_SetupState(true);

    DENG_ASSERT_IN_MAIN_THREAD();
    DENG_ASSERT_GL_CONTEXT_ACTIVE();

    // Prepare the texture rotation matrix.
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();
    // Rotate around the center of the texture.
    glTranslatef(0.5f, 0.5f, 0);
    glRotatef(turnAngle / float(de::PI) * 180, 0, 0, 1);
    glTranslatef(-0.5f, -0.5f, 0);

    for(i = 0, fl = flares; i < haloMode && i < NUM_FLARES; ++i, fl++)
    {
        if(primary && i)
            break;
        if(!primary && !i)
            continue;

        // Calculate the dimming factor.
        if(i > 0)
            // Secondary flares receive additional dimming.
            f = MAX_OF(minHaloSize * size / distanceToViewer, 1);
        else
            f = 1;
        f *= distanceDim * brightnessFactor;

        // The rgba & alpha of the flare.
        rgba[CA] = f * (fl->alpha * occlusionFactor * fadeFactor + colorAverage * colorAverage / 5);

        radius = size * (1 - colorAverage / 3) + distanceToViewer / haloZMagDiv;
        if(radius < haloMinRadius)
            radius = haloMinRadius;
        radius *= occlusionFactor;

        secBold = colorAverage - 8 * (1 - secDimFactor);

        rgba[CA] *= .8f * haloBright / 100.0f;
        if(i)
        {
            rgba[CA] *= secBold; // Secondary flare boldness.
        }

        if(rgba[CA] <= 0)
            break; // Not visible.

        // In the realistic mode, halos are slightly dimmer.
        if(haloRealistic)
        {
            rgba[CA] *= .6f;
        }

        if(haloRealistic)
        {
            // The 'realistic' halos just use the blurry round
            // texture unless custom.
            if(!tex)
                tex = GL_PrepareSysFlaremap(FXT_ROUND);
        }
        else
        {
            if(!(primary && tex))
            {
                if(size > 45 || (colorAverage > .90 && size > 20))
                {
                    // The "Very Bright" condition.
                    radius *= .65f;
                    if(!i)
                        tex = GL_PrepareSysFlaremap(FXT_BIGFLARE);
                    else
                        tex = GL_PrepareSysFlaremap(flaretexid_t(fl->texture));
                }
                else
                {
                    if(!i)
                        tex = GL_PrepareSysFlaremap(FXT_ROUND);
                    else
                        tex = GL_PrepareSysFlaremap(flaretexid_t(fl->texture));
                }
            }
        }

        // In the realistic mode, halos are slightly smaller.
        if(haloRealistic)
        {
            radius *= 0.8f;
        }

        // The final radius.
        radX = radius * fl->size;
        radY = radX / 1.2f;

        // Determine the final position of the halo.
        haloPos[VX] = center[VX];
        haloPos[VY] = center[VY];
        haloPos[VZ] = center[VZ];
        if(i)
        {   // Secondary halos.
            // Mirror it according to the flare table.
            for(k = 0; k < 3; ++k)
                haloPos[k] += mirror[k] * fl->offset;
        }

        GL_BindTextureUnmanaged(renderTextures? tex : 0, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
        glEnable(GL_TEXTURE_2D);

        glColor4fv(rgba);

        glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex3f(haloPos[VX] + radX * leftOff[VX],
                       haloPos[VY] + radY * leftOff[VY],
                       haloPos[VZ] + radX * leftOff[VZ]);
            glTexCoord2f(1, 0);
            glVertex3f(haloPos[VX] + radX * rightOff[VX],
                       haloPos[VY] + radY * rightOff[VY],
                       haloPos[VZ] + radX * rightOff[VZ]);
            glTexCoord2f(1, 1);
            glVertex3f(haloPos[VX] - radX * leftOff[VX],
                       haloPos[VY] - radY * leftOff[VY],
                       haloPos[VZ] - radX * leftOff[VZ]);
            glTexCoord2f(0, 1);
            glVertex3f(haloPos[VX] - radX * rightOff[VX],
                       haloPos[VY] - radY * rightOff[VY],
                       haloPos[VZ] - radX * rightOff[VZ]);
        glEnd();

        glDisable(GL_TEXTURE_2D);
    }

    glMatrixMode(GL_TEXTURE);
    glPopMatrix();

    // Restore previous GL state.
    if(primary)
        H_SetupState(false);

    return true;
}
예제 #5
0
void FR_DrawText3(const char* text, const Point2Raw* _origin, int alignFlags, short _textFlags)
{
    fontid_t origFont = FR_Font();
    float cx, cy, extraScale;
    drawtextstate_t state;
    const char* fragment;
    int pass, curCase;
    Point2Raw origin;
    Size2Raw textSize;
    size_t charCount;
    float origColor[4];
    char* str, *end;
    boolean escaped = false;

    errorIfNotInited("FR_DrawText");

    if(!text || !text[0]) return;

    origin.x = _origin? _origin->x : 0;
    origin.y = _origin? _origin->y : 0;

    _textFlags &= ~(DTF_INTERNAL_MASK);

    // If we aren't aligning to top-left we need to know the dimensions.
    if(alignFlags & ALIGN_RIGHT)
        FR_TextSize(&textSize, text);

    DENG_ASSERT_IN_MAIN_THREAD();
    DENG_ASSERT_GL_CONTEXT_ACTIVE();

    // We need to change the current color, so remember for restore.
    glGetFloatv(GL_CURRENT_COLOR, origColor);

    for(pass = ((_textFlags & DTF_NO_SHADOW)  != 0? 1 : 0);
        pass < ((_textFlags & DTF_NO_GLITTER) != 0? 2 : 3); ++pass)
    {
        short textFlags = 0;

        // Configure the next pass.
        cx = (float) origin.x;
        cy = (float) origin.y;
        curCase = -1;
        charCount = 0;
        switch(pass)
        {
        case 0: textFlags = _textFlags | (DTF_NO_GLITTER|DTF_NO_CHARACTER); break;
        case 1: textFlags = _textFlags | (DTF_NO_SHADOW |DTF_NO_GLITTER);   break;
        case 2: textFlags = _textFlags | (DTF_NO_SHADOW |DTF_NO_CHARACTER); break;
        }

        // Apply defaults.
        initDrawTextState(&state, textFlags);

        str = (char*)text;
        while(*str)
        {
            if(*str == FR_FORMAT_ESCAPE_CHAR)
            {
                escaped = true;
                ++str;
                continue;
            }
            if(!escaped && *str == '{') // Paramaters included?
            {
                fontid_t lastFont = state.fontNum;
                int lastTracking = state.tracking;
                float lastLeading = state.leading;
                float lastShadowStrength = state.shadowStrength;
                float lastGlitterStrength = state.glitterStrength;
                boolean lastCaseScale = state.caseScale;
                float lastRGBA[4];
                int numBreaks = 0;

                lastRGBA[CR] = state.rgba[CR];
                lastRGBA[CG] = state.rgba[CG];
                lastRGBA[CB] = state.rgba[CB];
                lastRGBA[CA] = state.rgba[CA];

                parseParamaterBlock(&str, &state, &numBreaks);

                if(numBreaks != 0)
                {
                    do
                    {
                        cx = (float) origin.x;
                        cy += state.lastLineHeight * (1+lastLeading);
                    } while(--numBreaks > 0);
                }

                if(state.fontNum != lastFont)
                    FR_SetFont(state.fontNum);
                if(state.tracking != lastTracking)
                    FR_SetTracking(state.tracking);
                if(state.leading != lastLeading)
                    FR_SetLeading(state.leading);
                if(state.rgba[CR] != lastRGBA[CR] || state.rgba[CG] != lastRGBA[CG] || state.rgba[CB] != lastRGBA[CB] || state.rgba[CA] != lastRGBA[CA])
                    FR_SetColorAndAlphav(state.rgba);
                if(state.shadowStrength != lastShadowStrength)
                    FR_SetShadowStrength(state.shadowStrength);
                if(state.glitterStrength != lastGlitterStrength)
                    FR_SetGlitterStrength(state.glitterStrength);
                if(state.caseScale != lastCaseScale)
                    FR_SetCaseScale(state.caseScale);
            }

            for(end = str; *end && *end != FR_FORMAT_ESCAPE_CHAR && (escaped || *end != '{');)
            {
                int newlines = 0, fragmentAlignFlags;
                float alignx = 0;

                // Find the end of the next fragment.
                if(FR_CaseScale())
                {
                    curCase = -1;
                    // Select a substring with characters of the same case (or whitespace).
                    for(; *end && *end != FR_FORMAT_ESCAPE_CHAR && (escaped || *end != '{') &&
                        *end != '\n'; end++)
                    {
                        escaped = false;

                        // We can skip whitespace.
                        if(isspace(*end))
                            continue;

                        if(curCase < 0)
                            curCase = (isupper(*end) != 0);
                        else if(curCase != (isupper(*end) != 0))
                            break;
                    }
                }
                else
                {
                    curCase = 0;
                    for(; *end && *end != FR_FORMAT_ESCAPE_CHAR && (escaped || *end != '{') &&
                        *end != '\n'; end++) { escaped = false; }
                }

                // No longer escaped.
                escaped = false;

                { char* buffer = enlargeTextBuffer(end - str);
                memcpy(buffer, str, end - str);
                buffer[end - str] = '\0';
                fragment = buffer;
                }

                while(*end == '\n')
                {
                    newlines++;
                    end++;
                }

                // Continue from here.
                str = end;

                if(!(alignFlags & (ALIGN_LEFT|ALIGN_RIGHT)))
                {
                    fragmentAlignFlags = alignFlags;
                }
                else
                {
                    // We'll take care of horizontal positioning of the fragment so align left.
                    fragmentAlignFlags = (alignFlags & ~(ALIGN_RIGHT)) | ALIGN_LEFT;
                    if(alignFlags & ALIGN_RIGHT)
                        alignx = -textSize.width * state.scaleX;
                }

                // Setup the scaling.
                glMatrixMode(GL_MODELVIEW);
                glPushMatrix();

                // Rotate.
                if(state.angle != 0)
                {
                    // The origin is the specified (x,y) for the patch.
                    // We'll undo the aspect ratio (otherwise the result would be skewed).
                    /// @todo Do not assume the aspect ratio and therefore whether
                    // correction is even needed.
                    glTranslatef((float)origin.x, (float)origin.y, 0);
                    glScalef(1, 200.0f / 240.0f, 1);
                    glRotatef(state.angle, 0, 0, 1);
                    glScalef(1, 240.0f / 200.0f, 1);
                    glTranslatef(-(float)origin.x, -(float)origin.y, 0);
                }

                glTranslatef(cx + state.offX + alignx, cy + state.offY + (FR_CaseScale() ? state.caseMod[curCase].offset : 0), 0);
                extraScale = (FR_CaseScale() ? state.caseMod[curCase].scale : 1);
                glScalef(state.scaleX, state.scaleY * extraScale, 1);

                // Draw it.
                if(fr.fontNum)
                {
                    textFragmentDrawer(fragment, 0, 0, fragmentAlignFlags, textFlags, state.typeIn ? (int) charCount : DEFAULT_INITIALCOUNT);
                }
                charCount += strlen(fragment);

                // Advance the current position?
                if(newlines == 0)
                {
                    cx += ((float) textFragmentWidth(fragment) + currentAttribs()->tracking) * state.scaleX;
                }
                else
                {
                    if(strlen(fragment) > 0)
                        state.lastLineHeight = textFragmentHeight(fragment);

                    cx = (float) origin.x;
                    cy += newlines * (float) state.lastLineHeight * (1+FR_Leading());
                }

                glMatrixMode(GL_MODELVIEW);
                glPopMatrix();
            }
        }

        FR_PopAttrib();
    }

    freeTextBuffer();

    FR_SetFont(origFont);
    glColor4fv(origColor);
}