It's not *that* great to use one Text object for each character, but I can't think of another way that still takes advantage of SFML's text loading and rendering.
You can use the Font class and build your own vertex array of rotated glyphs (quads). That would be a lot more work, but a lot more efficient (one CurvedText would be equivalent to a single Text).
Yeah, that works much better:
And it wasn't even *that* much work, since a big part of it was "translating" your Text code:
public class CurvedText : Transformable, Drawable
{
public CurvedText(
string str,
float radius,
float spacing,
Font font,
uint charSize) : this(str, radius, font, charSize)
{
Spacing = spacing;
}
public CurvedText(
string str,
float radius,
Font font = null,
uint charSize = 30)
{
vertices = new VertexArray(PrimitiveType.Quads);
DisplayedString = str;
CharacterSize = charSize;
Radius = radius;
Spacing = 0;
Font = font;
}
public void Draw(RenderTarget target, RenderStates states)
{
if (Font == null)
return;
states.Transform *= Transform;
if (Centered)
states.Transform *= centerTransform;
states.Texture = Font.GetTexture(CharacterSize);
target.Draw(vertices, states);
}
protected void UpdateGeometry()
{
if (font == null)
return;
vertices.Clear();
float hspace = Font.GetGlyph(' ', CharacterSize, true).Advance;
float vspace = Font.GetLineSpacing(CharacterSize);
float previousX = 0;
float x = 0;
float y = CharacterSize;
char previousChar = '\0';
float angleCovered = 0;
Transform transform = Transform.Identity;
foreach (char c in DisplayedString)
{
x += Font.GetKerning(previousChar, c, CharacterSize) + Spacing;
previousChar = c;
switch (c)
{
case ' ':
x += hspace;
continue;
case '\t':
x += 4 * hspace;
continue;
case '\n':
y += vspace;
previousX = 0;
angleCovered = 0;
transform = Transform.Identity;
x = 0;
continue;
case '\v':
y += vspace * 4;
continue;
}
Glyph g = Font.GetGlyph(c, CharacterSize, false);
float x0 = x + g.Bounds.Left;
float y0 = y + g.Bounds.Top;
float x1 = x0 + g.Bounds.Width;
float y1 = y0 + g.Bounds.Height;
float u0 = g.TextureRect.Left;
float v0 = g.TextureRect.Top;
float u1 = u0 + g.TextureRect.Width;
float v1 = v0 + g.TextureRect.Height;
float angle = 2f * (float)Math.Atan((x - previousX) / 2f / Radius)
* 180f / (float)Math.PI;
angleCovered += angle;
transform.Rotate(angle, x0 - g.Bounds.Width / 2f, Radius / 2f);
Vector2f topLeft = new Vector2f(x0, y0);
Vector2f bottomLeft = new Vector2f(x0, y1);
Vector2f topRight = new Vector2f(x1, y0);
Vector2f bottomRight = new Vector2f(x1, y1);
topLeft = transform.TransformPoint(topLeft);
topRight = transform.TransformPoint(topRight);
bottomLeft = transform.TransformPoint(bottomLeft);
bottomRight = transform.TransformPoint(bottomRight);
vertices.Append(new Vertex(topLeft, Color, new Vector2f(u0, v0)));
vertices.Append(new Vertex(topRight, Color, new Vector2f(u1, v0)));
vertices.Append(new Vertex(bottomRight, Color, new Vector2f(u1, v1)));
vertices.Append(new Vertex(bottomLeft, Color, new Vector2f(u0, v1)));
previousX = x;
x+= g.Advance;
}
centerTransform = Transform.Identity;
centerTransform.Rotate(-angleCovered / 2f, 0, Radius);
centerTransform.Translate(0, -Radius / 2f);
}
public float Radius
{
get { return radius; }
set
{
radius = value;
UpdateGeometry();
}
}
public string DisplayedString
{
get { return displayedString; }
set
{
displayedString = value;
UpdateGeometry();
}
}
public Font Font
{
get { return font; }
set
{
font = value;
UpdateGeometry();
}
}
public uint CharacterSize
{
get { return characterSize; }
set
{
characterSize = value;
UpdateGeometry();
}
}
public Color Color
{
get { return color; }
set
{
color = value;
for (uint i = 0; i < vertices.VertexCount; i++)
{
Vertex v = vertices[i];
v.Color = color;
vertices[i] = v;
}
}
}
public float Spacing
{
get { return spacing; }
set
{
spacing = value;
UpdateGeometry();
}
}
public bool Centered
{
get { return centered; }
set
{
centered = value;
}
}
VertexArray vertices;
float radius;
string displayedString;
Font font;
uint characterSize;
Color color;
float spacing;
bool centered;
Transform centerTransform;
}
It has some bugs (like handling of multiple lines), but meh... for now it's good enough for my purposes.