/*
==============
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 );
}
Example #2
0
/*
===========
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 );
	}
}