summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source')
-rw-r--r--src/Runtime/Source/engine/Qt3DSRenderRuntimeBinding.cpp7
-rw-r--r--src/Runtime/Source/engine/Qt3DSRenderRuntimeBindingImpl.h5
-rw-r--r--src/Runtime/Source/runtime/Qt3DSApplication.cpp5
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp519
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp75
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h75
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h100
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.cpp1006
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.h172
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSFontDatabase.cpp106
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSFontDatabase_p.h82
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.cpp21
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.h3
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSRenderer.h3
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSTextRenderer.h4
-rw-r--r--src/Runtime/Source/runtimerender/graphobjects/Qt3DSRenderText.cpp7
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp12
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.h45
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp21
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.h3
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp28
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h2
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp79
-rw-r--r--src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h7
24 files changed, 2339 insertions, 48 deletions
diff --git a/src/Runtime/Source/engine/Qt3DSRenderRuntimeBinding.cpp b/src/Runtime/Source/engine/Qt3DSRenderRuntimeBinding.cpp
index 8fa75792..27f326eb 100644
--- a/src/Runtime/Source/engine/Qt3DSRenderRuntimeBinding.cpp
+++ b/src/Runtime/Source/engine/Qt3DSRenderRuntimeBinding.cpp
@@ -894,9 +894,14 @@ struct Qt3DSRenderSceneManager : public Q3DStudio::ISceneManager,
inPresentation.SetScene(&inScene);
if (m_ProjectInitialized == false) {
m_ProjectInitialized = true;
- if (m_Context->m_Context->GetTextRenderer())
+ if (m_Context->m_Context->GetTextRenderer()) {
m_Context->m_Context->GetTextRenderer()->AddProjectFontDirectory(
inScene.m_Presentation->m_PresentationDirectory);
+ }
+ if (m_Context->m_Context->getDistanceFieldRenderer()) {
+ m_Context->m_Context->getDistanceFieldRenderer()->AddProjectFontDirectory(
+ inScene.m_Presentation->m_PresentationDirectory);
+ }
eastl::string theBinaryPath(inPresentation.GetFilePath().toLatin1().constData());
qt3ds::foundation::CFileTools::AppendDirectoryInPathToFile(theBinaryPath, "binary");
eastl::string theBinaryDir(theBinaryPath);
diff --git a/src/Runtime/Source/engine/Qt3DSRenderRuntimeBindingImpl.h b/src/Runtime/Source/engine/Qt3DSRenderRuntimeBindingImpl.h
index 3ffe15ce..1d27f293 100644
--- a/src/Runtime/Source/engine/Qt3DSRenderRuntimeBindingImpl.h
+++ b/src/Runtime/Source/engine/Qt3DSRenderRuntimeBindingImpl.h
@@ -118,6 +118,11 @@ namespace render {
m_CoreContext->SetTextRendererCore(
ITextRendererCore::CreateQtTextRenderer(*m_Foundation, *m_StringTable));
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ m_CoreContext->setDistanceFieldRenderer(
+ ITextRendererCore::createDistanceFieldRenderer(*m_Foundation));
+#endif
+
m_CoreContext->SetOnscreenTextRendererCore(
ITextRendererCore::CreateOnscreenTextRenderer(*m_Foundation));
}
diff --git a/src/Runtime/Source/runtime/Qt3DSApplication.cpp b/src/Runtime/Source/runtime/Qt3DSApplication.cpp
index 249070c2..0538666d 100644
--- a/src/Runtime/Source/runtime/Qt3DSApplication.cpp
+++ b/src/Runtime/Source/runtime/Qt3DSApplication.cpp
@@ -1314,6 +1314,11 @@ struct SApp : public IApplication
NVFoundationBase &fnd(m_CoreFactory->GetFoundation());
+ if (m_CoreFactory->GetRenderContextCore().getDistanceFieldRenderer()) {
+ m_CoreFactory->GetRenderContextCore().getDistanceFieldRenderer()
+ ->AddProjectFontDirectory(projectDirectory.c_str());
+ }
+
if (m_CoreFactory->GetRenderContextCore().GetTextRendererCore()) {
m_CoreFactory->GetRenderContextCore().GetTextRendererCore()->AddProjectFontDirectory(
projectDirectory.c_str());
diff --git a/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp
new file mode 100644
index 00000000..c6941860
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDistanceFieldGlyphCache_p.h"
+
+#include <QtQuick/private/qsgareaallocator_p.h>
+
+#include <QtCore/qmath.h>
+#include <QtCore/qendian.h>
+#include <QtGui/qimage.h>
+
+#include "render/Qt3DSRenderContext.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "Qt3DSRenderResourceManager.h"
+#include "foundation/Qt3DSAllocator.h"
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+QT_BEGIN_NAMESPACE
+
+// Should work on most hardware. Used as stop gap until Qt 3D provides a
+// way to retrieve the system value
+#ifndef Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE
+# define Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE 2048
+#endif
+
+#if !defined(Q3DSDISTANCEFIELDGLYPHCACHE_PADDING)
+# define Q3DSDISTANCEFIELDGLYPHCACHE_PADDING 2
+#endif
+
+Q3DSDistanceFieldGlyphCache::Q3DSDistanceFieldGlyphCache(
+ const QRawFont &font, qt3ds::render::IQt3DSRenderContext &context)
+ : QSGDistanceFieldGlyphCache(font)
+ , m_context(context)
+{
+ m_maxTextureSize = Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE;
+
+ loadPregeneratedCache(font);
+}
+
+Q3DSDistanceFieldGlyphCache::~Q3DSDistanceFieldGlyphCache()
+{
+ for (auto &texture : m_textures)
+ qt3ds::foundation::NVDelete(m_context.GetAllocator(), texture.texture);
+}
+
+int Q3DSDistanceFieldGlyphCache::maxTextureSize() const
+{
+ return m_maxTextureSize;
+}
+
+Q3DSDistanceFieldGlyphCache::TextureInfo *Q3DSDistanceFieldGlyphCache::textureInfo(int index) const
+{
+ while (index >= m_textures.size())
+ m_textures.append(TextureInfo());
+ return &m_textures[index];
+}
+
+void Q3DSDistanceFieldGlyphCache::referenceGlyphs(const QSet<glyph_t> &glyphs)
+{
+ m_unusedGlyphs -= glyphs;
+}
+
+void Q3DSDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyphs)
+{
+ m_unusedGlyphs += glyphs;
+}
+
+void Q3DSDistanceFieldGlyphCache::resizeTexture(TextureInfo *info, int width, int height)
+{
+ QImage &image = info->copy;
+ if (info->texture == nullptr) {
+ info->texture = m_context.GetRenderContext().CreateTexture2D();
+ info->texture->SetMinFilter(qt3ds::render::NVRenderTextureMinifyingOp::Enum::Linear);
+ info->texture->SetMagFilter(qt3ds::render::NVRenderTextureMagnifyingOp::Enum::Linear);
+ info->texture->SetTextureData(qt3ds::render::toU8DataRef(image.bits(), image.byteCount()),
+ 0, image.width(), image.height(),
+ qt3ds::render::NVRenderTextureFormats::R8);
+ }
+
+ qt3ds::render::STextureDetails textureDetails = info->texture->GetTextureDetails();
+ if (int(textureDetails.m_Width) != width || int(textureDetails.m_Height) != height) {
+ info->texture->SetTextureData(qt3ds::render::toU8DataRef(image.bits(), image.byteCount()),
+ 0, image.width(), image.height(),
+ qt3ds::render::NVRenderTextureFormats::R8);
+ }
+
+ if (info->copy.width() != width || info->copy.height() != height) {
+ QImage newImage(width, height, QImage::Format_Alpha8);
+
+ for (int y = 0; y < info->copy.height(); ++y) {
+ uchar *dest = newImage.scanLine(y);
+ const uchar *src = info->copy.scanLine(y);
+ ::memcpy(dest, src, size_t(info->copy.width()));
+ }
+
+ info->copy = newImage;
+
+ info->texture->SetTextureData(qt3ds::render::toU8DataRef(image.bits(), image.byteCount()),
+ 0, image.width(), image.height(),
+ qt3ds::render::NVRenderTextureFormats::R8);
+ }
+}
+
+void Q3DSDistanceFieldGlyphCache::storeGlyphs(const QList<QDistanceField> &glyphs)
+{
+ using GlyphTextureHash = QHash<TextureInfo *, QVector<glyph_t> >;
+ using GlyphTextureHashConstIt = GlyphTextureHash::const_iterator;
+
+ GlyphTextureHash glyphTextures;
+ for (int i = 0; i < glyphs.size(); ++i) {
+ QDistanceField glyph = glyphs.at(i);
+ glyph_t glyphIndex = glyph.glyph();
+ TexCoord c = glyphTexCoord(glyphIndex);
+ TextureInfo *texInfo = m_glyphsTexture.value(glyphIndex);
+
+ resizeTexture(texInfo, maxTextureSize(), texInfo->allocatedArea.height());
+
+ Q_ASSERT(!glyphTextures[texInfo].contains(glyphIndex));
+ glyphTextures[texInfo].append(glyphIndex);
+
+ int padding = texInfo->padding;
+ int expectedWidth = qCeil(c.width + c.xMargin * 2);
+ glyph = glyph.copy(-padding, -padding,
+ expectedWidth + padding * 2, glyph.height() + padding * 2);
+
+ for (int y = 0; y < glyph.height(); ++y) {
+ const uchar *src = glyph.scanLine(y);
+ uchar *dest = texInfo->copy.scanLine(int(c.y) + y - padding) + (int(c.x) - padding);
+ ::memcpy(dest, src, size_t(glyph.width()));
+ }
+ }
+
+ for (GlyphTextureHashConstIt i = glyphTextures.constBegin(),
+ cend = glyphTextures.constEnd(); i != cend; ++i) {
+ Texture t;
+ // 0 == empty texture, (i - 1) == index into m_textures
+ t.textureId = uint(i.key() - m_textures.constData()) + 1;
+ qt3ds::render::STextureDetails textureDetails = i.key()->texture->GetTextureDetails();
+ t.size = QSize(textureDetails.m_Width, textureDetails.m_Height);
+ setGlyphsTexture(i.value(), t);
+
+ QImage &image = i.key()->copy;
+
+ i.key()->texture->SetTextureData(qt3ds::render::toU8DataRef(image.bits(),
+ image.byteCount()),
+ 0, image.width(), image.height(),
+ qt3ds::render::NVRenderTextureFormats::R8);
+ }
+}
+
+void Q3DSDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyphs)
+{
+ // Note: Most of this is copy-pasted from QSGDefaultDistanceFieldGlyphCache in Qt Quick.
+ // All of this can probably be shared as a default implementation, since it does not
+ // actually create any textures, but it might have to be either templated or based
+ // on void*. For now we will just live with the duplication.
+
+ QList<GlyphPosition> glyphPositions;
+ QVector<glyph_t> glyphsToRender;
+
+ if (m_areaAllocator == nullptr) {
+ m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(),
+ m_maxTextureCount * maxTextureSize()));
+ }
+
+ for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
+ glyph_t glyphIndex = *it;
+
+ int padding = Q3DSDISTANCEFIELDGLYPHCACHE_PADDING;
+ QRectF boundingRect = glyphData(glyphIndex).boundingRect;
+ int glyphWidth = qCeil(boundingRect.width()) + distanceFieldRadius() * 2;
+ int glyphHeight = qCeil(boundingRect.height()) + distanceFieldRadius() * 2;
+ QSize glyphSize(glyphWidth + padding * 2, glyphHeight + padding * 2);
+ QRect alloc = m_areaAllocator->allocate(glyphSize);
+
+ if (alloc.isNull()) {
+ // Unallocate unused glyphs until we can allocated the new glyph
+ while (alloc.isNull() && !m_unusedGlyphs.isEmpty()) {
+ glyph_t unusedGlyph = *m_unusedGlyphs.constBegin();
+
+ TexCoord unusedCoord = glyphTexCoord(unusedGlyph);
+ QRectF unusedGlyphBoundingRect = glyphData(unusedGlyph).boundingRect;
+ int unusedGlyphWidth = qCeil(unusedGlyphBoundingRect.width())
+ + distanceFieldRadius() * 2;
+ int unusedGlyphHeight = qCeil(unusedGlyphBoundingRect.height())
+ + distanceFieldRadius() * 2;
+ m_areaAllocator->deallocate(QRect(int(unusedCoord.x) - padding,
+ int(unusedCoord.y) - padding,
+ padding * 2 + unusedGlyphWidth,
+ padding * 2 + unusedGlyphHeight));
+
+ m_unusedGlyphs.remove(unusedGlyph);
+ m_glyphsTexture.remove(unusedGlyph);
+ removeGlyph(unusedGlyph);
+
+ alloc = m_areaAllocator->allocate(glyphSize);
+ }
+
+ // Not enough space left for this glyph... skip to the next one
+ if (alloc.isNull())
+ continue;
+ }
+
+ TextureInfo *tex = textureInfo(alloc.y() / maxTextureSize());
+ alloc = QRect(alloc.x(), alloc.y() % maxTextureSize(), alloc.width(), alloc.height());
+
+ tex->allocatedArea |= alloc;
+ Q_ASSERT(tex->padding == padding || tex->padding < 0);
+ tex->padding = padding;
+
+ GlyphPosition p;
+ p.glyph = glyphIndex;
+ p.position = alloc.topLeft() + QPoint(padding, padding);
+
+ glyphPositions.append(p);
+ glyphsToRender.append(glyphIndex);
+ m_glyphsTexture.insert(glyphIndex, tex);
+ }
+
+ setGlyphsPosition(glyphPositions);
+ markGlyphsToRender(glyphsToRender);
+}
+
+void Q3DSDistanceFieldGlyphCache::processPendingGlyphs()
+{
+ update();
+}
+
+Q3DSDistanceFieldGlyphCache::TextureInfo *Q3DSDistanceFieldGlyphCache::textureInfoById(
+ uint id) const
+{
+ Q_ASSERT(id > 0);
+ return textureInfo(id - 1);
+}
+
+// This is all copy-pasted from Qt Quick, as sharing it would require some refactoring, and we
+// need to work with Qt 5.12.2 at the moment.
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+qreal Q3DSDistanceFieldGlyphCache::fontSize() const
+{
+ return QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution);
+}
+
+bool Q3DSDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ using GlyphTextureHash = QHash<TextureInfo *, QVector<glyph_t> >;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = int(Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize));
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (padding != Q3DSDISTANCEFIELDGLYPHCACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ Q3DSDISTANCEFIELDGLYPHCACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(int(glyphCount));
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData,
+ qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(int(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX)));
+ tex->allocatedArea.setY(int(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY)));
+ tex->allocatedArea.setWidth(int(Qtdf::fetch<quint32>(textureRecord,
+ Qtdf::allocatedWidth)));
+ tex->allocatedArea.setHeight(int(Qtdf::fetch<quint32>(textureRecord,
+ Qtdf::allocatedHeight)));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) (qreal(value)/qreal(65536))
+
+ glyphData.texCoord.x
+ = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y
+ = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width
+ = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height
+ = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin
+ = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin
+ = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(
+ FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(
+ FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(
+ FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord,
+ Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(
+ FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord,
+ Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ resizeTexture(texInfo, width, height);
+
+ memcpy(texInfo->copy.bits(), textureData, size);
+ textureData += size;
+
+ QImage &image = texInfo->copy;
+ texInfo->texture->SetTextureData(qt3ds::render::toU8DataRef(image.bits(),
+ image.byteCount()),
+ 0, image.width(), image.height(),
+ qt3ds::render::NVRenderTextureFormats::R8);
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.textureId = uint(i + 1);
+ t.size = texInfo->copy.size();
+
+ setGlyphsTexture(glyphs, t);
+ }
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
diff --git a/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp
new file mode 100644
index 00000000..750301dc
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDistanceFieldGlyphCacheManager_p.h"
+#include "Qt3DSDistanceFieldGlyphCache_p.h"
+
+#include <QtQuick/private/qsgdefaultrendercontext_p.h>
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+ class FontKeyAccessor : public QSGDefaultRenderContext
+ {
+ public:
+ static QString fontKey(const QRawFont &font)
+ {
+ return QSGDefaultRenderContext::fontKey(font);
+ }
+ };
+}
+
+Q3DSDistanceFieldGlyphCacheManager::~Q3DSDistanceFieldGlyphCacheManager()
+{
+ for (auto &cache : qAsConst(m_glyphCaches))
+ delete cache;
+}
+
+Q3DSDistanceFieldGlyphCache *Q3DSDistanceFieldGlyphCacheManager::glyphCache(const QRawFont &font)
+{
+ QString key = FontKeyAccessor::fontKey(font);
+ Q3DSDistanceFieldGlyphCache *cache = m_glyphCaches.value(key);
+ if (cache == nullptr) {
+ cache = new Q3DSDistanceFieldGlyphCache(font, *m_context);
+ m_glyphCaches.insert(key, cache);
+ }
+
+ return cache;
+}
+
+void Q3DSDistanceFieldGlyphCacheManager::setContext(qt3ds::render::IQt3DSRenderContext &context)
+{
+ m_context = &context;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
diff --git a/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h
new file mode 100644
index 00000000..19d3c088
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCacheManager_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSDISTANCEFIELDGLYPHCACHEMANAGER_P_H
+#define Q3DSDISTANCEFIELDGLYPHCACHEMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qhash.h>
+#include <QtGui/qrawfont.h>
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+namespace qt3ds {
+namespace render {
+class IQt3DSRenderContext;
+}
+}
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSDistanceFieldGlyphCache;
+class Q3DSDistanceFieldGlyphCacheManager
+{
+public:
+ ~Q3DSDistanceFieldGlyphCacheManager();
+ Q3DSDistanceFieldGlyphCache *glyphCache(const QRawFont &font);
+ void setContext(qt3ds::render::IQt3DSRenderContext &context);
+
+private:
+ QHash<QString, Q3DSDistanceFieldGlyphCache *> m_glyphCaches;
+ qt3ds::render::IQt3DSRenderContext *m_context;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+#endif // Q3DSDISTANCEFIELDGLYPHCACHEMANAGER_P_H
diff --git a/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h
new file mode 100644
index 00000000..e3ec204a
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldGlyphCache_p.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSDISTANCEFIELDGLYPHCACHE_P_H
+#define Q3DSDISTANCEFIELDGLYPHCACHE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qsgadaptationlayer_p.h>
+#include "render/Qt3DSRenderTexture2D.h"
+#include "Qt3DSRenderContextCore.h"
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+QT_BEGIN_NAMESPACE
+
+class QSGAreaAllocator;
+class Q3DSDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache
+{
+public:
+ struct TextureInfo {
+ qt3ds::render::NVRenderTexture2D *texture;
+ int padding = -1;
+
+ QRect allocatedArea;
+ QImage copy;
+ };
+
+ Q3DSDistanceFieldGlyphCache(const QRawFont &font,
+ qt3ds::render::IQt3DSRenderContext &context);
+ ~Q3DSDistanceFieldGlyphCache() override;
+
+ void requestGlyphs(const QSet<glyph_t> &glyphs) override;
+ void storeGlyphs(const QList<QDistanceField> &glyphs) override;
+ void referenceGlyphs(const QSet<glyph_t> &glyphs) override;
+ void releaseGlyphs(const QSet<glyph_t> &glyphs) override;
+
+ void processPendingGlyphs() override;
+
+ TextureInfo *textureInfoById(uint textureId) const;
+
+ qreal fontSize() const;
+
+private:
+ bool loadPregeneratedCache(const QRawFont &font);
+ TextureInfo *textureInfo(int index) const;
+
+ int maxTextureSize() const;
+ void resizeTexture(TextureInfo *info, int width, int height);
+
+ QSGAreaAllocator *m_areaAllocator = nullptr;
+ int m_maxTextureSize = 0;
+ int m_maxTextureCount = 3;
+
+ mutable QVector<TextureInfo> m_textures;
+ QHash<glyph_t, TextureInfo *> m_glyphsTexture;
+ QSet<glyph_t> m_unusedGlyphs;
+ qt3ds::render::IQt3DSRenderContext &m_context;
+};
+
+QT_END_NAMESPACE
+
+#endif // Q3DSDISTANCEFIELDGLYPHCACHE_P_H
+
+#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
diff --git a/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.cpp b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.cpp
new file mode 100644
index 00000000..64e5a737
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.cpp
@@ -0,0 +1,1006 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSDistanceFieldRenderer.h"
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+#include "Qt3DSRenderContextCore.h"
+#include "Qt3DSRenderShaderCodeGeneratorV2.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "render/Qt3DSRenderContext.h"
+#include "qmath.h"
+#include "foundation/Qt3DSAllocator.h"
+
+using namespace qt3ds::render;
+
+Q3DSDistanceFieldRenderer::Q3DSDistanceFieldRenderer(NVFoundationBase &foundation)
+ : m_foundation(foundation)
+{
+ const QWindowList list = QGuiApplication::topLevelWindows();
+ if (list.size() > 0)
+ m_pixelRatio = list[0]->devicePixelRatio();
+}
+
+Q3DSDistanceFieldRenderer::~Q3DSDistanceFieldRenderer()
+{
+ NVAllocatorCallback &alloc = m_context->GetAllocator();
+
+ QHash<size_t, Q3DSDistanceFieldMesh>::const_iterator it;
+ for (it = m_meshCache.constBegin(); it != m_meshCache.constEnd(); ++it) {
+ const Q3DSDistanceFieldMesh &mesh = it.value();
+ NVDelete(alloc, mesh.vertexBuffer);
+ NVDelete(alloc, mesh.indexBuffer);
+ NVDelete(alloc, mesh.inputAssembler);
+ }
+}
+
+void Q3DSDistanceFieldRenderer::AddSystemFontDirectory(const char8_t *dir)
+{
+ QString systemDir(dir);
+ m_systemDirs += systemDir;
+ m_fontDatabase.registerFonts({ systemDir });
+}
+
+void Q3DSDistanceFieldRenderer::AddProjectFontDirectory(const char8_t *dir)
+{
+ QString projectDir(dir);
+ projectDir += QLatin1String("/fonts");
+ m_projectDirs += projectDir;
+ m_fontDatabase.registerFonts({ projectDir });
+}
+
+void Q3DSDistanceFieldRenderer::ClearProjectFontDirectories()
+{
+ m_fontDatabase.unregisterFonts(m_projectDirs);
+ m_projectDirs.clear();
+}
+
+ITextRenderer &Q3DSDistanceFieldRenderer::GetTextRenderer(NVRenderContext &)
+{
+ return *this;
+}
+
+void Q3DSDistanceFieldRenderer::EndFrame()
+{
+ // Remove meshes for glyphs that weren't rendered last frame
+ NVAllocatorCallback &alloc = m_context->GetAllocator();
+
+ QHash<size_t, Q3DSDistanceFieldMesh>::const_iterator it = m_meshCache.constBegin();
+ while (it != m_meshCache.constEnd()) {
+ const size_t glyphHash = it.key();
+ const Q3DSDistanceFieldMesh &mesh = it.value();
+ if (!m_renderedGlyphs.contains(glyphHash)) {
+ NVDelete(alloc, mesh.vertexBuffer);
+ NVDelete(alloc, mesh.indexBuffer);
+ NVDelete(alloc, mesh.inputAssembler);
+ m_meshCache.erase(it++);
+ } else {
+ it++;
+ }
+ }
+ m_renderedGlyphs.clear();
+}
+
+QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo>
+Q3DSDistanceFieldRenderer::buildGlyphsPerTexture(const SText &textInfo)
+{
+ if (textInfo.m_BoundingBox.x < 0 || textInfo.m_BoundingBox.y < 0)
+ return QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo>();
+
+ QVector2D boundingBox = QVector2D(textInfo.m_BoundingBox.x, textInfo.m_BoundingBox.y);
+
+ QRawFont font = m_fontDatabase.findFont(textInfo.m_Font.c_str());
+ font.setPixelSize(qreal(textInfo.m_FontSize));
+
+ const qreal maximumWidth = boundingBox.isNull() ? qreal(0x01000000) : qreal(boundingBox.x());
+ const qreal maximumHeight = boundingBox.isNull() ? qreal(0x01000000) : qreal(boundingBox.y());
+
+ QTextLayout layout;
+ QTextOption option = layout.textOption();
+
+ QTextOption::WrapMode wrapMode;
+ switch (textInfo.m_WordWrap) {
+ case TextWordWrap::Clip:
+ wrapMode = QTextOption::ManualWrap;
+ break;
+ case TextWordWrap::WrapWord:
+ wrapMode = QTextOption::WrapAtWordBoundaryOrAnywhere;
+ break;
+ case TextWordWrap::WrapAnywhere:
+ wrapMode = QTextOption::WrapAnywhere;
+ break;
+ case TextWordWrap::Unknown:
+ wrapMode = QTextOption::ManualWrap;
+ Q_ASSERT(0);
+ };
+ option.setWrapMode(wrapMode);
+ option.setUseDesignMetrics(true);
+
+ layout.setTextOption(option);
+ layout.setRawFont(font);
+
+ QString text = textInfo.m_Text.c_str();
+ text.replace(QLatin1Char('\n'), QChar::LineSeparator);
+
+ qreal width;
+ qreal height;
+ bool needsElide;
+ do {
+ needsElide = false;
+
+ layout.clearLayout();
+
+ float leading = textInfo.m_Leading;
+ width = 0.0;
+ height = qreal(-leading);
+
+ QVector<QTextLayout::FormatRange> formatRanges;
+
+ QTextLayout::FormatRange formatRange;
+ formatRange.start = 0;
+ formatRange.length = text.length();
+ formatRange.format.setFontLetterSpacingType(QFont::AbsoluteSpacing);
+ formatRange.format.setFontLetterSpacing(qreal(textInfo.m_Tracking));
+ formatRanges.append(formatRange);
+ layout.setFormats(formatRanges);
+
+ layout.setText(text);
+ layout.beginLayout();
+
+ QTextLine previousLine;
+ forever {
+ QTextLine line = layout.createLine();
+ if (!line.isValid())
+ break;
+
+ line.setLineWidth(maximumWidth);
+ height += qreal(leading);
+ height = qCeil(height);
+
+ qreal textWidth = line.naturalTextWidth();
+ line.setPosition(QPointF(0.0, height));
+
+ width = qMin(maximumWidth, qMax(width, textWidth));
+ height += layout.engine()->lines[line.lineNumber()].height().toReal();
+
+ // Fast path for right elide
+ if (textInfo.m_Elide == TextElide::ElideRight
+ && previousLine.isValid()
+ && height > maximumHeight) {
+ break;
+ }
+
+ previousLine = line;
+ }
+ layout.endLayout();
+
+ if (textInfo.m_Elide != TextElide::ElideNone && height > maximumHeight) {
+ needsElide = true;
+
+ QString elidedText;
+ switch (textInfo.m_Elide) {
+ case TextElide::ElideRight:
+ if (previousLine.textStart() > 0)
+ elidedText = text.left(previousLine.textStart());
+
+ elidedText += layout.engine()->elidedText(
+ Qt::ElideRight, QFixed::fromReal(width), 0, previousLine.textStart(),
+ text.length() - previousLine.textStart());
+ break;
+ case TextElide::ElideLeft:
+ {
+ height = 0.0;
+ previousLine = QTextLine();
+ for (int i = layout.lineCount() - 1; i >= 0; --i) {
+ qreal lineHeight = layout.lineAt(i).height();
+ if (i < layout.lineCount() - 1 && height + lineHeight > maximumHeight)
+ break;
+ height += lineHeight;
+ previousLine = layout.lineAt(i);
+ }
+
+ Q_ASSERT(previousLine.isValid());
+ elidedText += layout.engine()->elidedText(
+ Qt::ElideLeft, QFixed::fromReal(width), 0, 0,
+ previousLine.textStart() + previousLine.textLength());
+
+ int nextPosition = (previousLine.textStart() + previousLine.textLength());
+ if (nextPosition < text.length())
+ elidedText += text.mid(nextPosition);
+ break;
+ }
+ case TextElide::ElideMiddle:
+ {
+ height = 0.0;
+ QTextLine lastLineBefore;
+ QTextLine firstLineAfter;
+ for (int i = 0; i < (layout.lineCount() / 2) + (layout.lineCount() % 2); ++i) {
+ qreal lineHeight = 3 * layout.lineAt(i).height();
+ if (height + lineHeight > maximumHeight)
+ break;
+ height += lineHeight;
+
+ lastLineBefore = layout.lineAt(i);
+ firstLineAfter = layout.lineAt(layout.lineCount() - i - 1);
+ }
+
+ int nextPosition = 0;
+ if (lastLineBefore.isValid()) {
+ elidedText += text.left(lastLineBefore.textStart()
+ + lastLineBefore.textLength());
+ nextPosition = lastLineBefore.textStart() + lastLineBefore.textLength();
+ }
+
+ QString suffix;
+ int length = text.length() - nextPosition;
+ if (firstLineAfter.isValid()) {
+ length = firstLineAfter.textStart() - nextPosition;
+ suffix = text.mid(firstLineAfter.textStart());
+ }
+
+ elidedText += layout.engine()->elidedText(
+ Qt::ElideMiddle, QFixed::fromReal(width), 0, nextPosition, length);
+
+ elidedText += suffix;
+ break;
+ }
+ case TextElide::ElideNone:
+ Q_UNREACHABLE();
+ };
+
+ // Failsafe
+ if (elidedText.isEmpty() || elidedText == text)
+ needsElide = false;
+ else
+ text = elidedText;
+ }
+ } while (needsElide);
+
+ QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo> glyphsPerTexture;
+
+ float originY = float(height) / 2.0f;
+ if (textInfo.m_VerticalAlignment == TextVerticalAlignment::Bottom)
+ originY = float(height);
+ else if (textInfo.m_VerticalAlignment == TextVerticalAlignment::Top)
+ originY = 0.0;
+
+ float originX = float(width) / 2.0f;
+ if (textInfo.m_HorizontalAlignment == TextHorizontalAlignment::Right)
+ originX = float(width);
+ else if (textInfo.m_HorizontalAlignment == TextHorizontalAlignment::Left)
+ originX = 0.0;
+
+ float offsetY = -originY;
+
+ QT3DSVec3 minimum(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), 0);
+ QT3DSVec3 maximum(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(), 0);
+
+ // To match the original behavior of the sources, we don't actually align to
+ // the bounding box. This is only used for word wrapping and elide. Keeping the
+ // code here in case this was a mistake or we for some other reason want to change
+ // it.
+#if 0
+ // If there is no bounding box, then alignmentHeight == height, so we skip it
+ if (!boundingBox.isNull() && text3DS->verticalAlignment() == Q3DSTextNode::Bottom)
+ offsetY += float(maximumHeight - height);
+ else if (!boundingBox.isNull() && text3DS->verticalAlignment() == Q3DSTextNode::Middle)
+ offsetY += float(maximumHeight / 2.0 - height / 2.0);
+ float alignmentWidth = boundingBox.isNull() ? float(width) : float(maximumWidth);
+#else
+ float alignmentWidth = float(width);
+#endif
+
+ for (int j = 0; j < layout.lineCount(); ++j) {
+ QTextLine line = layout.lineAt(j);
+
+ float offsetX = -originX;
+ if (textInfo.m_HorizontalAlignment == TextHorizontalAlignment::Right)
+ offsetX += alignmentWidth - float(line.naturalTextWidth());
+ else if (textInfo.m_HorizontalAlignment == TextHorizontalAlignment::Center)
+ offsetX += alignmentWidth / 2.0f - float(line.naturalTextWidth()) / 2.0f;
+
+ const QList<QGlyphRun> glyphRuns = line.glyphRuns();
+ for (const QGlyphRun &glyphRun : glyphRuns) {
+ const QVector<quint32> glyphIndexes = glyphRun.glyphIndexes();
+ const QVector<QPointF> glyphPositions = glyphRun.positions();
+
+ Q3DSDistanceFieldGlyphCache *cache = m_glyphCacheManager.glyphCache(
+ glyphRun.rawFont());
+ cache->populate(glyphRun.glyphIndexes());
+ cache->processPendingGlyphs();
+
+ qreal fontPixelSize = glyphRun.rawFont().pixelSize();
+
+ qreal shadowOffsetX = qreal(cache->fontSize())
+ * qreal(textInfo.m_DropShadowOffsetX) / 1000.0;
+ qreal shadowOffsetY = qreal(cache->fontSize())
+ * qreal(textInfo.m_DropShadowOffsetY) / 1000.0;
+
+ qreal maxTexMargin = cache->distanceFieldRadius();
+ qreal fontScale = cache->fontScale(fontPixelSize);
+ qreal margin = 2;
+ qreal texMargin = margin / fontScale;
+ if (texMargin > maxTexMargin) {
+ texMargin = maxTexMargin;
+ margin = maxTexMargin * fontScale;
+ }
+
+ for (int i = 0; i < glyphIndexes.size(); ++i) {
+ quint32 glyphIndex = glyphIndexes.at(i);
+ QPointF position = glyphPositions.at(i);
+
+ QSGDistanceFieldGlyphCache::TexCoord c = cache->glyphTexCoord(glyphIndex);
+ if (c.isNull())
+ continue;
+
+ QSGDistanceFieldGlyphCache::Metrics metrics = cache->glyphMetrics(glyphIndex,
+ fontPixelSize);
+ if (metrics.isNull())
+ continue;
+
+ metrics.width += margin * 2;
+ metrics.height += margin * 2;
+ metrics.baselineX -= margin;
+ metrics.baselineY += margin;
+ c.xMargin -= texMargin;
+ c.yMargin -= texMargin;
+ c.width += texMargin * 2;
+ c.height += texMargin * 2;
+
+ float cx1 = float(position.x() + metrics.baselineX) + offsetX;
+ float cx2 = cx1 + float(metrics.width);
+ float cy1 = float(position.y() - metrics.baselineY) + offsetY;
+ float cy2 = cy1 + float(metrics.height);
+
+ if (textInfo.m_DropShadow) {
+ if (shadowOffsetX < 0.0)
+ cx1 += float(shadowOffsetX * fontScale);
+ else
+ cx2 += float(shadowOffsetX * fontScale);
+
+ if (shadowOffsetY < 0.0)
+ cy1 += float(shadowOffsetY * fontScale);
+ else
+ cy2 += float(shadowOffsetY * fontScale);
+ }
+
+ cy1 = -cy1;
+ cy2 = -cy2;
+
+ if (cx1 < minimum.x)
+ minimum.x = cx1;
+ else if (cx1 > maximum.x)
+ maximum.x = cx1;
+
+ if (cx2 < minimum.x)
+ minimum.x = cx2;
+ else if (cx2 > maximum.x)
+ maximum.x = cx2;
+
+ if (cy1 < minimum.y)
+ minimum.y = cy1;
+ else if (cy1 > maximum.y)
+ maximum.y = cy1;
+
+ if (cy2 < minimum.y)
+ minimum.y = cy2;
+ else if (cy2 > maximum.y)
+ maximum.y = cy2;
+
+ if (boundingBox.x() > 0 || boundingBox.y() > 0) {
+ const float halfWidth = boundingBox.x() / 2.0f;
+ const float halfHeight = boundingBox.y() / 2.0f;
+ if (maximum.x < halfWidth)
+ maximum.x = halfWidth;
+ if (minimum.x > -halfWidth)
+ minimum.x = -halfWidth;
+ if (maximum.y < halfHeight)
+ maximum.y = halfHeight;
+ if (minimum.y > -halfHeight)
+ minimum.y = -halfHeight;
+ }
+
+ float tx1 = float(c.x + c.xMargin);
+ float tx2 = tx1 + float(c.width);
+ float ty1 = float(c.y + c.yMargin);
+ float ty2 = ty1 + float(c.height);
+
+ // Preserve original bounds of glyphs
+ float ttx1 = tx1;
+ float tty1 = ty1;
+ float ttx2 = tx2;
+ float tty2 = ty2;
+
+ if (textInfo.m_DropShadow) {
+ if (shadowOffsetX < 0.0)
+ tx1 += float(c.width * shadowOffsetX * fontScale) / float(metrics.width);
+ else
+ tx2 += float(c.width * shadowOffsetX * fontScale) / float(metrics.width);
+
+ if (shadowOffsetY < 0.0)
+ ty1 += float(c.height * shadowOffsetY * fontScale) / float(metrics.height);
+ else
+ ty2 += float(c.height * shadowOffsetY * fontScale) / float(metrics.height);
+ }
+
+ const QSGDistanceFieldGlyphCache::Texture *texture
+ = cache->glyphTexture(glyphIndex);
+ if (texture->textureId == 0) {
+ qWarning() << "Empty texture for glyph" << glyphIndex;
+ continue;
+ }
+
+ Q3DSDistanceFieldGlyphCache::TextureInfo *textureInfo = cache->textureInfoById(
+ texture->textureId);
+
+ GlyphInfo &glyphInfo = glyphsPerTexture[textureInfo];
+ glyphInfo.fontScale = float(fontScale);
+ glyphInfo.shadowOffsetX = float(shadowOffsetX);
+ glyphInfo.shadowOffsetY = float(shadowOffsetY);
+ glyphInfo.bounds = NVBounds3(minimum, maximum);
+
+ QVector<float> &vertexes = glyphInfo.vertexes;
+ vertexes.reserve(vertexes.size() + 20 + (textInfo.m_DropShadow ? 16 : 0));
+
+ vertexes.append(cx1);
+ vertexes.append(cy1);
+ vertexes.append(0.0);
+ vertexes.append(tx1);
+ vertexes.append(ty1);
+
+ if (textInfo.m_DropShadow) {
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+
+ vertexes.append(cx2);
+ vertexes.append(cy1);
+ vertexes.append(0.0);
+ vertexes.append(tx2);
+ vertexes.append(ty1);
+
+ if (textInfo.m_DropShadow) {
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+
+ vertexes.append(cx2);
+ vertexes.append(cy2);
+ vertexes.append(0.0);
+ vertexes.append(tx2);
+ vertexes.append(ty2);
+
+ if (textInfo.m_DropShadow) {
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+
+ vertexes.append(cx1);
+ vertexes.append(cy2);
+ vertexes.append(0.0);
+ vertexes.append(tx1);
+ vertexes.append(ty2);
+
+ if (textInfo.m_DropShadow) {
+ vertexes.append(ttx1);
+ vertexes.append(tty1);
+ vertexes.append(ttx2);
+ vertexes.append(tty2);
+ }
+ }
+ }
+ }
+
+ return glyphsPerTexture;
+}
+
+template <typename T>
+static QVector<T> fillIndexBuffer(uint quadCount)
+{
+ QVector<T> indexes;
+
+ const uint triangleCount = 2 * quadCount;
+ indexes.resize(3 * int(triangleCount));
+
+ Q_ASSERT(indexes.size() % 6 == 0);
+
+ for (uint i = 0; i < quadCount; i ++) {
+ indexes[int(i * 6 + 0)] = T(i * 4 + 0);
+ indexes[int(i * 6 + 1)] = T(i * 4 + 3);
+ indexes[int(i * 6 + 2)] = T(i * 4 + 1);
+
+ indexes[int(i * 6 + 3)] = T(i * 4 + 1);
+ indexes[int(i * 6 + 4)] = T(i * 4 + 3);
+ indexes[int(i * 6 + 5)] = T(i * 4 + 2);
+ }
+
+ return indexes;
+}
+
+void Q3DSDistanceFieldRenderer::buildShaders()
+{
+ IShaderProgramGenerator &gen = m_context->GetShaderProgramGenerator();
+ gen.BeginProgram();
+ IShaderStageGenerator &vertexGenerator(*gen.GetStage(ShaderGeneratorStages::Vertex));
+ IShaderStageGenerator &fragmentGenerator(*gen.GetStage(ShaderGeneratorStages::Fragment));
+
+ if (m_context->GetRenderContext().GetRenderContextType() == NVRenderContextValues::GLES2) {
+ vertexGenerator.AddInclude("distancefieldtext.vert");
+ fragmentGenerator.AddInclude("distancefieldtext.frag");
+ } else {
+ vertexGenerator.AddInclude("distancefieldtext_core.vert");
+ fragmentGenerator.AddInclude("distancefieldtext_core.frag");
+ }
+
+ m_shader.program = gen.CompileGeneratedShader("distancefieldtext",
+ SShaderCacheProgramFlags(),
+ TShaderFeatureSet(), false);
+
+ if (m_shader.program) {
+ m_shader.mvp = NVRenderCachedShaderProperty<QT3DSMat44>(
+ "mvp", *m_shader.program);
+ m_shader.modelView = NVRenderCachedShaderProperty<QT3DSMat44>(
+ "modelView", *m_shader.program);
+ m_shader.textureWidth = NVRenderCachedShaderProperty<QT3DSI32>(
+ "textureWidth", *m_shader.program);
+ m_shader.textureHeight = NVRenderCachedShaderProperty<QT3DSI32>(
+ "textureHeight", *m_shader.program);
+ m_shader.fontScale = NVRenderCachedShaderProperty<QT3DSF32>(
+ "fontScale", *m_shader.program);
+ m_shader.texture = NVRenderCachedShaderProperty<NVRenderTexture2D *>(
+ "_qt_texture", *m_shader.program);
+ m_shader.color = NVRenderCachedShaderProperty<QT3DSVec4>(
+ "color", *m_shader.program);
+ }
+
+ gen.BeginProgram();
+ vertexGenerator = *gen.GetStage(ShaderGeneratorStages::Vertex);
+ fragmentGenerator = *gen.GetStage(ShaderGeneratorStages::Fragment);
+
+ if (m_context->GetRenderContext().GetRenderContextType() == NVRenderContextValues::GLES2) {
+ vertexGenerator.AddInclude("distancefieldtext_dropshadow.vert");
+ fragmentGenerator.AddInclude("distancefieldtext_dropshadow.frag");
+ } else {
+ vertexGenerator.AddInclude("distancefieldtext_dropshadow_core.vert");
+ fragmentGenerator.AddInclude("distancefieldtext_dropshadow_core.frag");
+ }
+
+ m_dropShadowShader.program = gen.CompileGeneratedShader("distancefieldtext_dropshadow",
+ SShaderCacheProgramFlags(),
+ TShaderFeatureSet(), false);
+
+ if (m_dropShadowShader.program) {
+ m_dropShadowShader.mvp = NVRenderCachedShaderProperty<QT3DSMat44>(
+ "mvp", *m_dropShadowShader.program);
+ m_dropShadowShader.modelView = NVRenderCachedShaderProperty<QT3DSMat44>(
+ "modelView", *m_dropShadowShader.program);
+ m_dropShadowShader.textureWidth = NVRenderCachedShaderProperty<QT3DSI32>(
+ "textureWidth", *m_dropShadowShader.program);
+ m_dropShadowShader.textureHeight = NVRenderCachedShaderProperty<QT3DSI32>(
+ "textureHeight", *m_dropShadowShader.program);
+ m_dropShadowShader.fontScale = NVRenderCachedShaderProperty<QT3DSF32>(
+ "fontScale", *m_dropShadowShader.program);
+ m_dropShadowShader.shadowOffset = NVRenderCachedShaderProperty<QT3DSVec2>(
+ "shadowOffset", *m_dropShadowShader.program);
+ m_dropShadowShader.texture = NVRenderCachedShaderProperty<NVRenderTexture2D *>(
+ "_qt_texture", *m_dropShadowShader.program);
+ m_dropShadowShader.color = NVRenderCachedShaderProperty<QT3DSVec4>(
+ "color", *m_dropShadowShader.program);
+ m_dropShadowShader.shadowColor = NVRenderCachedShaderProperty<QT3DSVec4>(
+ "shadowColor", *m_dropShadowShader.program);
+ }
+}
+
+Q3DSDistanceFieldMesh Q3DSDistanceFieldRenderer::buildMesh(const GlyphInfo &glyphInfo,
+ bool shadow)
+{
+ static NVRenderVertexBufferEntry entries[] = {
+ NVRenderVertexBufferEntry("vCoord", NVRenderComponentTypes::QT3DSF32, 3),
+ NVRenderVertexBufferEntry("tCoord", NVRenderComponentTypes::QT3DSF32, 2, 3 * sizeof(float))
+ };
+
+ static NVRenderVertexBufferEntry shadowEntries[] = {
+ NVRenderVertexBufferEntry("vCoord", NVRenderComponentTypes::QT3DSF32, 3),
+ NVRenderVertexBufferEntry("tCoord", NVRenderComponentTypes::QT3DSF32, 2,
+ 3 * sizeof(float)),
+ NVRenderVertexBufferEntry("textureBounds", NVRenderComponentTypes::QT3DSF32, 4,
+ 5 * sizeof(float))
+ };
+
+ const uint floatsPerVertex = 3 + 2 + (shadow ? 4 : 0);
+ const uint stride = floatsPerVertex * sizeof(float);
+ const uint offset = 0;
+
+ NVRenderContext &renderContext = m_context->GetRenderContext();
+ QVector<float> vertexes = glyphInfo.vertexes;
+
+ Q_ASSERT(uint(vertexes.size()) % floatsPerVertex == 0);
+ const uint vertexCount = uint(vertexes.size()) / floatsPerVertex;
+
+ Q_ASSERT(vertexCount % 4 == 0);
+ const uint quadCount = vertexCount / 4;
+
+ Q3DSDistanceFieldMesh mesh;
+
+ mesh.attribLayout = renderContext.CreateAttributeLayout(
+ toConstDataRef(shadow ? shadowEntries : entries, shadow ? 3 : 2));
+ mesh.vertexBuffer = renderContext.CreateVertexBuffer(
+ NVRenderBufferUsageType::Static, size_t(vertexes.size()) * sizeof(float), stride,
+ toU8DataRef(vertexes.begin(), QT3DSU32(vertexes.size())));
+
+ if (vertexCount <= 0xffff) {
+ QVector<QT3DSU16> indexes = fillIndexBuffer<QT3DSU16>(quadCount);
+ mesh.indexBuffer = renderContext.CreateIndexBuffer(
+ NVRenderBufferUsageType::Static, NVRenderComponentTypes::QT3DSU16,
+ size_t(indexes.size()) * sizeof(QT3DSU16),
+ toU8DataRef(indexes.begin(), QT3DSU32(indexes.size())));
+ } else {
+ QVector<QT3DSU32> indexes = fillIndexBuffer<QT3DSU32>(quadCount);
+ mesh.indexBuffer = renderContext.CreateIndexBuffer(
+ NVRenderBufferUsageType::Static, NVRenderComponentTypes::QT3DSU32,
+ size_t(indexes.size()) * sizeof(QT3DSU32),
+ toU8DataRef(indexes.begin(), QT3DSU32(indexes.size())));
+ }
+
+ mesh.inputAssembler = renderContext.CreateInputAssembler(
+ mesh.attribLayout, toConstDataRef(&mesh.vertexBuffer, 1), mesh.indexBuffer,
+ toConstDataRef(&stride, 1), toConstDataRef(&offset, 1));
+
+ return mesh;
+}
+
+void Q3DSDistanceFieldRenderer::renderMesh(
+ NVRenderInputAssembler *inputAssembler, NVRenderTexture2D *texture, const QT3DSMat44 &mvp,
+ const QT3DSMat44 &modelView, QT3DSI32 textureWidth, QT3DSI32 textureHeight,
+ QT3DSF32 fontScale, QT3DSVec4 color)
+{
+ NVRenderContext &renderContext = m_context->GetRenderContext();
+ renderContext.SetCullingEnabled(false);
+
+ renderContext.SetBlendFunction(NVRenderBlendFunctionArgument(
+ NVRenderSrcBlendFunc::One,
+ NVRenderDstBlendFunc::OneMinusSrcAlpha,
+ NVRenderSrcBlendFunc::One,
+ NVRenderDstBlendFunc::OneMinusSrcAlpha));
+ renderContext.SetBlendEquation(NVRenderBlendEquationArgument(
+ NVRenderBlendEquation::Add, NVRenderBlendEquation::Add));
+
+ renderContext.SetActiveShader(m_shader.program);
+ m_shader.mvp.Set(mvp);
+ m_shader.modelView.Set(modelView);
+ m_shader.textureWidth.Set(textureWidth);
+ m_shader.textureHeight.Set(textureHeight);
+ m_shader.fontScale.Set(fontScale);
+ m_shader.texture.Set(texture);
+ m_shader.color.Set(color);
+
+ renderContext.SetInputAssembler(inputAssembler);
+ renderContext.Draw(NVRenderDrawMode::Triangles, inputAssembler->GetIndexCount(), 0);
+}
+
+void Q3DSDistanceFieldRenderer::renderMeshWithDropShadow(
+ NVRenderInputAssembler *inputAssembler, NVRenderTexture2D *texture, const QT3DSMat44 &mvp,
+ const QT3DSMat44 &modelView, QT3DSI32 textureWidth, QT3DSI32 textureHeight,
+ QT3DSF32 fontScale, QT3DSVec2 shadowOffset, QT3DSVec4 color, QT3DSVec4 shadowColor)
+{
+ NVRenderContext &renderContext = m_context->GetRenderContext();
+ renderContext.SetCullingEnabled(false);
+
+ renderContext.SetBlendFunction(NVRenderBlendFunctionArgument(
+ NVRenderSrcBlendFunc::One,
+ NVRenderDstBlendFunc::OneMinusSrcAlpha,
+ NVRenderSrcBlendFunc::One,
+ NVRenderDstBlendFunc::OneMinusSrcAlpha));
+ renderContext.SetBlendEquation(NVRenderBlendEquationArgument(
+ NVRenderBlendEquation::Add, NVRenderBlendEquation::Add));
+
+ renderContext.SetActiveShader(m_dropShadowShader.program);
+ m_dropShadowShader.mvp.Set(mvp);
+ m_dropShadowShader.modelView.Set(modelView);
+ m_dropShadowShader.textureWidth.Set(textureWidth);
+ m_dropShadowShader.textureHeight.Set(textureHeight);
+ m_dropShadowShader.fontScale.Set(fontScale);
+ m_dropShadowShader.shadowOffset.Set(shadowOffset);
+ m_dropShadowShader.texture.Set(texture);
+ m_dropShadowShader.color.Set(color);
+ m_dropShadowShader.shadowColor.Set(shadowColor);
+
+ renderContext.SetInputAssembler(inputAssembler);
+ renderContext.Draw(NVRenderDrawMode::Triangles, inputAssembler->GetIndexCount(), 0);
+}
+
+namespace std {
+template<class T>
+struct hash<QVector<T>>
+{
+ size_t operator()(const QVector<T> &s) const
+ {
+ return qHash(s);
+ }
+};
+
+template<>
+struct hash<TextHorizontalAlignment::Enum>
+{
+ size_t operator()(const TextHorizontalAlignment::Enum& s) const
+ {
+ return qHash(s);
+ }
+};
+
+template<>
+struct hash<TextVerticalAlignment::Enum>
+{
+ size_t operator()(const TextVerticalAlignment::Enum& s) const
+ {
+ return qHash(s);
+ }
+};
+
+template<>
+struct hash<TextElide::Enum>
+{
+ size_t operator()(const TextElide::Enum& s) const
+ {
+ return qHash(s);
+ }
+};
+
+template<>
+struct hash<TextWordWrap::Enum>
+{
+ size_t operator()(const TextWordWrap::Enum& s) const
+ {
+ return qHash(s);
+ }
+};
+}
+
+// Copied from boost
+template <class T>
+inline void hashCombine(std::size_t &seed, const T &v)
+{
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+}
+
+size_t getTextHashValue(const SText &text)
+{
+ size_t hashValue = 0;
+ hashCombine(hashValue, text.m_TextColor.x);
+ hashCombine(hashValue, text.m_TextColor.y);
+ hashCombine(hashValue, text.m_TextColor.z);
+ hashCombine(hashValue, std::string(text.m_Font.c_str()));
+ hashCombine(hashValue, std::string(text.m_Text.c_str()));
+ hashCombine(hashValue, text.m_Elide);
+ hashCombine(hashValue, text.m_Leading);
+ hashCombine(hashValue, text.m_FontSize);
+ hashCombine(hashValue, text.m_Tracking);
+ hashCombine(hashValue, text.m_WordWrap);
+ hashCombine(hashValue, text.m_DropShadow);
+ hashCombine(hashValue, text.m_BoundingBox.x);
+ hashCombine(hashValue, text.m_BoundingBox.y);
+ hashCombine(hashValue, text.m_DropShadowOffsetX);
+ hashCombine(hashValue, text.m_DropShadowOffsetY);
+ hashCombine(hashValue, text.m_DropShadowStrength);
+ hashCombine(hashValue, text.m_VerticalAlignment);
+ hashCombine(hashValue, text.m_HorizontalAlignment);
+ hashCombine(hashValue, text.m_DropShadowVerticalAlignment);
+ hashCombine(hashValue, text.m_DropShadowHorizontalAlignment);
+ return hashValue;
+}
+
+size_t getGlyphHashValue(const GlyphInfo &glyph)
+{
+ size_t hashValue = 0;
+ hashCombine(hashValue, glyph.vertexes);
+ hashCombine(hashValue, glyph.glyphIndexes);
+ hashCombine(hashValue, glyph.fontScale);
+ hashCombine(hashValue, glyph.shadowOffsetX);
+ hashCombine(hashValue, glyph.shadowOffsetY);
+ return hashValue;
+}
+
+void Q3DSDistanceFieldRenderer::renderText(SText &text, const QT3DSMat44 &mvp,
+ const QT3DSMat44 &modelView)
+{
+ if (!m_shader.program)
+ buildShaders();
+
+ int shadowRgb = int(100 - int(text.m_DropShadowStrength));
+ QT3DSVec4 shadowColor(shadowRgb * 0.01f, shadowRgb * 0.01f, shadowRgb * 0.01f, 1);
+
+ size_t textHashValue = getTextHashValue(text);
+ if (!m_glyphCache.contains(textHashValue))
+ m_glyphCache[textHashValue] = buildGlyphsPerTexture(text);
+
+ QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo> &glyphsPerTexture
+ = m_glyphCache[textHashValue];
+
+ QT3DSVec3 minimum(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), 0);
+ QT3DSVec3 maximum(-std::numeric_limits<float>::max(), -std::numeric_limits<float>::max(), 0);
+
+ QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo>::const_iterator it;
+ for (it = glyphsPerTexture.constBegin(); it != glyphsPerTexture.constEnd(); ++it) {
+ const GlyphInfo &glyphInfo = it.value();
+
+ if (glyphInfo.bounds.minimum.x < minimum.x)
+ minimum.x = glyphInfo.bounds.minimum.x;
+ if (glyphInfo.bounds.minimum.y < minimum.y)
+ minimum.y = glyphInfo.bounds.minimum.y;
+ if (glyphInfo.bounds.minimum.z < minimum.z)
+ minimum.z = glyphInfo.bounds.minimum.z;
+
+ if (glyphInfo.bounds.maximum.x > maximum.x)
+ maximum.x = glyphInfo.bounds.maximum.x;
+ if (glyphInfo.bounds.maximum.y > maximum.y)
+ maximum.y = glyphInfo.bounds.maximum.y;
+ if (glyphInfo.bounds.maximum.z > maximum.z)
+ maximum.z = glyphInfo.bounds.maximum.z;
+
+ size_t glyphHashValue = getGlyphHashValue(glyphInfo);
+
+ if (!m_meshCache.contains(glyphHashValue))
+ m_meshCache[glyphHashValue] = buildMesh(glyphInfo, text.m_DropShadow);
+
+ Q3DSDistanceFieldMesh &mesh = m_meshCache[glyphHashValue];
+
+ STextureDetails textureDetails = it.key()->texture->GetTextureDetails();
+
+ if (text.m_DropShadow) {
+ renderMeshWithDropShadow(mesh.inputAssembler, it.key()->texture, mvp, modelView,
+ int(textureDetails.m_Width), int(textureDetails.m_Height),
+ glyphInfo.fontScale * float(m_pixelRatio),
+ QT3DSVec2(glyphInfo.shadowOffsetX, glyphInfo.shadowOffsetY),
+ QT3DSVec4(text.m_TextColor, 1),
+ shadowColor);
+ } else {
+ renderMesh(mesh.inputAssembler, it.key()->texture, mvp, modelView,
+ int(textureDetails.m_Width), int(textureDetails.m_Height),
+ glyphInfo.fontScale * float(m_pixelRatio),
+ QT3DSVec4(text.m_TextColor, 1));
+ }
+
+ m_renderedGlyphs += glyphHashValue;
+ }
+
+ text.m_Bounds = NVBounds3(minimum, maximum);
+}
+
+void Q3DSDistanceFieldRenderer::renderTextDepth(SText &text, const QT3DSMat44 &mvp,
+ const QT3DSMat44 &modelView)
+{
+ // TODO: Create a depth pass shader for distance field text
+ renderText(text, mvp, modelView);
+}
+
+void Q3DSDistanceFieldRenderer::setContext(IQt3DSRenderContext &context)
+{
+ m_context = &context;
+ m_glyphCacheManager.setContext(context);
+}
+
+ITextRendererCore &ITextRendererCore::createDistanceFieldRenderer(NVFoundationBase &fnd)
+{
+ return *QT3DS_NEW(fnd.getAllocator(), Q3DSDistanceFieldRenderer)(fnd);
+}
+
+// Unused methods:
+
+void Q3DSDistanceFieldRenderer::PreloadFonts()
+{
+ Q_ASSERT(false);
+}
+
+void Q3DSDistanceFieldRenderer::BeginPreloadFonts(IThreadPool &, IPerfTimer &)
+{
+ Q_ASSERT(false);
+}
+
+void Q3DSDistanceFieldRenderer::EndPreloadFonts()
+{
+ Q_ASSERT(false);
+}
+
+void Q3DSDistanceFieldRenderer::ReloadFonts()
+{
+ Q_ASSERT(false);
+}
+
+NVConstDataRef<SRendererFontEntry> Q3DSDistanceFieldRenderer::GetProjectFontList()
+{
+ Q_ASSERT(false);
+ return NVConstDataRef<SRendererFontEntry>();
+}
+
+Option<CRegisteredString> Q3DSDistanceFieldRenderer::GetFontNameForFont(CRegisteredString)
+{
+ Q_ASSERT(false);
+ return Option<CRegisteredString>();
+}
+
+Option<CRegisteredString> Q3DSDistanceFieldRenderer::GetFontNameForFont(const char8_t *)
+{
+ Q_ASSERT(false);
+ return Option<CRegisteredString>();
+}
+
+STextDimensions Q3DSDistanceFieldRenderer::MeasureText(const STextRenderInfo &, QT3DSF32,
+ const char8_t *)
+{
+ Q_ASSERT(false);
+ return STextDimensions();
+}
+
+STextTextureDetails Q3DSDistanceFieldRenderer::RenderText(const STextRenderInfo &,
+ NVRenderTexture2D &)
+{
+ Q_ASSERT(false);
+ return STextTextureDetails();
+}
+
+STextTextureDetails Q3DSDistanceFieldRenderer::RenderText(
+ const STextRenderInfo &, NVRenderPathFontItem &, NVRenderPathFontSpecification &)
+{
+ Q_ASSERT(false);
+ return STextTextureDetails();
+}
+
+SRenderTextureAtlasDetails Q3DSDistanceFieldRenderer::RenderText(const STextRenderInfo &)
+{
+ Q_ASSERT(false);
+ return SRenderTextureAtlasDetails();
+}
+
+void Q3DSDistanceFieldRenderer::BeginFrame()
+{
+ Q_ASSERT(false);
+}
+
+QT3DSI32 Q3DSDistanceFieldRenderer::CreateTextureAtlas()
+{
+ Q_ASSERT(false);
+ return 0;
+}
+
+STextTextureAtlasEntryDetails Q3DSDistanceFieldRenderer::RenderAtlasEntry(QT3DSU32,
+ NVRenderTexture2D &)
+{
+ Q_ASSERT(false);
+ return STextTextureAtlasEntryDetails();
+}
+
+#endif
diff --git a/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.h b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.h
new file mode 100644
index 00000000..0e037fd8
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSDistanceFieldRenderer.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT3DSDISTANCEFIELDRENDERER_H
+#define QT3DSDISTANCEFIELDRENDERER_H
+
+#include "Qt3DSFontDatabase_p.h"
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+#include "Qt3DSTextRenderer.h"
+#include "Qt3DSRenderText.h"
+#include "render/Qt3DSRenderShaderProgram.h"
+
+#include "Qt3DSDistanceFieldGlyphCacheManager_p.h"
+#include "Qt3DSDistanceFieldGlyphCache_p.h"
+
+namespace qt3ds {
+namespace render {
+
+struct GlyphInfo {
+ QVector<float> vertexes;
+ QVector<quint32> glyphIndexes;
+ Q3DSDistanceFieldGlyphCache *cache;
+ float fontScale;
+ float shadowOffsetX;
+ float shadowOffsetY;
+ NVBounds3 bounds;
+};
+
+struct Q3DSDistanceFieldShader {
+ NVRenderShaderProgram *program = nullptr;
+ NVRenderCachedShaderProperty<QT3DSMat44> mvp;
+ NVRenderCachedShaderProperty<QT3DSMat44> modelView;
+ NVRenderCachedShaderProperty<QT3DSI32> textureWidth;
+ NVRenderCachedShaderProperty<QT3DSI32> textureHeight;
+ NVRenderCachedShaderProperty<QT3DSF32> fontScale;
+ NVRenderCachedShaderProperty<NVRenderTexture2D *> texture;
+ NVRenderCachedShaderProperty<QT3DSVec4> color;
+};
+
+struct Q3DSDistanceFieldDropShadowShader {
+ NVRenderShaderProgram *program = nullptr;
+ NVRenderCachedShaderProperty<QT3DSMat44> mvp;
+ NVRenderCachedShaderProperty<QT3DSMat44> modelView;
+ NVRenderCachedShaderProperty<QT3DSI32> textureWidth;
+ NVRenderCachedShaderProperty<QT3DSI32> textureHeight;
+ NVRenderCachedShaderProperty<QT3DSF32> fontScale;
+ NVRenderCachedShaderProperty<QT3DSVec2> shadowOffset;
+ NVRenderCachedShaderProperty<NVRenderTexture2D *> texture;
+ NVRenderCachedShaderProperty<QT3DSVec4> color;
+ NVRenderCachedShaderProperty<QT3DSVec4> shadowColor;
+};
+
+struct Q3DSDistanceFieldMesh
+{
+ NVRenderAttribLayout *attribLayout = nullptr;
+ NVRenderVertexBuffer *vertexBuffer = nullptr;
+ NVRenderIndexBuffer *indexBuffer = nullptr;
+ NVRenderInputAssembler *inputAssembler = nullptr;
+};
+
+class Q3DSDistanceFieldRenderer : public ITextRenderer
+{
+public:
+ Q3DSDistanceFieldRenderer(NVFoundationBase &foundation);
+ ~Q3DSDistanceFieldRenderer() override;
+ QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo> buildGlyphsPerTexture(
+ const SText &textInfo);
+ void buildShaders();
+ Q3DSDistanceFieldMesh buildMesh(const GlyphInfo &glyphInfo, bool shadow);
+ void renderMesh(NVRenderInputAssembler *inputAssembler,
+ NVRenderTexture2D *texture, const QT3DSMat44 &mvp,
+ const QT3DSMat44 &modelView, QT3DSI32 textureWidth,
+ QT3DSI32 textureHeight, QT3DSF32 fontScale, QT3DSVec4 color);
+ void renderMeshWithDropShadow(NVRenderInputAssembler *inputAssembler,
+ NVRenderTexture2D *texture, const QT3DSMat44 &mvp,
+ const QT3DSMat44 &modelView, QT3DSI32 textureWidth,
+ QT3DSI32 textureHeight, QT3DSF32 fontScale,
+ QT3DSVec2 shadowOffset, QT3DSVec4 color, QT3DSVec4 shadowColor);
+ void renderText(SText &text, const QT3DSMat44 &mvp, const QT3DSMat44 &modelView);
+ void renderTextDepth(SText &text, const QT3DSMat44 &mvp, const QT3DSMat44 &modelView);
+ void setContext(IQt3DSRenderContext &context);
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_foundation.getAllocator())
+
+ void AddSystemFontDirectory(const char8_t *dir) override;
+ void AddProjectFontDirectory(const char8_t *dir) override;
+ void ClearProjectFontDirectories() override;
+ ITextRenderer &GetTextRenderer(NVRenderContext &) override;
+ void EndFrame() override;
+
+ // Unused methods:
+ void PreloadFonts() override;
+ void BeginPreloadFonts(IThreadPool &, IPerfTimer &) override;
+ void EndPreloadFonts() override;
+ void ReloadFonts() override;
+
+ NVConstDataRef<SRendererFontEntry> GetProjectFontList() override;
+
+ Option<CRegisteredString> GetFontNameForFont(CRegisteredString) override;
+ Option<CRegisteredString> GetFontNameForFont(const char8_t *) override;
+
+ STextDimensions MeasureText(const STextRenderInfo &, QT3DSF32,
+ const char8_t *) override;
+
+ STextTextureDetails RenderText(const STextRenderInfo &,
+ NVRenderTexture2D &) override;
+
+ STextTextureDetails RenderText(const STextRenderInfo &, NVRenderPathFontItem &,
+ NVRenderPathFontSpecification &) override;
+
+ SRenderTextureAtlasDetails RenderText(const STextRenderInfo &) override;
+
+ void BeginFrame() override;
+
+ QT3DSI32 CreateTextureAtlas() override;
+ STextTextureAtlasEntryDetails RenderAtlasEntry(QT3DSU32, NVRenderTexture2D &) override;
+
+private:
+ IQt3DSRenderContext *m_context = nullptr;
+ QHash<size_t, QHash<Q3DSDistanceFieldGlyphCache::TextureInfo *, GlyphInfo>> m_glyphCache;
+ QHash<size_t, Q3DSDistanceFieldMesh> m_meshCache;
+
+ Q3DSFontDatabase m_fontDatabase;
+ Q3DSDistanceFieldGlyphCacheManager m_glyphCacheManager;
+
+ Q3DSDistanceFieldShader m_shader;
+ Q3DSDistanceFieldDropShadowShader m_dropShadowShader;
+ QVector<size_t> m_renderedGlyphs;
+
+ QStringList m_systemDirs;
+ QStringList m_projectDirs;
+
+ qreal m_pixelRatio = 0.0;
+
+ NVFoundationBase &m_foundation;
+ volatile QT3DSI32 mRefCount = 0;
+};
+
+}
+}
+
+#endif // Qt version check
+
+#endif // QT3DSDISTANCEFIELDRENDERER_H
diff --git a/src/Runtime/Source/runtimerender/Qt3DSFontDatabase.cpp b/src/Runtime/Source/runtimerender/Qt3DSFontDatabase.cpp
new file mode 100644
index 00000000..b44cdbd1
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSFontDatabase.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "Qt3DSFontDatabase_p.h"
+
+#include <QtCore/qdir.h>
+#include "qloggingcategory.h"
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+QT_BEGIN_NAMESPACE
+
+Q3DSFontDatabase::Q3DSFontDatabase()
+{
+}
+
+void Q3DSFontDatabase::registerFonts(const QStringList &directories)
+{
+ const QStringList nameFilters = { QStringLiteral("*.ttf"), QStringLiteral("*.otf") };
+ for (const QString &directory : directories) {
+ QDir fontDir(directory);
+ if (!fontDir.exists()) {
+ qWarning("Attempted to register invalid font directory: %s",
+ qPrintable(directory));
+ continue;
+ }
+
+ const QFileInfoList entryInfoList = fontDir.entryInfoList(nameFilters);
+ for (const QFileInfo &entryInfo : entryInfoList) {
+ QRawFont font(entryInfo.absoluteFilePath(), 16);
+ if (!font.isValid()) {
+ qWarning("Invalid font file: %s", qPrintable(entryInfo.absoluteFilePath()));
+ continue;
+ }
+
+ // ### Only support scalable fonts
+
+ QString fontId = entryInfo.baseName();
+ if (std::find_if(m_fonts.constBegin(), m_fonts.constEnd(),
+ [fontId](const Font &f) { return f.fontId == fontId; })
+ != m_fonts.constEnd()) {
+ // already registered
+ continue;
+ }
+
+ m_fonts.append(Font(fontId, entryInfo.absoluteFilePath()));
+ }
+ }
+}
+
+void Q3DSFontDatabase::unregisterFonts(const QStringList &directories)
+{
+ for (const QString &directory : directories) {
+ QDir dir(directory);
+ QVector<Font>::iterator it = m_fonts.begin();
+ while (it != m_fonts.end()) {
+ if (dir == QDir(QFileInfo(it->filePath).absolutePath()))
+ it = m_fonts.erase(it);
+ else
+ ++it;
+ }
+ }
+}
+
+QRawFont Q3DSFontDatabase::findFont(const QString &fontId)
+{
+ for (Font &font : m_fonts) {
+ if (font.fontId == fontId) {
+ if (!font.rawFont.isValid())
+ font.rawFont = QRawFont(font.filePath, 16);
+ return font.rawFont;
+ }
+ }
+
+ return QRawFont::fromFont(QFont());
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
diff --git a/src/Runtime/Source/runtimerender/Qt3DSFontDatabase_p.h b/src/Runtime/Source/runtimerender/Qt3DSFontDatabase_p.h
new file mode 100644
index 00000000..73e03624
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSFontDatabase_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef Q3DSFONTDATABASE_P_H
+#define Q3DSFONTDATABASE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qvector.h>
+#include <QtGui/qrawfont.h>
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+QT_BEGIN_NAMESPACE
+
+class Q3DSSceneManager;
+class Q3DSFontDatabase
+{
+public:
+ Q3DSFontDatabase();
+
+ void registerFonts(const QStringList &directories);
+ void unregisterFonts(const QStringList &directories);
+
+ QRawFont findFont(const QString &fontId);
+
+private:
+ struct Font {
+ Font() = default;
+ Font(const QString &id, const QString &path)
+ : fontId(id)
+ , filePath(path)
+ {}
+
+ QString fontId;
+ QString filePath;
+ QRawFont rawFont;
+ };
+
+ QVector<Font> m_fonts;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+
+#endif // Q3DSFONTDATABASE_P_H
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.cpp b/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.cpp
index e0eedfd3..3c940805 100644
--- a/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.cpp
+++ b/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.cpp
@@ -65,6 +65,7 @@
#include "Qt3DSRenderShaderCodeGeneratorV2.h"
#include "Qt3DSRenderDefaultMaterialShaderGenerator.h"
#include "Qt3DSRenderCustomMaterialShaderGenerator.h"
+#include "Qt3DSDistanceFieldRenderer.h"
using namespace qt3ds::render;
@@ -85,6 +86,7 @@ struct SRenderContextCore : public IQt3DSRenderContextCore
NVScopedRefCounted<ITextRendererCore> m_TextRenderer;
NVScopedRefCounted<ITextRendererCore> m_OnscreenTexRenderer;
NVScopedRefCounted<IPathManagerCore> m_PathManagerCore;
+ NVScopedRefCounted<ITextRendererCore> m_distanceFieldRenderer;
QT3DSI32 mRefCount;
SRenderContextCore(NVFoundationBase &fnd, IStringTable &strTable)
@@ -127,6 +129,11 @@ struct SRenderContextCore : public IQt3DSRenderContextCore
const char8_t *inPrimitivesDirectory) override;
void SetTextRendererCore(ITextRendererCore &inRenderer) override { m_TextRenderer = inRenderer; }
ITextRendererCore *GetTextRendererCore() override { return m_TextRenderer.mPtr; }
+ void setDistanceFieldRenderer(ITextRendererCore &inRenderer) override
+ {
+ m_distanceFieldRenderer = inRenderer;
+ }
+ ITextRendererCore *getDistanceFieldRenderer() override { return m_distanceFieldRenderer.mPtr; }
void SetOnscreenTextRendererCore(ITextRendererCore &inRenderer) override
{
m_OnscreenTexRenderer = inRenderer;
@@ -213,6 +220,7 @@ struct SRenderContext : public IQt3DSRenderContext
NVScopedRefCounted<IOffscreenRenderManager> m_OffscreenRenderManager;
NVScopedRefCounted<IQt3DSRenderer> m_Renderer;
NVScopedRefCounted<ITextRenderer> m_TextRenderer;
+ NVScopedRefCounted<ITextRenderer> m_distanceFieldRenderer;
NVScopedRefCounted<ITextRenderer> m_OnscreenTextRenderer;
NVScopedRefCounted<ITextTextureCache> m_TextTextureCache;
NVScopedRefCounted<ITextTextureAtlas> m_TextTextureAtlas;
@@ -307,6 +315,15 @@ struct SRenderContext : public IQt3DSRenderContext
m_RenderContext->GetFoundation(), *m_TextRenderer, *m_RenderContext);
}
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ ITextRendererCore *distanceFieldRenderer = inCore.getDistanceFieldRenderer();
+ if (distanceFieldRenderer) {
+ m_distanceFieldRenderer = distanceFieldRenderer->GetTextRenderer(ctx);
+ static_cast<Q3DSDistanceFieldRenderer *>(m_distanceFieldRenderer.mPtr)
+ ->setContext(*this);
+ }
+#endif
+
ITextRendererCore *theOnscreenTextCore = inCore.GetOnscreenTextRendererCore();
if (theOnscreenTextCore) {
m_OnscreenTextRenderer = theOnscreenTextCore->GetTextRenderer(ctx);
@@ -381,6 +398,8 @@ struct SRenderContext : public IQt3DSRenderContext
ITextRenderer *GetTextRenderer() override { return m_TextRenderer; }
+ ITextRenderer *getDistanceFieldRenderer() override { return m_distanceFieldRenderer; }
+
ITextRenderer *GetOnscreenTextRenderer() override { return m_OnscreenTextRenderer; }
void SetSceneColor(Option<QT3DSVec4> inSceneColor) override { m_SceneColor = inSceneColor; }
@@ -797,6 +816,8 @@ struct SRenderContext : public IQt3DSRenderContext
m_TextTextureCache->EndFrame();
if (m_TextRenderer)
m_TextRenderer->EndFrame();
+ if (m_distanceFieldRenderer)
+ m_distanceFieldRenderer->EndFrame();
m_OffscreenRenderManager->EndFrame();
m_Renderer->EndFrame();
m_CustomMaterialSystem->EndFrame();
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.h b/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.h
index be1498b8..012864ba 100644
--- a/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.h
+++ b/src/Runtime/Source/runtimerender/Qt3DSRenderContextCore.h
@@ -70,6 +70,8 @@ namespace render {
// Text renderers may be provided by clients at runtime.
virtual void SetTextRendererCore(ITextRendererCore &inRenderer) = 0;
virtual ITextRendererCore *GetTextRendererCore() = 0;
+ virtual void setDistanceFieldRenderer(ITextRendererCore &inRenderer) = 0;
+ virtual ITextRendererCore *getDistanceFieldRenderer() = 0;
// this is our default 2D text onscreen renderer
virtual void SetOnscreenTextRendererCore(ITextRendererCore &inRenderer) = 0;
virtual ITextRendererCore *GetOnscreenTextRendererCore() = 0;
@@ -106,6 +108,7 @@ namespace render {
virtual IPerfTimer &GetPerfTimer() = 0;
virtual ITextTextureCache *GetTextureCache() = 0;
virtual ITextRenderer *GetTextRenderer() = 0;
+ virtual ITextRenderer *getDistanceFieldRenderer() = 0;
virtual IRenderList &GetRenderList() = 0;
virtual IPathManager &GetPathManager() = 0;
virtual IShaderProgramGenerator &GetShaderProgramGenerator() = 0;
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderer.h b/src/Runtime/Source/runtimerender/Qt3DSRenderer.h
index 360ebf48..69a49942 100644
--- a/src/Runtime/Source/runtimerender/Qt3DSRenderer.h
+++ b/src/Runtime/Source/runtimerender/Qt3DSRenderer.h
@@ -182,7 +182,8 @@ namespace render {
// Testing function to allow clients to render a layer using a custom view project instead
// of the one that would be setup
// using the layer's camera in conjunction with the layer's position,scale.
- virtual void RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection) = 0;
+ virtual void RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inProjection,
+ const QT3DSMat44 &inViewProjection) = 0;
// This allocator is cleared every frame on BeginFrame. Objects constructed using this
// allocator
diff --git a/src/Runtime/Source/runtimerender/Qt3DSTextRenderer.h b/src/Runtime/Source/runtimerender/Qt3DSTextRenderer.h
index 756c3a90..65b7cb00 100644
--- a/src/Runtime/Source/runtimerender/Qt3DSTextRenderer.h
+++ b/src/Runtime/Source/runtimerender/Qt3DSTextRenderer.h
@@ -79,6 +79,10 @@ namespace render {
static ITextRendererCore &CreateQtTextRenderer(NVFoundationBase &inFoundation,
IStringTable &inStrTable);
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ static ITextRendererCore &createDistanceFieldRenderer(NVFoundationBase &inFnd);
+#endif
+
// call this to create onscreen text renderer
// it needs true type fonts
static ITextRendererCore &CreateOnscreenTextRenderer(NVFoundationBase &inFoundation);
diff --git a/src/Runtime/Source/runtimerender/graphobjects/Qt3DSRenderText.cpp b/src/Runtime/Source/runtimerender/graphobjects/Qt3DSRenderText.cpp
index 8b44e00f..e4694c56 100644
--- a/src/Runtime/Source/runtimerender/graphobjects/Qt3DSRenderText.cpp
+++ b/src/Runtime/Source/runtimerender/graphobjects/Qt3DSRenderText.cpp
@@ -67,10 +67,5 @@ SText::SText()
NVBounds3 SText::GetTextBounds() const
{
- NVBounds3 retval;
- retval.setEmpty();
- if (m_TextTexture != NULL) {
- retval.include(m_Bounds);
- }
- return retval;
+ return m_Bounds;
}
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp
index 7d8054f7..24684861 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.cpp
@@ -430,6 +430,18 @@ namespace render {
}
}
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ void SDistanceFieldRenderable::Render(const QT3DSVec2 &inCameraVec)
+ {
+ m_distanceFieldText.renderText(m_text, m_mvp, m_modelView);
+ }
+
+ void SDistanceFieldRenderable::RenderDepthPass(const QT3DSVec2 &inCameraVec)
+ {
+ m_distanceFieldText.renderTextDepth(m_text, m_mvp, m_modelView);
+ }
+#endif
+
void SCustomMaterialRenderable::Render(const QT3DSVec2 & /*inCameraVec*/,
const SLayerRenderData &inLayerData,
const SLayer &inLayer, NVDataRef<SLight *> inLights,
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.h b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.h
index d380b850..deca9c47 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.h
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRenderableObjects.h
@@ -42,6 +42,7 @@
#include "Qt3DSRenderShaderCache.h"
#include "foundation/Qt3DSInvasiveLinkedList.h"
#include "Qt3DSRenderableImage.h"
+#include "Qt3DSDistanceFieldRenderer.h"
namespace qt3ds {
namespace render {
@@ -59,7 +60,8 @@ namespace render {
CustomMaterialMeshSubset = 1 << 7,
HasRefraction = 1 << 8,
Path = 1 << 9,
- ShadowCaster = 1 << 10
+ ShadowCaster = 1 << 10,
+ DistanceField = 1 << 11,
};
};
@@ -129,6 +131,16 @@ namespace render {
void SetText(bool inText) { ClearOrSet(inText, RenderPreparationResultFlagValues::Text); }
bool IsText() const { return this->operator&(RenderPreparationResultFlagValues::Text); }
+ void setDistanceField(bool inText)
+ {
+ ClearOrSet(inText, RenderPreparationResultFlagValues::DistanceField);
+ }
+
+ bool isDistanceField() const
+ {
+ return this->operator&(RenderPreparationResultFlagValues::DistanceField);
+ }
+
void SetCustom(bool inCustom)
{
ClearOrSet(inCustom, RenderPreparationResultFlagValues::Custom);
@@ -296,6 +308,7 @@ namespace render {
m_RenderableFlags.SetDefaultMaterialMeshSubset(true);
m_RenderableFlags.SetCustom(false);
m_RenderableFlags.SetText(false);
+ m_RenderableFlags.setDistanceField(false);
}
void Render(const QT3DSVec2 &inCameraVec, TShaderFeatureSet inFeatureSet);
@@ -374,11 +387,41 @@ namespace render {
m_RenderableFlags.SetDefaultMaterialMeshSubset(false);
m_RenderableFlags.SetCustom(false);
m_RenderableFlags.SetText(true);
+ m_RenderableFlags.setDistanceField(false);
+ }
+
+ void Render(const QT3DSVec2 &inCameraVec);
+ void RenderDepthPass(const QT3DSVec2 &inCameraVec);
+ };
+
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ struct SDistanceFieldRenderable : public SRenderableObject
+ {
+ Q3DSDistanceFieldRenderer &m_distanceFieldText;
+ QT3DSMat44 m_mvp;
+ QT3DSMat44 m_modelView;
+ SText &m_text;
+
+ SDistanceFieldRenderable(SRenderableObjectFlags flags, QT3DSVec3 worldCenterPt,
+ SText &text, const NVBounds3 &bounds,
+ const QT3DSMat44 &mvp, const QT3DSMat44 &modelView,
+ Q3DSDistanceFieldRenderer &distanceFieldText)
+ : SRenderableObject(flags, worldCenterPt, text.m_GlobalTransform, bounds)
+ , m_distanceFieldText(distanceFieldText)
+ , m_mvp(mvp)
+ , m_modelView(modelView)
+ , m_text(text)
+ {
+ m_RenderableFlags.SetDefaultMaterialMeshSubset(false);
+ m_RenderableFlags.SetCustom(false);
+ m_RenderableFlags.SetText(false);
+ m_RenderableFlags.setDistanceField(true);
}
void Render(const QT3DSVec2 &inCameraVec);
void RenderDepthPass(const QT3DSVec2 &inCameraVec);
};
+#endif
struct SPathRenderable : public SRenderableObject
{
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp
index d6fb3b1b..7c2603f6 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.cpp
@@ -696,9 +696,19 @@ namespace render {
{
if (inRenderableObject.m_RenderableFlags.IsText()) {
STextRenderable &theRenderable = static_cast<STextRenderable &>(inRenderableObject);
- if (&theRenderable.m_Text == &inNode)
+ if (&theRenderable.m_Text == &inNode) {
return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform,
inRenderableObject.m_Bounds);
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ } else if (inRenderableObject.m_RenderableFlags.isDistanceField()) {
+ SDistanceFieldRenderable &theRenderable = static_cast<SDistanceFieldRenderable &>(
+ inRenderableObject);
+ if (&theRenderable.m_text == &inNode) {
+ return inPickRay.GetRelativeXY(inRenderableObject.m_GlobalTransform,
+ inRenderableObject.m_Bounds);
+ }
+#endif
} else if (inRenderableObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) {
SSubsetRenderable &theRenderable = static_cast<SSubsetRenderable &>(inRenderableObject);
if (&theRenderable.m_ModelContext.m_Model == &inNode)
@@ -964,14 +974,15 @@ namespace render {
}
// This doesn't have to be cheap.
- void Qt3DSRendererImpl::RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection)
+ void Qt3DSRendererImpl::RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inProjection,
+ const QT3DSMat44 &inViewProjection)
{
SLayerRenderData *theData = GetOrCreateLayerRenderDataForNode(inLayer);
if (theData == NULL || theData->m_Camera == NULL) {
QT3DS_ASSERT(false);
return;
}
- theData->PrepareAndRender(inViewProjection);
+ theData->PrepareAndRender(inProjection, inViewProjection);
}
void Qt3DSRendererImpl::AddRenderWidget(IRenderWidget &inWidget)
@@ -1356,6 +1367,10 @@ namespace render {
&static_cast<SSubsetRenderable *>(&inRenderableObject)->m_ModelContext.m_Model;
else if (inRenderableObject.m_RenderableFlags.IsText())
thePickObject = &static_cast<STextRenderable *>(&inRenderableObject)->m_Text;
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ else if (inRenderableObject.m_RenderableFlags.isDistanceField())
+ thePickObject = &static_cast<SDistanceFieldRenderable *>(&inRenderableObject)->m_text;
+#endif
else if (inRenderableObject.m_RenderableFlags.IsCustomMaterialMeshSubset())
thePickObject = &static_cast<SCustomMaterialRenderable *>(&inRenderableObject)
->m_ModelContext.m_Model;
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.h b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.h
index 686e7d3b..e5b39b84 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.h
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImpl.h
@@ -365,7 +365,8 @@ namespace render {
Option<NVRenderRectF> GetLayerRect(SLayer &inLayer) override;
- void RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inViewProjection) override;
+ void RunLayerRender(SLayer &inLayer, const QT3DSMat44 &inProjection,
+ const QT3DSMat44 &inViewProjection) override;
// Note that this allocator is completely reset on BeginFrame.
NVAllocatorCallback &GetPerFrameAllocator() override
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp
index af3cb613..3fb3bc68 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.cpp
@@ -787,11 +787,15 @@ namespace render {
const QT3DSVec2 &inCameraProps, TShaderFeatureSet, QT3DSU32,
const SCamera &inCamera)
{
- if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset())
+ if (inObject.m_RenderableFlags.IsDefaultMaterialMeshSubset()) {
static_cast<SSubsetRenderable &>(inObject).RenderDepthPass(inCameraProps);
- else if (inObject.m_RenderableFlags.IsText())
+ } else if (inObject.m_RenderableFlags.IsText()) {
static_cast<STextRenderable &>(inObject).RenderDepthPass(inCameraProps);
- else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) {
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ } else if (inObject.m_RenderableFlags.isDistanceField()) {
+ static_cast<SDistanceFieldRenderable &>(inObject).RenderDepthPass(inCameraProps);
+#endif
+ } else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) {
static_cast<SCustomMaterialRenderable &>(inObject).RenderDepthPass(
inCameraProps, inData.m_Layer, inData.m_Lights, inCamera, NULL);
} else if (inObject.m_RenderableFlags.IsPath()) {
@@ -845,6 +849,10 @@ namespace render {
static_cast<SSubsetRenderable &>(inObject).Render(inCameraProps, inFeatureSet);
else if (inObject.m_RenderableFlags.IsText())
static_cast<STextRenderable &>(inObject).Render(inCameraProps);
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ else if (inObject.m_RenderableFlags.isDistanceField())
+ static_cast<SDistanceFieldRenderable &>(inObject).Render(inCameraProps);
+#endif
else if (inObject.m_RenderableFlags.IsCustomMaterialMeshSubset()) {
// PKC : Need a better place to do this.
SCustomMaterialRenderable &theObject =
@@ -1419,6 +1427,15 @@ namespace render {
static_cast<STextRenderable &>(*m_TransparentObjects[idx]);
OffsetProjectionMatrix(theRenderable.m_ModelViewProjection,
theVertexOffsets);
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ } else if (m_TransparentObjects[idx]->m_RenderableFlags
+ .isDistanceField()) {
+ SDistanceFieldRenderable &theRenderable
+ = static_cast<SDistanceFieldRenderable &>(
+ *m_TransparentObjects[idx]);
+ OffsetProjectionMatrix(theRenderable.m_mvp,
+ theVertexOffsets);
+#endif
} else if (m_TransparentObjects[idx]->m_RenderableFlags.IsPath()) {
SPathRenderable &theRenderable =
static_cast<SPathRenderable &>(*m_TransparentObjects[idx]);
@@ -2140,7 +2157,8 @@ namespace render {
m_BoundingRectColor.setEmpty();
}
- void SLayerRenderData::PrepareAndRender(const QT3DSMat44 &inViewProjection)
+ void SLayerRenderData::PrepareAndRender(const QT3DSMat44 &inProjection,
+ const QT3DSMat44 &inViewProjection)
{
TRenderableObjectList theTransparentObjects(m_TransparentObjects);
TRenderableObjectList theOpaqueObjects(m_OpaqueObjects);
@@ -2148,7 +2166,7 @@ namespace render {
theOpaqueObjects.clear();
m_ModelContexts.clear();
SLayerRenderPreparationResultFlags theFlags;
- PrepareRenderablesForRender(inViewProjection, Empty(), 1.0, theFlags);
+ PrepareRenderablesForRender(inProjection, inViewProjection, Empty(), 1.0, theFlags);
RenderDepthPass(false);
Render();
}
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h
index 4e237b0b..6716eada 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderData.h
@@ -153,7 +153,7 @@ struct AdvancedBlendModes
// layer setup system. This assumes the client has setup the viewport, scissor, and render
// target
// the way they want them.
- void PrepareAndRender(const QT3DSMat44 &inViewProjection);
+ void PrepareAndRender(const QT3DSMat44 &inProjection, const QT3DSMat44 &inViewProjection);
SOffscreenRendererEnvironment CreateOffscreenRenderEnvironment() override;
IRenderTask &CreateRenderToTextureRunnable() override;
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp
index 68ed9907..2940f088 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.cpp
@@ -362,8 +362,8 @@ namespace render {
}
bool SLayerRenderPreparationData::PrepareTextForRender(
- SText &inText, const QT3DSMat44 &inViewProjection, QT3DSF32 inTextScaleFactor,
- SLayerRenderPreparationResultFlags &ioFlags)
+ SText &inText, const QT3DSMat44 &inProjection, const QT3DSMat44 &inViewProjection,
+ QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags)
{
ITextTextureCache *theTextRenderer = m_Renderer.GetQt3DSContext().GetTextureCache();
if (theTextRenderer == NULL)
@@ -378,31 +378,49 @@ namespace render {
if (theFlags.IsCompletelyTransparent() == false) {
retval = inText.m_Flags.IsDirty() || inText.m_Flags.IsTextDirty();
inText.m_Flags.SetTextDirty(false);
- TTPathObjectAndTexture theResult =
- theTextRenderer->RenderText(inText, inTextScaleFactor);
- inText.m_TextTexture = theResult.second.second.mPtr;
- inText.m_TextTextureDetails = theResult.second.first;
- inText.m_PathFontItem = theResult.first.second;
- inText.m_PathFontDetails = theResult.first.first;
- STextScaleAndOffset theScaleAndOffset(*inText.m_TextTexture,
- inText.m_TextTextureDetails, inText);
- QT3DSVec2 theTextScale(theScaleAndOffset.m_TextScale);
- QT3DSVec2 theTextOffset(theScaleAndOffset.m_TextOffset);
- QT3DSVec3 minimum(theTextOffset[0] - theTextScale[0], theTextOffset[1] - theTextScale[1],
- 0);
- QT3DSVec3 maximum(theTextOffset[0] + theTextScale[0], theTextOffset[1] + theTextScale[1],
- 0);
- inText.m_Bounds = NVBounds3(minimum, maximum);
QT3DSMat44 theMVP;
QT3DSMat33 theNormalMatrix;
inText.CalculateMVPAndNormalMatrix(inViewProjection, theMVP, theNormalMatrix);
- if (inText.m_PathFontDetails)
- ioFlags.SetRequiresStencilBuffer(true);
+ SRenderableObject *theRenderable = nullptr;
+#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
+ // TODO: Implement clipping for the distance field renderer
+ if (inText.m_WordWrap != TextWordWrap::Clip || (inText.m_BoundingBox.x == 0.0f
+ && inText.m_BoundingBox.y == 0.0f)) {
+ QT3DSMat44 modelView = (inProjection.getInverse() * inViewProjection)
+ * inText.m_GlobalTransform;
+ Q3DSDistanceFieldRenderer *distanceFieldText
+ = static_cast<Q3DSDistanceFieldRenderer *>(
+ m_Renderer.GetQt3DSContext().getDistanceFieldRenderer());
+ theRenderable = RENDER_FRAME_NEW(SDistanceFieldRenderable)(
+ theFlags, inText.GetGlobalPos(), inText, inText.m_Bounds, theMVP,
+ modelView, *distanceFieldText);
+ } else
+#endif
+ {
+ TTPathObjectAndTexture theResult
+ = theTextRenderer->RenderText(inText, inTextScaleFactor);
+ inText.m_TextTexture = theResult.second.second.mPtr;
+ inText.m_TextTextureDetails = theResult.second.first;
+ inText.m_PathFontItem = theResult.first.second;
+ inText.m_PathFontDetails = theResult.first.first;
+ STextScaleAndOffset theScaleAndOffset(*inText.m_TextTexture,
+ inText.m_TextTextureDetails, inText);
+ QT3DSVec2 theTextScale(theScaleAndOffset.m_TextScale);
+ QT3DSVec2 theTextOffset(theScaleAndOffset.m_TextOffset);
+ QT3DSVec3 minimum(theTextOffset[0] - theTextScale[0],
+ theTextOffset[1] - theTextScale[1], 0);
+ QT3DSVec3 maximum(theTextOffset[0] + theTextScale[0],
+ theTextOffset[1] + theTextScale[1], 0);
+ inText.m_Bounds = NVBounds3(minimum, maximum);
+
+ if (inText.m_PathFontDetails)
+ ioFlags.SetRequiresStencilBuffer(true);
- STextRenderable *theRenderable = RENDER_FRAME_NEW(STextRenderable)(
- theFlags, inText.GetGlobalPos(), m_Renderer, inText, inText.m_Bounds, theMVP,
- inViewProjection, *inText.m_TextTexture, theTextOffset, theTextScale);
+ theRenderable = RENDER_FRAME_NEW(STextRenderable)(
+ theFlags, inText.GetGlobalPos(), m_Renderer, inText, inText.m_Bounds, theMVP,
+ inViewProjection, *inText.m_TextTexture, theTextOffset, theTextScale);
+ }
m_TransparentObjects.push_back(theRenderable);
}
return retval;
@@ -997,11 +1015,13 @@ namespace render {
}
bool SLayerRenderPreparationData::PrepareRenderablesForRender(
- const QT3DSMat44 &inViewProjection, const Option<SClippingFrustum> &inClipFrustum,
- QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags)
+ const QT3DSMat44 &inProjection, const QT3DSMat44 &inViewProjection,
+ const Option<SClippingFrustum> &inClipFrustum,
+ QT3DSF32 inTextScaleFactor, SLayerRenderPreparationResultFlags &ioFlags)
{
SStackPerfTimer __timer(m_Renderer.GetQt3DSContext().GetPerfTimer(),
"SLayerRenderData::PrepareRenderablesForRender");
+ m_projection = inProjection;
m_ViewProjection = inViewProjection;
QT3DSF32 theTextScaleFactor = inTextScaleFactor;
bool wasDataDirty = false;
@@ -1025,7 +1045,8 @@ namespace render {
SText *theText = static_cast<SText *>(theNode);
theText->CalculateGlobalVariables();
if (theText->m_Flags.IsGloballyActive()) {
- bool wasTextDirty = PrepareTextForRender(*theText, inViewProjection,
+ bool wasTextDirty = PrepareTextForRender(*theText, inProjection,
+ inViewProjection,
theTextScaleFactor, ioFlags);
wasDataDirty = wasDataDirty || wasTextDirty;
}
@@ -1344,6 +1365,7 @@ namespace render {
QT3DSF32 theTextScaleFactor = 1.0f;
if (m_Camera) {
+ m_projection = m_Camera->m_Projection;
m_Camera->CalculateViewProjectionMatrix(m_ViewProjection);
theTextScaleFactor = m_Camera->GetTextScaleFactor(
thePrepResult.GetLayerToPresentationViewport(),
@@ -1359,8 +1381,10 @@ namespace render {
// the near plane's bbox edges are calculated in the clipping frustum's
// constructor.
m_ClippingFrustum = SClippingFrustum(m_ViewProjection, nearPlane);
- } else
+ } else {
+ m_projection = QT3DSMat44::createIdentity();
m_ViewProjection = QT3DSMat44::createIdentity();
+ }
// Setup the light directions here.
@@ -1372,7 +1396,8 @@ namespace render {
m_ModelContexts.clear();
if (GetOffscreenRenderer() == false) {
bool renderablesDirty =
- PrepareRenderablesForRender(m_ViewProjection, m_ClippingFrustum,
+ PrepareRenderablesForRender(m_projection, m_ViewProjection,
+ m_ClippingFrustum,
theTextScaleFactor, thePrepResult.m_Flags);
wasDataDirty = wasDataDirty || renderablesDirty;
if (thePrepResult.m_Flags.RequiresStencilBuffer())
diff --git a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h
index 5b8d6e10..05923e7f 100644
--- a/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h
+++ b/src/Runtime/Source/runtimerender/rendererimpl/Qt3DSRendererImplLayerRenderPreparationData.h
@@ -270,6 +270,7 @@ namespace render {
TRenderableObjectList m_RenderedOpaqueObjects;
TRenderableObjectList m_RenderedTransparentObjects;
QT3DSMat44 m_ViewProjection;
+ QT3DSMat44 m_projection;
SClippingFrustum m_ClippingFrustum;
Option<SLayerRenderPreparationResult> m_LayerPrepResult;
// Widgets drawn at particular times during the rendering process
@@ -324,14 +325,16 @@ namespace render {
const Option<SClippingFrustum> &inClipFrustum,
TNodeLightEntryList &inScopedLights);
- bool PrepareTextForRender(SText &inText, const QT3DSMat44 &inViewProjection,
+ bool PrepareTextForRender(SText &inText, const QT3DSMat44 &inProjection,
+ const QT3DSMat44 &inViewProjection,
QT3DSF32 inTextScaleFactor,
SLayerRenderPreparationResultFlags &ioFlags);
bool PreparePathForRender(SPath &inPath, const QT3DSMat44 &inViewProjection,
const Option<SClippingFrustum> &inClipFrustum,
SLayerRenderPreparationResultFlags &ioFlags);
// Helper function used during PRepareForRender and PrepareAndRender
- bool PrepareRenderablesForRender(const QT3DSMat44 &inViewProjection,
+ bool PrepareRenderablesForRender(const QT3DSMat44 &inProjection,
+ const QT3DSMat44 &inViewProjection,
const Option<SClippingFrustum> &inClipFrustum,
QT3DSF32 inTextScaleFactor,
SLayerRenderPreparationResultFlags &ioFlags);