Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F123379
Text.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
Text.cpp
View Options
#include
<echo/Graphics/Text.h>
#include
<echo/Graphics/Font.h>
#include
<echo/Graphics/Mesh.h>
#include
<echo/Maths/Vector2.h>
#include
<echo/Graphics/SubMesh.h>
#include
<echo/Resource/MeshManager.h>
#include
<echo/UTF8String.h>
namespace
Echo
{
Text
::
Text
(
const
UTF8String
&
content
,
shared_ptr
<
Font
>
font
)
:
mFont
(
font
),
mString
(
content
),
mSpaceWidth
(
0.5f
),
mTextScale
(
1.f
),
mMaxWidth
(
100.f
),
//Just an arbitrary size.'
mFixedLineSpacing
(
1.f
),
mUseFixedLineSpacing
(
false
),
mUseMaxWidth
(
false
),
mMeshOutOfDate
(
true
),
mUseReplacementCharacterForGlyphFailure
(
true
),
mColour
()
{
SetMesh
(
make_shared
<
Mesh
>
());
GetMesh
()
->
CreateSubMesh
();
}
Text
::
Text
()
:
mFont
(),
mString
(),
mSpaceWidth
(
0.5f
),
mTextScale
(
1.f
),
mMaxWidth
(
100.f
),
//Just an arbitrary size.'
mFixedLineSpacing
(
1.f
),
mUseFixedLineSpacing
(
false
),
mUseMaxWidth
(
false
),
mMeshOutOfDate
(
true
),
mUseReplacementCharacterForGlyphFailure
(
true
),
mColour
()
{
SetMesh
(
make_shared
<
Mesh
>
());
GetMesh
()
->
CreateSubMesh
();
}
Text
::
Text
(
MeshManager
&
meshManager
,
const
std
::
string
resourceName
)
:
mFont
(),
mString
(),
mSpaceWidth
(
0.5f
),
mTextScale
(
1.f
),
mMaxWidth
(
100.f
),
//Just an arbitrary size.'
mFixedLineSpacing
(
1.f
),
mUseFixedLineSpacing
(
false
),
mUseMaxWidth
(
false
),
mMeshOutOfDate
(
true
),
mUseReplacementCharacterForGlyphFailure
(
true
),
mColour
()
{
SetMesh
(
meshManager
.
CreateMesh
(
resourceName
));
GetMesh
()
->
CreateSubMesh
();
}
Text
::~
Text
()
{
}
void
Text
::
SetFont
(
shared_ptr
<
Font
>
font
)
{
mFont
=
font
;
mMeshOutOfDate
=
true
;
NeedUpdate
();
}
void
Text
::
Set
(
const
UTF8String
&
content
)
{
if
(
mString
!=
content
)
{
mString
=
content
;
mMeshOutOfDate
=
true
;
NeedUpdate
(
true
);
}
}
void
Text
::
Append
(
const
UTF8String
&
content
)
{
mString
+=
content
;
mMeshOutOfDate
=
true
;
NeedUpdate
(
true
);
}
void
Text
::
BuildMesh
(
const
UTF8String
&
utf8String
)
{
if
(
!
mFont
||
!
GetMesh
())
{
return
;
}
shared_ptr
<
SubMesh
>
subMesh
=
GetMesh
()
->
GetSubMesh
(
0
);
if
(
!
subMesh
)
{
return
;
}
subMesh
->
Clear
();
UTF8String
::
iterator
it
=
utf8String
.
begin
();
UTF8String
::
iterator
itEnd
=
utf8String
.
end
();
Glyph
*
previousGlyph
=
0
;
Vector2
currentPosition
(
0
,
0
);
f32
currentWidth
=
0
;
//Current height is the cursor y position.
f32
currentWordWidth
=
0
;
f32
currentWordHeight
=
0
;
f32
currentWordYBearing
=
0
;
f32
currentLineHeight
=
0
;
f32
currentLineMaxYBearing
=
0
;
f32
lineHeight
;
f32
spaceWidth
=
mSpaceWidth
*
mFont
->
GetMaxWidth
();
if
(
mUseFixedLineSpacing
)
{
lineHeight
=
mFixedLineSpacing
*
mFont
->
GetMaxHeight
();
currentLineHeight
=
mFixedLineSpacing
*
mFont
->
GetMaxHeight
();
}
else
{
lineHeight
=
mFont
->
GetMaxHeight
();
}
// Determine the length of the current word.
// If the current width plus word width is less than the specified object width
// Add the word.
// Else
// Add new line
// Add the word.
UTF8String
currentWord
;
bool
lastCharacter
=
false
;
while
(
!
lastCharacter
)
{
UTF32Code
code
;
if
(
it
==
itEnd
)
{
lastCharacter
=
true
;
code
=
0
;
}
else
{
code
=
*
it
;
it
++
;
}
Glyph
*
glyph
=
mFont
->
GetGlyph
(
code
,
mUseReplacementCharacterForGlyphFailure
);
const
UTF32Code
UTF8_SPACE
=
32
;
if
(
!
glyph
||
lastCharacter
||
code
==
UTF8_SPACE
)
{
//When dealing with the last character in a word we want to adjust the width so that it
//doesn't include advance space of the last character.
if
(
previousGlyph
)
{
currentWordWidth
-=
previousGlyph
->
mAdvanceX
;
currentWordWidth
+=
previousGlyph
->
mWidth
;
}
//We don't apply the scale while we're calculating the word width because we may need to adjust it if it exceeds max width.
f32
wordScale
=
mTextScale
;
f32
scaledWordWidth
=
currentWordWidth
*
wordScale
;
f32
scaledWordHeight
=
currentWordHeight
*
wordScale
;
f32
scaledWordYBearing
=
currentWordYBearing
*
wordScale
;
//End of word as far as we are concerned.
if
(
mUseMaxWidth
&&
(
currentWidth
+
scaledWordWidth
)
>
mMaxWidth
)
{
//If the word is larger than the width we need to scale it so it will fit.
if
(
scaledWordWidth
>
mMaxWidth
)
{
//Update the scaled values.
wordScale
=
mMaxWidth
/
currentWordWidth
;
//Find the new scale, use the unscaled word with.
scaledWordWidth
=
currentWordWidth
*
wordScale
;
scaledWordHeight
=
currentWordHeight
*
wordScale
;
scaledWordYBearing
=
currentWordYBearing
*
wordScale
;
if
(
mUseFixedLineSpacing
)
{
currentPosition
.
y
-=
lineHeight
;
}
else
{
//Calculate the next line position. This takes into account the max y bearing of both
//lines (the single word line and the one preceeding it).
currentPosition
.
y
-=
(
currentLineHeight
-
currentLineMaxYBearing
)
+
scaledWordYBearing
;
currentLineHeight
=
lineHeight
*
wordScale
;
}
currentLineMaxYBearing
=
0
;
currentPosition
.
x
=
0
;
currentWidth
=
0
;
}
else
{
//Create a new line if the current width is not 0.
if
(
currentWidth
>
0
)
{
currentPosition
.
x
=
0
;
currentWidth
=
0
;
if
(
mUseFixedLineSpacing
)
{
currentPosition
.
y
-=
lineHeight
*
wordScale
;
}
else
{
//This is a best guess as to what the next line position will be based on the next word.
//Unless we scan the whole line we can't know what the next line max Y bearing is.
currentPosition
.
y
-=
(
currentLineHeight
-
currentLineMaxYBearing
)
+
scaledWordYBearing
;
currentLineHeight
=
0
;
}
}
}
}
if
(
scaledWordHeight
>
currentLineHeight
)
{
currentLineHeight
=
scaledWordHeight
;
}
if
(
scaledWordYBearing
>
currentLineMaxYBearing
)
{
currentLineMaxYBearing
=
scaledWordYBearing
;
}
if
(
currentWord
.
Length
()
>
0
)
{
AddWordToMesh
(
currentPosition
,
currentWord
,
wordScale
);
}
//Move the cursor over
currentPosition
.
x
+=
scaledWordWidth
;
currentPosition
.
x
+=
spaceWidth
*
wordScale
;
currentWidth
+=
scaledWordWidth
+
spaceWidth
*
wordScale
;
currentWordWidth
=
0
;
currentWordYBearing
=
0
;
currentWordHeight
=
0
;
currentWord
.
clear
();
previousGlyph
=
0
;
continue
;
}
else
{
currentWord
+=
code
;
currentWordWidth
+=
glyph
->
mAdvanceX
;
if
(
previousGlyph
&&
code
)
{
s32
kerning
=
previousGlyph
->
GetKerning
(
code
);
currentWordWidth
+=
kerning
;
}
if
(
glyph
->
mHeight
>
currentWordHeight
)
{
currentWordHeight
=
glyph
->
mHeight
;
}
if
(
glyph
->
mYBearing
>
currentWordYBearing
)
{
currentWordYBearing
=
glyph
->
mYBearing
;
}
previousGlyph
=
glyph
;
}
}
//Now centre the mesh
shared_ptr
<
Mesh
>
mesh
=
GetMesh
();
mesh
->
CentreMeshToOrigin
();
mDimensions
=
mesh
->
GetMax
()
-
mesh
->
GetMin
();
mesh
->
SetMaterial
(
mFont
->
GetMaterial
()
->
Clone
());
SetColour
(
mColour
);
mMeshOutOfDate
=
false
;
}
void
Text
::
AddWordToMesh
(
Vector2
position
,
const
UTF8String
&
word
,
f32
scale
)
{
f32
spaceWidth
=
mSpaceWidth
*
mFont
->
GetMaxWidth
();
UTF8String
::
iterator
it
=
word
.
begin
();
UTF8String
::
iterator
itEnd
=
word
.
end
();
Glyph
*
previousGlyph
=
0
;
while
(
it
!=
itEnd
)
{
UTF32Code
code
=
*
it
;
Glyph
*
glyph
=
mFont
->
GetGlyph
(
code
,
mUseReplacementCharacterForGlyphFailure
);
if
(
!
glyph
)
{
position
.
x
+=
spaceWidth
*
scale
;
it
++
;
previousGlyph
=
0
;
continue
;
}
if
(
previousGlyph
&&
code
)
{
f32
kerning
=
static_cast
<
f32
>
(
previousGlyph
->
GetKerning
(
code
));
position
.
x
+=
kerning
*
scale
;
}
AddCharacterToMesh
(
position
,
*
glyph
,
scale
);
position
.
x
+=
static_cast
<
f32
>
(
glyph
->
mAdvanceX
)
*
scale
;
previousGlyph
=
glyph
;
it
++
;
}
}
void
Text
::
AddCharacterToMesh
(
const
Vector2
&
position
,
const
Glyph
&
glyph
,
f32
scale
)
{
shared_ptr
<
SubMesh
>
subMesh
=
GetMesh
()
->
GetSubMesh
(
0
);
if
(
!
subMesh
)
{
return
;
}
//Vertices
f32
left
=
position
.
x
+
static_cast
<
f32
>
(
glyph
.
mXBearing
)
*
scale
;
f32
right
=
left
+
static_cast
<
f32
>
(
glyph
.
mWidth
)
*
scale
;
f32
top
=
position
.
y
+
static_cast
<
f32
>
(
glyph
.
mYBearing
)
*
scale
;
f32
bottom
=
top
-
static_cast
<
f32
>
(
glyph
.
mHeight
)
*
scale
;
shared_ptr
<
std
::
vector
<
Vector3
>
>
vertices
=
subMesh
->
GetVertices
();
size_t
indexBase
=
vertices
?
vertices
->
size
()
:
0
;
subMesh
->
AddVertex
(
Vector3
(
left
,
top
,
0
));
subMesh
->
AddVertex
(
Vector3
(
right
,
top
,
0
));
subMesh
->
AddVertex
(
Vector3
(
left
,
bottom
,
0
));
subMesh
->
AddVertex
(
Vector3
(
right
,
bottom
,
0
));
//Texture coordinates
TextureUV
uv
(
glyph
.
mTextureCoordinates
.
first
);
TextureUV
st
(
glyph
.
mTextureCoordinates
.
second
);
subMesh
->
AddTextureUV
(
uv
);
subMesh
->
AddTextureUV
(
TextureUV
(
st
.
u
,
uv
.
v
));
subMesh
->
AddTextureUV
(
TextureUV
(
uv
.
u
,
st
.
v
));
subMesh
->
AddTextureUV
(
st
);
//Triangles
subMesh
->
AddFace
(
IndexedTriangle
(
indexBase
,
indexBase
+
1
,
indexBase
+
2
));
subMesh
->
AddFace
(
IndexedTriangle
(
indexBase
+
1
,
indexBase
+
3
,
indexBase
+
2
));
}
AxisAlignedBox
Text
::
GetAxisAlignedBox
(
bool
applyLocalTransform
)
{
if
(
mMeshOutOfDate
)
{
UpdateMesh
();
}
return
SceneEntity
::
GetAxisAlignedBox
(
applyLocalTransform
);
}
const
Vector3
&
Text
::
GetTextDimensions
()
const
{
return
mDimensions
;}
void
Text
::
UpdateMesh
()
{
BuildMesh
(
mString
);
}
void
Text
::
Render
(
const
Matrix4
&
transform
,
RenderTarget
&
renderTarget
)
{
if
(
mMeshOutOfDate
)
{
UpdateMesh
();
}
SceneEntity
::
Render
(
transform
,
renderTarget
);
}
void
Text
::
SetColour
(
const
Colour
&
colour
)
{
mColour
=
colour
;
if
(
GetMesh
())
{
GetMesh
()
->
SetDiffuseColour
(
mColour
);
}
}
void
Text
::
SetMaxWidth
(
f32
maxWidth
)
{
if
(
maxWidth
!=
mMaxWidth
&&
mUseMaxWidth
)
{
mMeshOutOfDate
=
true
;
NeedUpdate
();
}
mMaxWidth
=
maxWidth
;
}
void
Text
::
SetUseMaxWidth
(
bool
useMaxWidth
)
{
if
(
useMaxWidth
!=
mUseMaxWidth
)
{
mMeshOutOfDate
=
true
;
NeedUpdate
();
}
mUseMaxWidth
=
useMaxWidth
;
}
void
Text
::
SetTextScale
(
f32
textScale
)
{
if
(
mTextScale
!=
textScale
)
{
mTextScale
=
textScale
;
mMeshOutOfDate
=
true
;
NeedUpdate
(
true
);
}
}
void
Text
::
SetFixedLineSpacing
(
f32
fixedLineSpacing
)
{
if
(
fixedLineSpacing
!=
mFixedLineSpacing
&&
mUseMaxWidth
)
{
mMeshOutOfDate
=
true
;
NeedUpdate
();
}
mFixedLineSpacing
=
fixedLineSpacing
;
}
void
Text
::
SetUseFixedLineSpacing
(
bool
useFixedLineSpacing
)
{
if
(
useFixedLineSpacing
!=
mUseFixedLineSpacing
&&
mUseMaxWidth
)
{
mMeshOutOfDate
=
true
;
NeedUpdate
(
true
);
}
mUseFixedLineSpacing
=
useFixedLineSpacing
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-c++
Expires
Wed, Jan 15, 8:08 PM (1 h, 53 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
72041
Default Alt Text
Text.cpp (10 KB)
Attached To
Mode
rEE Echo 3
Attached
Detach File
Event Timeline
Log In to Comment