Browse Source

Added functionality to Text3D. Attributes yet missing.
Added missing MarkNetworkUpdate() calls to NavigationMesh.
Removed unused parameter from Text::UpdateText().

Lasse Öörni 12 years ago
parent
commit
5bd757850f
7 changed files with 452 additions and 16 deletions
  1. 60 0
      Docs/ScriptAPI.dox
  2. 35 0
      Engine/Engine/UIAPI.cpp
  3. 26 0
      Engine/Navigation/NavigationMesh.cpp
  4. 1 1
      Engine/UI/Text.cpp
  5. 1 1
      Engine/UI/Text.h
  6. 266 10
      Engine/UI/Text3D.cpp
  7. 63 4
      Engine/UI/Text3D.h

+ 60 - 0
Docs/ScriptAPI.dox

@@ -4516,6 +4516,66 @@ Properties:<br>
 - int rowHeight (readonly)
 
 
+Text3D
+
+Methods:<br>
+- void SendEvent(const String&, VariantMap& arg1 = VariantMap ( ))
+- bool Load(File@)
+- bool Save(File@)
+- bool LoadXML(const XMLElement&)
+- bool SaveXML(XMLElement&)
+- void ApplyAttributes()
+- bool SetAttribute(const String&, const Variant&)
+- Variant GetAttribute(const String&)
+- void Remove()
+- void MarkNetworkUpdate() const
+- void DrawDebugGeometry(DebugRenderer@, bool)
+- bool SetFont(const String&, int)
+- bool SetFont(Font@, int)
+- void SetAlignment(HorizontalAlignment, VerticalAlignment)
+
+Properties:<br>
+- ShortStringHash type (readonly)
+- String typeName (readonly)
+- int refs (readonly)
+- int weakRefs (readonly)
+- uint numAttributes (readonly)
+- Variant[] attributes
+- AttributeInfo[] attributeInfos (readonly)
+- bool enabled
+- bool enabledEffective (readonly)
+- uint id (readonly)
+- Node@ node (readonly)
+- bool inView (readonly)
+- bool castShadows
+- bool occluder
+- bool occludee
+- float drawDistance
+- float shadowDistance
+- float lodBias
+- uint viewMask
+- uint lightMask
+- uint shadowMask
+- uint zoneMask
+- uint maxLights
+- BoundingBox worldBoundingBox (readonly)
+- Font@ font (readonly)
+- int fontSize (readonly)
+- String text
+- HorizontalAlignment textAlignment
+- HorizontalAlignment horizontalAlignment
+- VerticalAlignment verticalAlignment
+- float rowSpacing
+- bool wordwrap
+- int maxWidth
+- Color color (writeonly)
+- Color[] colors
+- float opacity
+- bool faceCamera
+- uint numRows (readonly)
+- int rowHeight (readonly)
+
+
 LineEdit
 
 Methods:<br>

+ 35 - 0
Engine/Engine/UIAPI.cpp

@@ -33,6 +33,7 @@
 #include "Slider.h"
 #include "Sprite.h"
 #include "Text.h"
+#include "Text3D.h"
 #include "UI.h"
 #include "Window.h"
 
@@ -338,6 +339,39 @@ static void RegisterText(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Text", "int get_rowHeight() const", asMETHOD(Text, GetRowHeight), asCALL_THISCALL);
 }
 
+static void RegisterText3D(asIScriptEngine* engine)
+{
+    RegisterDrawable<Text3D>(engine, "Text3D");
+    engine->RegisterObjectMethod("Text3D", "bool SetFont(const String&in, int)", asMETHODPR(Text3D, SetFont, (const String&, int), bool), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "bool SetFont(Font@+, int)", asMETHODPR(Text3D, SetFont, (Font*, int), bool), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void SetAlignment(HorizontalAlignment, VerticalAlignment)", asMETHOD(Text3D, SetAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "Font@+ get_font() const", asMETHOD(Text3D, GetFont), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "int get_fontSize() const", asMETHOD(Text3D, GetFontSize), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_text(const String&in)", asMETHOD(Text3D, SetText), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "const String& get_text() const", asMETHOD(Text3D, GetText), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_textAlignment(HorizontalAlignment)", asMETHOD(Text3D, SetTextAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "HorizontalAlignment get_textAlignment() const", asMETHOD(Text3D, GetTextAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_horizontalAlignment(HorizontalAlignment)", asMETHOD(Text3D, SetHorizontalAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "HorizontalAlignment get_horizontalAlignment() const", asMETHOD(Text3D, GetHorizontalAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_verticalAlignment(VerticalAlignment)", asMETHOD(Text3D, SetVerticalAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "VerticalAlignment get_verticalAlignment() const", asMETHOD(Text3D, GetVerticalAlignment), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_rowSpacing(float)", asMETHOD(Text3D, SetRowSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "float get_rowSpacing() const", asMETHOD(Text3D, GetRowSpacing), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_wordwrap(bool)", asMETHOD(Text3D, SetWordwrap), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "bool get_wordwrap() const", asMETHOD(Text3D, GetWordwrap), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_maxWidth(int)", asMETHOD(Text3D, SetMaxWidth), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "int get_maxWidth() const", asMETHOD(Text3D, GetMaxWidth), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_color(const Color&in)", asMETHODPR(Text3D, SetColor, (const Color&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_colors(Corner, const Color&in)", asMETHODPR(Text3D, SetColor, (Corner, const Color&), void), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "const Color& get_colors(Corner) const", asMETHOD(Text3D, GetColor), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_opacity(float)", asMETHOD(Text3D, SetOpacity), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "float get_opacity() const", asMETHOD(Text3D, GetOpacity), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_faceCamera(bool)", asMETHOD(Text3D, SetFaceCamera), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "bool get_faceCamera() const", asMETHOD(Text3D, GetFaceCamera), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "uint get_numRows() const", asMETHOD(Text3D, GetNumRows), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "int get_rowHeight() const", asMETHOD(Text3D, GetRowHeight), asCALL_THISCALL);
+}
+
 static void RegisterLineEdit(asIScriptEngine* engine)
 {
     RegisterBorderImage<LineEdit>(engine, "LineEdit");
@@ -567,6 +601,7 @@ void RegisterUIAPI(asIScriptEngine* engine)
     RegisterScrollView(engine);
     RegisterListView(engine);
     RegisterText(engine);
+    RegisterText3D(engine);
     RegisterLineEdit(engine);
     RegisterMenu(engine);
     RegisterDropDownList(engine);

+ 26 - 0
Engine/Navigation/NavigationMesh.cpp

@@ -223,66 +223,92 @@ void NavigationMesh::DrawDebugGeometry(DebugRenderer* debug, bool depthTest)
 void NavigationMesh::SetTileSize(int size)
 {
     tileSize_ = Max(size, 16);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetCellSize(float size)
 {
     cellSize_ = Max(size, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetCellHeight(float height)
 {
     cellHeight_ = Max(height, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetAgentHeight(float height)
 {
     agentHeight_ = Max(height, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetAgentRadius(float radius)
 {
     agentRadius_ = Max(radius, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetAgentMaxClimb(float maxClimb)
 {
     agentMaxClimb_ = Max(maxClimb, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetAgentMaxSlope(float maxSlope)
 {
     agentMaxSlope_ = Max(maxSlope, 0.0f);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetRegionMinSize(float size)
 {
     regionMinSize_ = Max(size, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetRegionMergeSize(float size)
 {
     regionMergeSize_ = Max(size, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetEdgeMaxLength(float length)
 {
     edgeMaxLength_ = Max(length, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetEdgeMaxError(float error)
 {
     edgeMaxError_ = Max(error, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetDetailSampleDistance(float distance)
 {
     detailSampleDistance_ = Max(distance, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 void NavigationMesh::SetDetailSampleMaxError(float error)
 {
     detailSampleMaxError_ = Max(error, M_EPSILON);
+    
+    MarkNetworkUpdate();
 }
 
 bool NavigationMesh::Build()

+ 1 - 1
Engine/UI/Text.cpp

@@ -353,7 +353,7 @@ ResourceRef Text::GetFontAttr() const
     return GetResourceRef(font_, Font::GetTypeStatic());
 }
 
-void Text::UpdateText(bool inResize)
+void Text::UpdateText()
 {
     int width = 0;
     int height = 0;

+ 1 - 1
Engine/UI/Text.h

@@ -110,7 +110,7 @@ public:
     
 protected:
     /// Update text when text, font or spacing changed.
-    void UpdateText(bool inResize = false);
+    void UpdateText();
     /// Validate text selection to be within the text.
     void ValidateSelection();
     /// Return row start X position.

+ 266 - 10
Engine/UI/Text3D.cpp

@@ -21,21 +21,31 @@
 //
 
 #include "Precompiled.h"
+#include "Camera.h"
 #include "Context.h"
+#include "Geometry.h"
 #include "Material.h"
+#include "Node.h"
+#include "Technique.h"
 #include "Text.h"
 #include "Text3D.h"
+#include "VertexBuffer.h"
 
 namespace Urho3D
 {
 
+static const float TEXT_SCALING = 1.0f / 128.0f;
+
 OBJECTTYPESTATIC(Text3D);
 
 Text3D::Text3D(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY),
-    text_(new Text(context))
+    text_(new Text(context)),
+    vertexBuffer_(new VertexBuffer(context_)),
+    faceCamera_(false),
+    textDirty_(true),
+    geometryDirty_(true)
 {
-    batches_.Resize(1);
 }
 
 Text3D::~Text3D()
@@ -47,39 +57,159 @@ void Text3D::RegisterObject(Context* context)
     context->RegisterFactory<Text3D>();
 }
 
+void Text3D::UpdateBatches(const FrameInfo& frame)
+{
+    const Matrix3x4& worldTransform = node_->GetWorldTransform();
+    distance_ = frame.camera_->GetDistance(GetWorldBoundingBox().Center());
+    
+    if (faceCamera_)
+    {
+        customWorldTransform_ = Matrix3x4(node_->GetWorldPosition(), frame.camera_->GetNode()->GetWorldRotation(), node_->GetWorldScale());
+        worldBoundingBoxDirty_ = true;
+    }
+    
+    for (unsigned i = 0; i < batches_.Size(); ++i)
+    {
+        batches_[i].distance_ = distance_;
+        batches_[i].worldTransform_ = faceCamera_ ? &customWorldTransform_ : &worldTransform;
+    }
+}
+
+void Text3D::UpdateGeometry(const FrameInfo& frame)
+{
+    for (unsigned i = 0; i < batches_.Size(); ++i)
+    {
+        Geometry* geometry = geometries_[i];
+        geometry->SetDrawRange(TRIANGLE_LIST, 0, 0, uiBatches_[i].vertexStart_, uiBatches_[i].vertexEnd_ - uiBatches_[i].vertexStart_);
+    }
+    
+    if (uiVertexData_.Size())
+    {
+        unsigned vertexCount = uiVertexData_.Size() / UI_VERTEX_SIZE;
+        if (vertexBuffer_->GetVertexCount() != vertexCount)
+            vertexBuffer_->SetSize(vertexCount, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1);
+        
+        vertexBuffer_->SetData(&uiVertexData_[0]);
+    }
+    
+    geometryDirty_ = false;
+}
+
+UpdateGeometryType Text3D::GetUpdateGeometryType()
+{
+    if (geometryDirty_ || vertexBuffer_->IsDataLost())
+        return UPDATE_MAIN_THREAD;
+    else
+        return UPDATE_NONE;
+}
+
 bool Text3D::SetFont(const String& fontName, int size)
 {
-    return text_->SetFont(fontName, size);
+    bool success = text_->SetFont(fontName, size);
+    
+    // Changing font requires materials to be re-evaluated. Material evaluation can not be done in worker threads,
+    // so UI batches must be brought up-to-date immediately
+    UpdateTextBatches();
+    UpdateTextMaterials();
+    MarkNetworkUpdate();
+    
+    return success;
 }
 
 bool Text3D::SetFont(Font* font, int size)
 {
-    return text_->SetFont(font, size);
+    bool success = text_->SetFont(font, size);
+    
+    UpdateTextBatches();
+    UpdateTextMaterials();
+    MarkNetworkUpdate();
+    
+    return success;
 }
 
 void Text3D::SetText(const String& text)
 {
     text_->SetText(text);
+    
+    // Changing text requires materials to be re-evaluated, in case the font is multi-page
+    UpdateTextBatches();
+    UpdateTextMaterials();
+    MarkNetworkUpdate();
+}
+
+void Text3D::SetAlignment(HorizontalAlignment hAlign, VerticalAlignment vAlign)
+{
+    text_->SetAlignment(hAlign, vAlign);
+    
+    MarkTextDirty();
+}
+
+void Text3D::SetHorizontalAlignment(HorizontalAlignment align)
+{
+    text_->SetHorizontalAlignment(align);
+    
+    MarkTextDirty();
+}
+
+void Text3D::SetVerticalAlignment(VerticalAlignment align)
+{
+    text_->SetVerticalAlignment(align);
+    
+    MarkTextDirty();
 }
 
 void Text3D::SetTextAlignment(HorizontalAlignment align)
 {
     text_->SetTextAlignment(align);
+    
+    MarkTextDirty();
 }
 
 void Text3D::SetRowSpacing(float spacing)
 {
     text_->SetRowSpacing(spacing);
+    
+    MarkTextDirty();
 }
 
-void Text3D::SetMaterial(Material* material)
+void Text3D::SetWordwrap(bool enable)
 {
-    batches_[0].material_ = material;
+    text_->SetWordwrap(enable);
+    
+    MarkTextDirty();
 }
 
-void Text3D::SetWordwrap(bool enable)
+void Text3D::SetMaxWidth(int width)
 {
-    text_->SetWordwrap(enable);
+    text_->SetMaxWidth(width);
+    
+    MarkTextDirty();
+}
+
+void Text3D::SetColor(const Color& color)
+{
+    text_->SetColor(color);
+    
+    MarkTextDirty();
+}
+
+void Text3D::SetColor(Corner corner, const Color& color)
+{
+    text_->SetColor(corner, color);
+    
+    MarkTextDirty();
+}
+
+void Text3D::SetOpacity(float opacity)
+{
+    text_->SetOpacity(opacity);
+    
+    MarkTextDirty();
+}
+
+void Text3D::SetFaceCamera(bool enable)
+{
+    faceCamera_ = enable;
 }
 
 Font* Text3D::GetFont() const
@@ -97,6 +227,16 @@ const String& Text3D::GetText() const
     return text_->GetText();
 }
 
+HorizontalAlignment Text3D::GetHorizontalAlignment() const
+{
+    return text_->GetHorizontalAlignment();
+}
+
+VerticalAlignment Text3D::GetVerticalAlignment() const
+{
+    return text_->GetVerticalAlignment();
+}
+
 HorizontalAlignment Text3D::GetTextAlignment() const
 {
     return text_->GetTextAlignment();
@@ -112,6 +252,11 @@ bool Text3D::GetWordwrap() const
     return text_->GetWordwrap();
 }
 
+int Text3D::GetMaxWidth() const
+{
+    return text_->GetMaxWidth();
+}
+
 int Text3D::GetRowHeight() const
 {
     return text_->GetRowHeight();
@@ -127,13 +272,124 @@ const PODVector<int>& Text3D::GetRowWidths() const
     return text_->GetRowWidths();
 }
 
-Material* Text3D::GetMaterial() const
+const Color& Text3D::GetColor(Corner corner) const
+{
+    return text_->GetColor(corner);
+}
+
+float Text3D::GetOpacity() const
+{
+    return text_->GetOpacity();
+}
+
+void Text3D::OnNodeSet(Node* node)
 {
-    return batches_[0].material_;
+    Drawable::OnNodeSet(node);
+    
+    if (node)
+        customWorldTransform_ = node->GetWorldTransform();
 }
 
 void Text3D::OnWorldBoundingBoxUpdate()
 {
+    if (textDirty_)
+        UpdateTextBatches();
+    
+    worldBoundingBox_ = faceCamera_ ? boundingBox_.Transformed(customWorldTransform_) :
+        boundingBox_.Transformed(node_->GetWorldTransform());
+}
+
+void Text3D::MarkTextDirty()
+{
+    textDirty_ = true;
+    MarkNetworkUpdate();
 }
 
+void Text3D::UpdateTextBatches()
+{
+    uiBatches_.Clear();
+    uiVertexData_.Clear();
+    
+    text_->GetBatches(uiBatches_, uiVertexData_, IntRect::ZERO);
+    
+    Vector3 offset(Vector3::ZERO);
+    
+    switch (text_->GetHorizontalAlignment())
+    {
+    case HA_LEFT:
+        break;
+        
+    case HA_CENTER:
+        offset.x_ -= (float)text_->GetWidth() * 0.5f;
+        break;
+        
+    case HA_RIGHT:
+        offset.x_ -= (float)text_->GetWidth();
+        break;
+    }
+    
+    switch (text_->GetVerticalAlignment())
+    {
+    case VA_TOP:
+        break;
+        
+    case VA_CENTER:
+        offset.y_ -= (float)text_->GetHeight() * 0.5f;
+        break;
+        
+    case VA_BOTTOM:
+        offset.y_ -= (float)text_->GetHeight();
+        break;
+    }
+    
+    boundingBox_.defined_ = false;
+    boundingBox_.min_ = boundingBox_.max_ = Vector3::ZERO;
+    
+    for (unsigned i = 0; i < uiVertexData_.Size(); i += UI_VERTEX_SIZE)
+    {
+        Vector3& position = *(reinterpret_cast<Vector3*>(&uiVertexData_[i]));
+        position += offset;
+        position *= TEXT_SCALING;
+        position.y_ = -position.y_;
+        boundingBox_.Merge(position);
+    }
+    
+    textDirty_ = false;
+    geometryDirty_ = true;
+}
+
+void Text3D::UpdateTextMaterials()
+{
+    batches_.Resize(uiBatches_.Size());
+    geometries_.Resize(uiBatches_.Size());
+    
+    for (unsigned i = 0; i < batches_.Size(); ++i)
+    {
+        if (!geometries_[i])
+        {
+            Geometry* geometry = new Geometry(context_);
+            geometry->SetVertexBuffer(0, vertexBuffer_, MASK_POSITION | MASK_COLOR | MASK_TEXCOORD1);
+            batches_[i].geometry_ = geometries_[i] = geometry;
+        }
+        
+        if (!batches_[i].material_)
+        {
+            Material* material = new Material(context_);
+            Technique* tech = new Technique(context_);
+            Pass* pass = tech->CreatePass(PASS_ALPHA);
+            pass->SetVertexShader("Basic_DiffVCol");
+            pass->SetPixelShader("Basic_AlphaVCol");
+            material->SetTechnique(0, tech);
+            material->SetCullMode(CULL_NONE);
+            batches_[i].material_ = material;
+        }
+        
+        Material* material = batches_[i].material_;
+        material->SetTexture(TU_DIFFUSE, uiBatches_[i].texture_);
+        Pass* pass = material->GetTechnique(0)->GetPass(PASS_ALPHA);
+        pass->SetBlendMode(uiBatches_[i].blendMode_);
+    }
+}
+
+
 }

+ 63 - 4
Engine/UI/Text3D.h

@@ -23,6 +23,7 @@
 #pragma once
 
 #include "Drawable.h"
+#include "UIBatch.h"
 
 namespace Urho3D
 {
@@ -42,20 +43,41 @@ public:
     /// Register object factory. Drawable must be registered first.
     static void RegisterObject(Context* context);
     
+    /// Calculate distance and prepare batches for rendering. May be called from worker thread(s), possibly re-entrantly.
+    virtual void UpdateBatches(const FrameInfo& frame);
+    /// Prepare geometry for rendering. Called from a worker thread if possible (no GPU update.)
+    virtual void UpdateGeometry(const FrameInfo& frame);
+    /// Return whether a geometry update is necessary, and if it can happen in a worker thread.
+    virtual UpdateGeometryType GetUpdateGeometryType();
+    
     /// Set font and font size.
     bool SetFont(const String& fontName, int size = DEFAULT_FONT_SIZE);
     /// Set font and font size.
     bool SetFont(Font* font, int size = DEFAULT_FONT_SIZE);
     /// Set text. Text is assumed to be either ASCII or UTF8-encoded.
     void SetText(const String& text);
+    /// Set horizontal and vertical alignment.
+    void SetAlignment(HorizontalAlignment hAlign, VerticalAlignment vAlign);
+    /// Set horizontal alignment.
+    void SetHorizontalAlignment(HorizontalAlignment align);
+    /// Set vertical alignment.
+    void SetVerticalAlignment(VerticalAlignment align);
     /// Set row alignment.
     void SetTextAlignment(HorizontalAlignment align);
     /// Set row spacing, 1.0 for original font spacing.
     void SetRowSpacing(float spacing);
     /// Set wordwrap. In wordwrap mode the text element will respect its current width. Otherwise it resizes itself freely.
     void SetWordwrap(bool enable);
-    /// Set material. To render fonts correctly, the material's pixel shader needs to read diffuse texture alpha as opacity.
-    void SetMaterial(Material* material);
+    /// Set maximum width for wordwrap mode.
+    void SetMaxWidth(int width);
+    /// Set color on all corners.
+    void SetColor(const Color& color);
+    /// Set color on one corner.
+    void SetColor(Corner corner, const Color& color);
+    /// Set opacity.
+    void SetOpacity(float opacity);
+    /// Set whether to face camera automatically.
+    void SetFaceCamera(bool enable);
     
     /// Return font.
     Font* GetFont() const;
@@ -65,18 +87,28 @@ public:
     const String& GetText() const;
     /// Return row alignment.
     HorizontalAlignment GetTextAlignment() const;
+    /// Return horizontal alignment.
+    HorizontalAlignment GetHorizontalAlignment() const;
+    /// Return vertical alignment.
+    VerticalAlignment GetVerticalAlignment() const;
     /// Return row spacing.
     float GetRowSpacing() const;
     /// Return wordwrap mode.
     bool GetWordwrap() const;
+    /// Return maximum width.
+    int GetMaxWidth() const;
     /// Return row height.
     int GetRowHeight() const;
     /// Return number of rows.
     unsigned GetNumRows() const;
     /// Return width of each row.
     const PODVector<int>& GetRowWidths() const;
-    /// Return material.
-    Material* GetMaterial() const;
+    /// Return corner color.
+    const Color& GetColor(Corner corner) const;
+    /// Return opacity.
+    float GetOpacity() const;
+    /// Return whether faces camera automatically.
+    bool GetFaceCamera() const { return faceCamera_; }
     
     /// Set font attribute.
     void SetFontAttr(ResourceRef value);
@@ -84,12 +116,39 @@ public:
     ResourceRef GetFontAttr() const;
     
 protected:
+    /// Handle node being assigned.
+    virtual void OnNodeSet(Node* node);
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
     
 private:
+    /// Mark text & geometry dirty.
+    void MarkTextDirty();
+    /// Update text UI batches.
+    void UpdateTextBatches();
+    /// Create materials for text rendering. May only be called from the main thread. Text UI batches must be up-to-date.
+    void UpdateTextMaterials();
+    
     /// Internally used text element.
     SharedPtr<Text> text_;
+    /// Geometries.
+    Vector<SharedPtr<Geometry> > geometries_;
+    /// Vertex buffer.
+    SharedPtr<VertexBuffer> vertexBuffer_;
+    /// Text UI batches.
+    PODVector<UIBatch> uiBatches_;
+    /// Text vertex data.
+    PODVector<float> uiVertexData_;
+    /// Local-space bounding box.
+    BoundingBox boundingBox_;
+    /// Custom world transform.
+    Matrix3x4 customWorldTransform_;
+    /// Face camera flag.
+    bool faceCamera_;
+    /// Text needs update flag.
+    bool textDirty_;
+    /// Geometry dirty flag.
+    bool geometryDirty_;
 };
 
 }