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(); } }
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); } }
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(); } } }
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; }
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); }