/* ============== CG_SurfaceText Add text in 3D space. Text faces away from axis forward directory. ref entity should have origin, axis, and shaderRGBA set ============== */ void CG_SurfaceText( const refEntity_t *originEnt, const fontInfo_t *font, float scale, const char *text, float adjust, int limit, float gradient, qboolean forceColor ) { int len, count; vec4_t newColor; vec4_t gradientColor; const glyphInfo_t *glyph; const char *s; float yadj, xadj; float useScale; vec3_t baseline; refEntity_t re; polyVert_t verts[4]; float x, y, w, h; int j; if ( !text ) { return; } scale *= 0.25f; // for world scale re = *originEnt; re.reType = RT_POLY_LOCAL; VectorCopy( re.origin, re.oldorigin ); // center justify at origin VectorCopy( vec3_origin, baseline ); x = 0 - Text_Width( text, font, scale, 0 ) / 2; y = 0; useScale = scale * font->glyphScale; newColor[0] = re.shaderRGBA[0] / 255.0f; newColor[1] = re.shaderRGBA[1] / 255.0f; newColor[2] = re.shaderRGBA[2] / 255.0f; newColor[3] = re.shaderRGBA[3] / 255.0f; gradientColor[0] = Com_Clamp( 0, 1, newColor[0] - gradient ); gradientColor[1] = Com_Clamp( 0, 1, newColor[1] - gradient ); gradientColor[2] = Com_Clamp( 0, 1, newColor[2] - gradient ); gradientColor[3] = newColor[3]; len = Q_UTF8_PrintStrlen( text ); if ( limit > 0 && len > limit ) { len = limit; } s = text; count = 0; while ( s && *s && count < len ) { if ( Q_IsColorString( s ) ) { if ( !forceColor ) { VectorCopy( g_color_table[ColorIndex(*(s+1))], newColor ); gradientColor[0] = Com_Clamp( 0, 1, newColor[0] - gradient ); gradientColor[1] = Com_Clamp( 0, 1, newColor[1] - gradient ); gradientColor[2] = Com_Clamp( 0, 1, newColor[2] - gradient ); } s += 2; continue; } glyph = Text_GetGlyph( font, Q_UTF8_CodePoint( &s ) ); yadj = useScale * glyph->top; xadj = useScale * glyph->left; w = glyph->imageWidth * useScale; h = glyph->imageHeight * useScale; // 0 1 // 3 2 verts[0].xyz[0] = baseline[0] + 0; verts[0].xyz[1] = baseline[1] - ( x + xadj ); verts[0].xyz[2] = baseline[2] + y + yadj; verts[1].xyz[0] = baseline[0] + 0; verts[1].xyz[1] = baseline[1] - ( x + xadj + w ); verts[1].xyz[2] = baseline[2] + y + yadj; verts[2].xyz[0] = baseline[0] + 0; verts[2].xyz[1] = baseline[1] - ( x + xadj + w ); verts[2].xyz[2] = baseline[2] + y + yadj - h; verts[3].xyz[0] = baseline[0] + 0; verts[3].xyz[1] = baseline[1] - ( x + xadj ); verts[3].xyz[2] = baseline[2] + y + yadj - h; // standard square texture coordinates for ( j = 0; j < 4; j++ ) { verts[j].st[0] = ( j == 0 || j == 3 ) ? glyph->s : glyph->s2; verts[j].st[1] = ( j < 2 ) ? glyph->t : glyph->t2; if ( j < 2 || gradient == 0 ) { verts[j].modulate[0] = newColor[0] * 0xFF; verts[j].modulate[1] = newColor[1] * 0xFF; verts[j].modulate[2] = newColor[2] * 0xFF; verts[j].modulate[3] = newColor[3] * 0xFF; } else { verts[j].modulate[0] = gradientColor[0] * 0xFF; verts[j].modulate[1] = gradientColor[1] * 0xFF; verts[j].modulate[2] = gradientColor[2] * 0xFF; verts[j].modulate[3] = gradientColor[3] * 0xFF; } } re.customShader = glyph->glyph; re.radius = w / 2; trap_R_AddPolyRefEntityToScene( &re, 4, verts, 1 ); x += ( glyph->xSkip * useScale ) + adjust; count++; } // debug axis //re.reType = RT_MODEL; //re.hModel = 0; //trap_R_AddRefEntityToScene( &re ); }
/* =========== G_ClientCleanName ============ */ static void G_ClientCleanName( const char *in, char *out, int outSize, gclient_t *client ) { int len, colorlessLen; char *p; int spaces; qboolean escaped; qboolean invalid = qfalse; qboolean hasletter = qfalse; //save room for trailing null byte outSize--; len = 0; colorlessLen = 0; p = out; *p = 0; spaces = 0; for ( ; *in; in++ ) { int cp, w; // don't allow leading spaces if ( colorlessLen == 0 && *in == ' ' ) { continue; } // don't allow nonprinting characters or (dead) console keys // but do allow UTF-8 (unvalidated) if ( *in >= 0 && *in < ' ' ) { continue; } // check colors if ( Q_IsColorString( in ) ) { in++; // make sure room in dest for both chars if ( len > outSize - 2 ) { break; } *out++ = Q_COLOR_ESCAPE; *out++ = *in; len += 2; continue; } else if ( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped ) ) { // make sure room in dest for both chars if ( len > outSize - 2 ) { break; } *out++ = '['; *out++ = '['; len += 2; if ( escaped ) { in++; } continue; } cp = Q_UTF8_CodePoint( in ); if ( Q_Unicode_IsAlphaOrIdeo( cp ) ) { hasletter = qtrue; } // don't allow too many consecutive spaces if ( *in == ' ' ) { spaces++; if ( spaces > 3 ) { continue; } } else { spaces = 0; } w = Q_UTF8_WidthCP( cp ); if ( len > outSize - w ) { break; } memcpy( out, in, w ); colorlessLen++; len += w; out += w; in += w - 1; // allow for loop increment } *out = 0; // don't allow names beginning with S_SKIPNOTIFY because it messes up /ignore-related code if ( !Q_strnicmp( p, S_SKIPNOTIFY, 12 ) ) { invalid = qtrue; } // don't allow comment-beginning strings because it messes up various parsers if ( strstr( p, "//" ) || strstr( p, "/*" ) ) { invalid = qtrue; } // don't allow empty names if ( *p == 0 || colorlessLen == 0 ) { invalid = qtrue; } // limit no. of code points if ( Q_UTF8_PrintStrlen( p ) > MAX_NAME_LENGTH_CP ) { invalid = qtrue; } // if something made the name bad, put them back to UnnamedPlayer if ( invalid || !hasletter ) { Q_strncpyz( p, G_UnnamedClientName( client ), outSize ); } }