|
@@ -21,21 +21,31 @@
|
|
|
//
|
|
//
|
|
|
|
|
|
|
|
#include "Precompiled.h"
|
|
#include "Precompiled.h"
|
|
|
|
|
+#include "Camera.h"
|
|
|
#include "Context.h"
|
|
#include "Context.h"
|
|
|
|
|
+#include "Geometry.h"
|
|
|
#include "Material.h"
|
|
#include "Material.h"
|
|
|
|
|
+#include "Node.h"
|
|
|
|
|
+#include "Technique.h"
|
|
|
#include "Text.h"
|
|
#include "Text.h"
|
|
|
#include "Text3D.h"
|
|
#include "Text3D.h"
|
|
|
|
|
+#include "VertexBuffer.h"
|
|
|
|
|
|
|
|
namespace Urho3D
|
|
namespace Urho3D
|
|
|
{
|
|
{
|
|
|
|
|
|
|
|
|
|
+static const float TEXT_SCALING = 1.0f / 128.0f;
|
|
|
|
|
+
|
|
|
OBJECTTYPESTATIC(Text3D);
|
|
OBJECTTYPESTATIC(Text3D);
|
|
|
|
|
|
|
|
Text3D::Text3D(Context* context) :
|
|
Text3D::Text3D(Context* context) :
|
|
|
Drawable(context, DRAWABLE_GEOMETRY),
|
|
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()
|
|
Text3D::~Text3D()
|
|
@@ -47,39 +57,159 @@ void Text3D::RegisterObject(Context* context)
|
|
|
context->RegisterFactory<Text3D>();
|
|
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)
|
|
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)
|
|
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)
|
|
void Text3D::SetText(const String& text)
|
|
|
{
|
|
{
|
|
|
text_->SetText(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)
|
|
void Text3D::SetTextAlignment(HorizontalAlignment align)
|
|
|
{
|
|
{
|
|
|
text_->SetTextAlignment(align);
|
|
text_->SetTextAlignment(align);
|
|
|
|
|
+
|
|
|
|
|
+ MarkTextDirty();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Text3D::SetRowSpacing(float spacing)
|
|
void Text3D::SetRowSpacing(float spacing)
|
|
|
{
|
|
{
|
|
|
text_->SetRowSpacing(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
|
|
Font* Text3D::GetFont() const
|
|
@@ -97,6 +227,16 @@ const String& Text3D::GetText() const
|
|
|
return text_->GetText();
|
|
return text_->GetText();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+HorizontalAlignment Text3D::GetHorizontalAlignment() const
|
|
|
|
|
+{
|
|
|
|
|
+ return text_->GetHorizontalAlignment();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+VerticalAlignment Text3D::GetVerticalAlignment() const
|
|
|
|
|
+{
|
|
|
|
|
+ return text_->GetVerticalAlignment();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
HorizontalAlignment Text3D::GetTextAlignment() const
|
|
HorizontalAlignment Text3D::GetTextAlignment() const
|
|
|
{
|
|
{
|
|
|
return text_->GetTextAlignment();
|
|
return text_->GetTextAlignment();
|
|
@@ -112,6 +252,11 @@ bool Text3D::GetWordwrap() const
|
|
|
return text_->GetWordwrap();
|
|
return text_->GetWordwrap();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+int Text3D::GetMaxWidth() const
|
|
|
|
|
+{
|
|
|
|
|
+ return text_->GetMaxWidth();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
int Text3D::GetRowHeight() const
|
|
int Text3D::GetRowHeight() const
|
|
|
{
|
|
{
|
|
|
return text_->GetRowHeight();
|
|
return text_->GetRowHeight();
|
|
@@ -127,13 +272,124 @@ const PODVector<int>& Text3D::GetRowWidths() const
|
|
|
return text_->GetRowWidths();
|
|
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()
|
|
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_);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
}
|
|
}
|